SAS 程序冷知识——关于%quote系函数中%的转义问题
上一篇是关于%str和%nrstr两个函数的SAS 程序冷知识——一个关于%str和%nrstr的案例解读 - 简书
此处是上次的一个扩展。同样具有mask效果的quote一族是怎样处理%的呢?对于quote一族来说,%当然也具有两个功能,一个是宏的触发符号,一个是mask关键词的功能。
首先是bquote,这个函数的作用是mask所有特殊符号,所以在这函数里%不具备任何特殊符号的作用,所以不必用两个%mask。
有趣的是quote和nrquote,我们先看代码:
%let s1=%%@2个百分号;
%let s2=%%%%@4个百分号;
%let s3=%%%%%%%%@8个百分号;
%let s4=%%%%%%%%%%%%%%%%@16个百分号;
%macro a(quote=,nrquote=);
%put &=quote.;
%put &=nrquote.;
%mend;
%a(quote=%quote(&s1.),nrquote=%nrquote(&s1.));
%a(quote=%quote(&s2.),nrquote=%nrquote(&s2.));
%a(quote=%quote(&s3.),nrquote=%nrquote(&s3.));
%a(quote=%quote(&s4.),nrquote=%nrquote(&s4.));
结果是:
QUOTE=%@2个百分号
NRQUOTE=%@2个百分号
QUOTE=%@4个百分号
NRQUOTE=%%@4个百分号
QUOTE=%%@8个百分号
NRQUOTE=%%%%@8个百分号
QUOTE=%%%%@16个百分号
NRQUOTE=%%%%%%%%@16个百分号
我们发现对于nrquote来说,经过该函数%的个数都减半了。这个可以理解为:对于每对%而言,前一个%作为mask的符号,转义了后一个%的特殊作用,然后前一个%完成使命消失,从而最后的个数是原来的一半。
但是quote就有意思了,在4个百分号的时候,结果依然是一个%,并且按照后面的规则看,似乎quote在执行了第一遍转义之后,又进行了一次转义。
然后如果宏是这样的:
%macro b(quote=,nrquote=);
%put quote=%quote("e.);
%put nrquote=%nrquote(&nrquote.);
%mend;
%b(quote=&s1.,nrquote=&s1.);
%b(quote=&s2.,nrquote=&s2.);
%b(quote=&s3.,nrquote=&s3.);
%b(quote=&s4.,nrquote=&s4.);
结果就完全不一样,变得正常了。
quote=% 2个百分号
nrquote=% 2个百分号
quote=%% 4个百分号
nrquote=%% 4个百分号
quote=%%%% 8个百分号
nrquote=%%%% 8个百分号
quote=%%%%%%%% 16个百分号
nrquote=%%%%%%%% 16个百分号
这个结果表明,如果在宏变量赋值的时候,就带着函数quote,那么当使用的时候%会被作为转义符执行两次。为了验证这个结论我们再做一次试验。
%let s1=%quote(%%@2个百分号);
%let s2=%quote(%%%%@4个百分号);
%let s3=%quote(%%%%%%%%@8个百分号);
%let s4=%quote(%%%%%%%%%%%%%%%%@16个百分号);
%put quote=&s1.;
%put quote=&s2.;
%put quote=&s3.;
%put quote=&s4.;
其结果为:
quote=%@2个百分号
quote=%@4个百分号
quote=%%@8个百分号
quote=%%%%@16个百分号
这个结果基本验证了我们的想法。那么为什么quote就会执行两次呢?猜想原因是宏变量赋值的时候,是先执行宏函数,后赋值。我们以%let s3=%quote(%%%%%%%%@8个百分号);为例。当赋值的时候,s3的值可以是%%%%@8个百分号。而当我们使用语句:%put quote=&s3.;的时候,由于解析宏变量中带有%quote的功能,所以在解析后再次执行转义功能,从而产生的结果”%%@8个百分号”。详见:SAS 程序冷知识——一个关于%str和%nrstr的案例解读(续) - 简书
那么为什么nrquote就不会有问题呢?我们通过试验看到了quote的运行机制。quote和nrquote实际上本质是在字符的两端加上一个标志符号,这个符号往往我们无法直接通过put看到,但是如果用16进制解析就能清楚看到,我们显示示例如下:
%let s1=%%%%%%%%@8个百分号;
%let s2=%quote(%%%%%%%%@8个百分号);
%let s3=%nrquote(%%%%%%%%@8个百分号);
data _null_;
set sashelp.vmacro;
where name='S1';
S1=strip(value);
put S1= hex50.;
run;
data _null_;
set sashelp.vmacro;
where name='S2';
S2=strip(value);
put S2= hex50.;
run;
data _null_;
set sashelp.vmacro;
where name='S3';
S3=strip(value);
put S3= hex50.;
run;
执行之后可以看到
S1中25有8个,实际上是符号‘%’,到了S2中25变成了4个,这说明quote已经转义掉了其中的4个。但是S2的前后各多了一个符号,分别是03和08(20表示空格,是长度不够补位用的)。其他的位置,S1和S2完全一样,对应的就是文字“@8个百分号”。
到了S3就更有趣了,和S2内容一样,但区别在于开头的符号从03变成了05,然后百分号从25变成了10。
这样我们就大致理解quote和nrquote的操作了,当使用这个函数的时候,首先函数会在字符两端加上两个符号,结尾都是08,quote的开头是03,nrquote的开头是05。一旦套上的是05,SAS会进一步将原来的%和&转换成其他符号,来达到mask宏触发符号的目的。因此我们猜测执行顺序是这样的:
1、解析quote或nrquote中的宏变量。
2、两端加上mask符号08,03或05。
3、有%的话进行转义,将+,-等符号进行替换。
4、如果是nrquote,将%和&进行符号替换。
5、如果是quote,检查是否有%可以执行。
而之所以nrquote不会执行2次,是因为在第一次执行转义之后,所有的%就都被替换成其他字符了,因此也就不会又后面的转义了。