Simple email validation SQL RPG ILE program

IBM i

Jun 13

There are lots of code snippets, example programs and long waffling discussions in internet land filled with pointy headed AS400, iSeries 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.

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:

regex sqlrpgle simple email validation - Simple email validation SQL RPG ILE program

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 a 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):

// --------------------------------------------------------------------
RPG Code Snippet// Example SQL RPG program : VLD8EMAIL
// Author : Nick Litten
// ? CRTSQLRPGI ??OBJ(LITTENN/VLD8EMAIL)
//   SRCFILE(LITTENN/QSQLSRC)
//   SRCMBR(VLD8EMAIL)
//   OBJTYPE(*PGM)
// History:
// 2018-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) option(*nodebugio:*srcstmt) ;

dcl-pi *n; // this is our program input a 255 long variable email address
  email varchar(255);
end-pi;

dcl-pr email_is_valid IND; // this is our subprocedure - put this in a *SRVPGM to share it
  *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) +
  ' is very naughty indeed. Bad Email! ');
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(100)
      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 dynamicalluy display 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?

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:

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:

testing rpgle to call sqlrpgle using variable length parms 800x489 - Simple email validation SQL RPG ILE program

as400 regex to validate bad email 800x489 - Simple email validation SQL RPG ILE program

PS: to test your regular expressions check out http://emailregex.com/regex-visual-tester/ it’s excellent!