Let’s look at a quick little example RPG email validation program – SQLRPGLE ILE
There are lots of code snippets, example programs, and long waffling discussions in internet land filled with pointy-headed AS400, Series, and IBM i programmer chaps (and chappettes) arguing about the best way to “validate an email address in RPG”.
I know because I’ve read nearly all of them, as well as tried all the techniques.
But yesterday I stumbled across a really simple variation of an established technique that seems to work 99% of the time (note: there is no perfect validation expressions since email address rules are changing frequently)
Using Regex to validate an email
Look at this SQLRPGLE code snippet:
exec sql set :myCount = regexp_count( :myEmail, '^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$'); If myCount = 1; // email is good else; // email is malformed endif;
Pretty simple huh!
So , what are we doing with this regular expression?
The regular expression ‘^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$‘ is saying – check this input string meets the standard of something before an @ sign followed by something dot something:
The internet is filled with alternate regular expressions to make the email validation logic as complex as you wish.
For my purposes this simple logic is exactly what I need.
So, let’s plumb this into an example RPG program, wrapped in a tidy sub-procedure, with a sprinkling of comments, my favorite QUILNGTX *API for display screen messages without coding any DSPF and using variable-length characters – just for fun (Yes, I know, I need to get out more):
**free // -------------------------------------------------------------- // Example SQL RPG program : VLD8EMAIL // Author : Nick Litten // ? CRTSQLRPGI ??OBJ(LITTENN/VLD8EMAIL) //  SRCFILE(LITTENN/QRPGLESRC) //  SRCMBR(VLD8EMAIL) //  OBJTYPE(*PGM) // History: // 2012-06-12 NJL Created // -------------------------------------------------------------- // This program accepts an input parameter of 'email address'. // We validate it using a regular expression in SQL and // display a message in a window telling you if the email // address is good or bad. nice or naughty. // Simple but Saucy. // -------------------------------------------------------------- ctl-opt dftactgrp(*no) ccsid(*CHAR:37) actgrp('VALIDATE') option(*nodebugio:*srcstmt); // this is our program input a 255 character variable containg the email address dcl-pi *n;  email char(255); end-pi; // this is our subprocedure - put this in a *SRVPGM to share it dcl-pr email_is_valid IND;  *n like(email); end-pr; if email_is_valid(email);  display_window ('Email Address ' + %trim(email) + ' is valid'); else;  display_window ('Yuk! Email Address ' + %trim(email) + ' failed email address validation!'); endif; *inlr = *on; return; // -------------------------------------------------------------- // PROCEDURE: email_is_valid // -------------------------------------------------------------- // Accept an input field (the email address) and validate it using // a regular expression to perform basic email address syntax checks. // // REGEXP_COUNT Returns a returns a count of the number of times // that a regular expression pattern is matched in a string // // The leading ^ and trailing $ match the beginning and the ending of // the input string, respectively. That is, the entire input string // shall match with this regexe, instead of a part of the input string. // // \w+ matches 1 or more word characters (a-z, A-Z, 0-9 and underscore). // // [.-] matches character . or -. We need to use . to represent . as . has // special meaning in regexe. The \ is known as the escape code, which // restore the original literal meaning of the following character. // // [.-]? matches 0 or 1 occurrence of [.-]. // // ([.-]?\w+)* matches 0 or more occurrences of [.-]?\w+. // // The sub-expression \w+([.-]?\w+)* is used to match the username in // the email, before the @ sign. It begins with at least one word // character (a-z, A-Z, 0-9 and underscore), followed by more word // characters or . or -. However, a . or - must follow by a word // character (a-z, A-Z, 0-9 and underscore). That is, the string cannot // contain "..", "--", ".-" or "-.". Example of valid string are "a.1-2-3". // // The @ matches itself. // // The sub-expression .\w{2,3} matches a . followed by two or three word // characters, e.g., ".com", ".edu", ".us", ".uk", ".co". // // (.\w{2,3})+ specifies that the above sub-expression shall occur one or // more times, e.g., ".com", ".co.uk", ".edu.sg" etc. // -------------------------------------------------------------- dcl-proc email_is_valid; dcl-pi *n IND;  myEmail like(email); end-pi; dcl-s myCount uns(10); // you can change this Regex string to anything you like!!! dcl-s myEmailValidationRegex varchar(1024)    inz('^(?:\w+\.?)*\w+@(?:\w+\.)*\w+$'); exec sql  set :myCount = regexp_count(:myEmail,:myEmailValidationRegex); If myCount = 1;  return *on; else;  return *off; endif; end-proc; // -------------------------------------------------------------- // PROCEDURE: display_window // -------------------------------------------------------------- // Use IBM i *API 'QUILNGTX' to dynamically show messages in a // window format rather than on the error message line // -------------------------------------------------------------- dcl-proc display_window export; dcl-pi display_window;  p_Text varchar(8192) const;  p_MsgId char(7) Options(*nopass: *omit);  p_MsgFile char(21) Options(*nopass: *omit); end-pi; dcl-ds myApiError inz qualified;  Bytes int(10) inz(%size(myApiError));  BytesAvailable int(10) inz;  ErrorID char(7) inz;  Reserved char(1) inz(x'00');  MessageData char(128) inz; end-ds; dcl-pr QUILNGTX extpgm('QUILNGTX');  *n char(8192) const;  *n int(10) const;  *n char(7) const;  *n char(21) const;  *n options(*omit: *varsize) like(myapierror); end-pr; dcl-s myMsgId like(p_MsgId); dcl-s myMsgFile like(p_MsgFile); If %Parms = 1;  myMsgId = 'CPF9999';  myMsgFile = 'QCPFMSG'; Elseif %Parms = 2;  myMsgId = p_MsgId;  myMsgFile = 'QCPFMSG'; Elseif %Parms = 3;  myMsgId = p_MsgId;  myMsgFile = p_MsgFile; Endif ; QUILNGTX ( p_Text : %Len(p_Text) : myMsgId : myMsgFile : myApiError ); end-proc;
I hope that helps someone out?
Let’s test this SQLRPGLE code
Testing is easy – I just knocked together a little calling program, because calling variable-length input parameters from an IBM i command line is painful. Convert the previous code into a *SRVPGM (or copy/paste the code into the mainline) to add it to this example and:
**free // Example SQL RPG program : TESTEMAIL // Author : Nick Litten // ? CRTSQLRPGI ??OBJ(LITTENN/TESTEMAIL) // SRCFILE(LITTENN/QRPGLESRC) // SRCMBR(TESTEMAIL) // OBJTYPE(*PGM) // History: // 2014-06-12 NJL Created // -------------------------------------------------------------- // call validation program with various VARCHAR email addresses // -------------------------------------------------------------- ctl-opt dftactgrp(*no) actgrp('VALIDATE') option(*nodebugio:*srcstmt); dcl-pr tst_email extpgm('VLD8EMAIL'); *n varchar(255); end-pr; dcl-s email varchar(255); email = 'nick@nicklitten.com'; tst_email(email); email = 'nick@this-is_a@very$duff email'; tst_email(email); *inlr = *on;
and when I call my TEST program I see these two sceeens:
PS: to test your regular expressions check out http://emailregex.com/regex-visual-tester/ it’s excellent!
Hope that helps some out?
Update Oct 2020
A new regex is shown over at email regex – I would love to know if this helps anyone?
General Email Regex (RFC 5322 Official Standard)
1 | (?:[a-z0-9!#$%&’*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&’*+/=?^_`{|}~-]+)*|”(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*”)@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]) |
Nick, once again you’ve helped me out a treat, 2nd of the day 🙂
Zippy
Nice work documenting and commenting, as well as including a usable subprocedure and test. Do you know whether IBM’s SNDSMTPEMM supports recipient email addresses with the “.bz” domain? The command throws a TCP530F Email Address Invalid whenever I try to use it with “.bz” domains.
Be aware that something like “nick-litten@test-company.at” will fail this validation.
Does the general email regex work? https://emailregex.com/
(?:[a-z0-9!#$%&’*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&’*+/=?^_`{|}~-]+)*|”(?:[x01-x08x0bx0cx0e-x1fx21x23-x5bx5d-x7f]|\[x01-x09x0bx0cx0e-x7f])*”)@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[x01-x08x0bx0cx0e-x1fx21-x5ax53-x7f]|\[x01-x09x0bx0cx0e-x7f])+)])
I’m trying to implement the Email Validation on a Power 9, V7R3, up to date PTF’s. I copied the source programs and compiled without any changes. Every email address I try to validate returns as invalid, showing the email address with a preceding ‘?’: ?nick@nicklitten.com Any suggestions on how to remove the ‘?’. I believe that’s the cause of all the failures. Thanks.
that happened to me when I was calling the program from the command line. Once I called the program from another program it worked just fine.
Javier just email me saying the problem was because it was users with different CCSID on their profiles. I just updated the code sample to specify a CCSID of 37. Let’s see if that fixes this quirkiness?
ps: Thanks Javier!
Seems always invalid.
Hi… I was able to make the program to work and test the email address. But for some weird reason when I run my program to test the email it works fine returning either 1 or 0 but for other users the count is always sent back as 0 even when the email is correct. I just do not get what is the reason.
Can you paste your code? I will copy it and see if it does the same on my machine…