I created a data dictionary with a column for field name and another column with the expected pattern (PRX) for each field. For example, the name fields allow letters, spaces, hyphens, commas, and apostrophes; so the pattern listed for those fields is /[A-Z\-\,\' ]/
I import the data dictionary then create macro variables to hold a listing of field names separated by space, and patterns separated by ~. However, when the code iterates through the patterns it chokes on the single quote. I've tried to put double quotes around the patterns in the data dictionary, and I tried putting ""&doubleQuotes"" around the macro variable, and tried %bquote, but I can't seem to find a combination that works. The SAS code and error message can be found below. The error message below corresponds to pattern /[A-Z\-\,\' ]/
ERROR: Closing delimiter "/" not found after regular expression "/[A-Z\-\,\".
ERROR: The regular expression passed to the function PRXMATCH contains a syntax error.
NOTE: Argument 1 to function PRXMATCH('/[A-Z\-\,\','L''ittle '[12 of 60 characters shown]) at
line 13 column 42 is invalid.
Any advice is appreciated.
Thanks,
Ryan
proc sql noprint;
select VARNAME, PATTERN
into :varlist separated by ' ',
:pattern separated by '~'
from Ccbf_dd
where PATTERN ne '';
quit;
%let cntlist = &sqlobs;
%put &varlist;
%macro test(); %put %bquote(&pattern); %mend test; %test;
%macro Val_check();
data QC_CCBF_values;
set CCBF;
length Issue_desc $250;
Issue_desc=''; /* Initialize Issue_desc */
%do i = 1 %to &cntlist; /* iterate through the list of fields */
%let varname= %scan(%bquote(&varlist.), &i., %str(' ')); /* scans through the list of fields and assigns them one by one to the &varname macro var */
%let patternM= %scan(%bquote(&pattern.), &i., %str('~')); /* scans through the list of patterns and assigns them one by one to the &patternM macro var */
if &varname ne '' and prxmatch("&patternM",&varname.) eq 0 then Issue_desc=catx('; ',Issue_desc,"&varname");
%end;
run;
%mend Val_check;
%Val_check();
Try using %qscan instead of %scan.
This works. Changes highlighted
%let varname= %scan(%bquote(&varlist.), &i., %str( )); %let patternM= %qscan(%bquote(&pattern), &i., ~);
Besides of the challenge with the single quote I believe that your current logic won't return the result you're after. The RegEx as per your code will only return something as invalid (value of zero) if ALL characters in the string are invalid.
Inded @Patrick is right.
If you want to "allow letters, spaces, hyphens, commas, and apostrophes" you probably want
BADFLAG=prxmatch("/[^A-Z ,'-]/i", VAR);
You are absolutely right. I didn't include the ^. I so focusing on the single quote issue. Thank you!
Rather than messing round with tons of Klingon syntax (macro quoting), why not just single-quote the patterns from the start:
proc sql noprint;
select VARNAME, quote(trim(PATTERN),"'")
into :varlist separated by ' ',
:pattern separated by '~'
from Ccbf_dd
where PATTERN ne '';
quit;
You can then change your PRXMATCH call to PRXMATCH(&PatternM,&Varname), and drop all the macro quoting. And you will not have to worry about patterns containing your string delimiter ("~", in this case), just use %sysfunc(Scan(&pattern,&i,~,Q)), instead of %SCAN or %QSCAN, then delimiters in patterns will not matter.
This is for me one of these use cases where generating and including code is much easier to implement and debug than using macro code. People are of course different - but that's how my brain works.
Below some code which illustrates the approach.
data parameterTable;
varname='varA'; pattern="/[^A-Z ,'-]/oi"; output;
varname='VarB'; pattern="/[^xyz]/oi"; ;output;
stop;
run;
filename codegen temp;
data _null_;
/* file print;*/
file codegen;
set parameterTable;
put @2 'if ' varname 'ne " " and prxmatch("' pattern +(-1)'",strip(' varname +(-1)')) > 0';
put @4 'then Issue_desc=catx("; ",Issue_desc,"' varname +(-1)'");';
run;
data CCBF;
varA='abc'; varB='xyz';output;
varA='a1b'; varB='xyz';output;
varA='abc'; varB='xbz';output;
varA='a1b'; varB='xbz';output;
stop;
run;
data QC_CCBF_values;
set CCBF;
length Issue_desc $250;
Issue_desc='';
%include codegen / source2;
run;
Besides of easily writing the generated code to the output window (file print) during development, I also like that it's easy to read in the SAS log when executed.
52 data QC_CCBF_values; 53 set CCBF; 54 length Issue_desc $250; 55 Issue_desc=''; 56 %include codegen / source2; NOTE: %INCLUDE (level 1) file CODEGEN is file C:\Users\ssapam\AppData\Local\Temp\SEG8912\SAS Temporary Files\_TD9428_SSAPAM_\#LN00019. 57 + if varA ne " " and prxmatch("/[^A-Z ,'-]/oi",strip(varA)) > 0 58 + then Issue_desc=catx("; ",Issue_desc,"varA"); 59 + if VarB ne " " and prxmatch("/[^xyz]/oi",strip(VarB)) > 0 60 + then Issue_desc=catx("; ",Issue_desc,"VarB"); NOTE: %INCLUDE (level 1) ending. 61 run;
This works too. Thanks for the suggestion!
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Select SAS Training centers are offering in-person courses. View upcoming courses for: