写在之前:
1. 写关于macro quoting的总结是需要一点勇气的。
2. 这个所谓的总结远远没有涵盖macro quoting所涉及到的全部问题,这篇文章的目的是让理解变得容易一点点。
3. 文章中用到的例子,有的是借用了其他文章中的想法(比如Ian Whitlock 的A Serious Look at Macro Quoting)。有的例子并不会经常用到,这里只是为了尽量清楚地解释某个问题。
4. 如果各位发现文中有任何不正确的地方,或是有更精确的解释,还望不吝赐教。
Why Quoting?
在SAS语言 中,比如data步中,ABC表示一个变量的名字,而“ABC”就是一个长度为3的字符串。SAS通过引号对字符串和变量名进行区分,这可以说是SAS语言里的quoting。
而在宏语言中,同样会遇到这种情况。比如逗号","既可以用来分隔某个宏调用或宏函数的参数(%SCAN(argument,n<, delimiters>)),也可以作为某个宏变量的值中一个普通的字符(%let value=part1,part2;)。在宏语言里,为了让宏处理器将某个逗号看作一个普通的字符,就需要用某种方法把逗号作为分隔符的功能隐藏起来(%put %scan(%str(part1,part2), 2, %str(,));),否则就会提示“ERROR:Macro function %SCAN has too many arguments. The excess arguments will be ignored”。这种方式就是宏语言中的quoting。
Quoting-Related Functions
- %STR & %NRSTR
除这两个函数之外的Quoting函数都是在宏或宏语句执行阶段起作用。
- %BQUOTE & %NRBQUOTE
宏语言中的另外两个quote函数%QUOTE和%NRQUOTE的功能都包含在%BQUOTE和%NRBQUOTE之中。
- %SUPERQ
- Other functions/macros
如函数 %QSCAN, %SUBSTR, %QSYSFUNC, %QUPCASE, 以及宏%QCOMPRES, %QLEFT, %QLOWCASE, %QTRIM。
- %UNQUOTE
What to Quote
- Macro quote函数都可以用来隐藏下面这些字符的功能:blank = NE ; | LE¬ + # LT ^ -- AND GE ~ * OR GT , (comma) / NOT < IN > EQ ;
- %NRSTR, %NRQUOTE, %NRBQUOTE 和%SUPERQ还能隐藏&和%;
- 对于不成对出现的引号和括号,
%STR、%NRSTR、 %QUOTE 和%NRQUOTE 需要在引号或括号之前加‘%’;
%BQUOTE、%NRBQUOTE不需要前置的‘%’; - 当字符串中有连续的百分号‘%’,并且百分号后面不是字母或下划线时,
%BQUOTE和%NRBQUOTE会直接进行quote;
%STR、%NRSTR、 %QUOTE 和%NRQUOTE会将两个连续的%处理成单个%。例如:
%put %str(% %% %%% ); %put %nrstr(% %% %%% %%%%); %put %quote(% %% %%% %%); %put %nrquote(% %% %%% ); %put %bquote(% %% %%%); %put %nrbquote(% %% %%%%); |
How to Quote
当宏处理器将quote函数作用在某个字符串上的时候,它会对其中的特殊的字符和运算符进行处理(具体来说应该是将其替换为其他的字符),并且也会在这个字符串的前后各加上一个十六进制字符,前边加的字符标志着字符串开始并记录quote函数的类型,后面加的字符标志字符串的结束。还有一点,quote函数作用时是会保留字符串前后的空格的。下面的几种方法可以显示出macro quoting的某些效果:(下面的例子是在SAS 9.1下运行,不同的SAS版本log里的显示可能不一样。)
1. 用%PUT语句在log中显示某个macro symbol table.
%macro test; %local mvar1 mvar2 mvar3 mvar4 mvar5 mvar6 mvar7 mvar8; %let mvar1=a,b,c; %let mvar2=%str( a,b,c ); %let mvar3=%str( &mvar1 ); %let mvar4=%unquote( &mvar2 ); %let mvar5=%quote( a,b,c ); %let mvar6=%quote(&mvar1); %let mvar7=%superq(mvar1); %let mvar8=%superq( mvar1 ); %put _local_; %put mvar1: *&mvar1*; %put mvar2: *&mvar2*; %put mvar3: *&mvar3*; %put mvar4: *&mvar4*; %put mvar5: *&mvar5*; %put mvar6: *&mvar6*; %put mvar7: *&mvar7*; %put mvar8: *&mvar8*; %if &mvar1 eq &mvar2 %then %put A: mvar1 equals to mvar2; %if &mvar1 eq &mvar3 %then %put B: mvar1 equals to mvar3; %if &mvar1 eq &mvar4 %then %put C: mvar1 equals to mvar4; %if &mvar1 eq &mvar5 %then %put D: mvar1 equals to mvar5; %if &mvar1 eq &mvar6 %then %put E: mvar1 equals to mvar6; %if &mvar1 eq &mvar7 %then %put F: mvar1 equals to mvar7; %if &mvar1 eq &mvar8 %then %put G: mvar1 equals to mvar8; %if &mvar1 eq %qleft(&mvar5) %then %put H: mvar1 equals to %nrstr(%qleft%()mvar5%str(%)); %mend; %test LOG: TEST MVAR8 _a_b_c_ TEST MVAR5 _ a_b_c _ TEST MVAR4 a,b,c TEST MVAR7 _a_b_c_ TEST MVAR6 _a_b_c_ TEST MVAR1 a,b,c TEST MVAR3 _ a,b,c _ TEST MVAR2 _ a_b_c _ mvar1: *a,b,c* mvar2: * a,b,c * mvar3: * a,b,c * mvar4: *a,b,c* mvar5: * a,b,c * mvar6: *a,b,c* mvar7: *a,b,c* mvar8: *a,b,c* A: mvar1 equals to mvar2 B: mvar1 equals to mvar3 C: mvar1 equals to mvar4 E: mvar1 equals to mvar6 F: mvar1 equals to mvar7 G: mvar1 equals to mvar8 H: mvar1 equals to %qleft(mvar5) |
- %put _local_;所输出的宏变量的值中,MVAR2和MVAR3是有差别的。其原因在于%STR是在编译阶段起作用。对于MVAR2,%STR作用于字符串” a,b,c ”,在加前置字符和后置字符的同时也会对逗号处理;而对于MVAR3,%STR作用于字符串” &mvar1 ”, 只会加前置和后置的字符,这时不会看到MVAR1的值里的逗号。这里也可以看出,字符串前后的空格都被保留了;
- %put _local_;所输出的MVAR5和MVAR6也有差别。这个差别在于MVAR5的%LET语句中字符串’ a,b,c ’前后各有一个空格,而MVAR6没有。需要注意的是,这里并不存在像MVAR2和MVAR3之间的差别。这是因为%QUOTE是在宏执行的时候才“工作”,这时会对它参数里宏变量的引用先进行解析,所以%QUOTE看到的都是解析后的值;
- %put _local_;所输出的MVAR7和MVAR8是没有差别的,这是因为%SUPERQ的参数只是宏变量的名字,所以在宏变量名字前后所加的空格,并不涉及到%SUPERQ的真正作用对象---“宏变量的值”;
- 通过%put mvar… 语句的输出结果,也可以看出,quote函数会保留字符串前后的空格;
- 最后对宏变量的比较中,除MVAR5外,其余的7个宏变量比较都是相等的。MVAR5的值和其他的不同,应该是由MVAR5的值中前面的空格引起。如果用%QLEFT把前面空格去掉,就能和MVAR1相等了。
2. 用MLOGIC显示宏参数的值
options mlogic; %macro quotedval(val); %put &val; %mend; %quotedval(a;b;c) %quotedval(%str(a;b;c)) LOG: 31 %quotedval(a;b;c) MLOGIC(QUOTEDVAL): Beginning execution. MLOGIC(QUOTEDVAL): Parameter VAL has value a;b;c MLOGIC(QUOTEDVAL): %PUT &val a;b;c MLOGIC(QUOTEDVAL): Ending execution. 32 %quotedval(%str(a;b;c)) MLOGIC(QUOTEDVAL): Beginning execution. MLOGIC(QUOTEDVAL): Parameter VAL has value _a_b_c_ MLOGIC(QUOTEDVAL): %PUT &val a;b;c MLOGIC(QUOTEDVAL): Ending execution |
3. 用SYMBOLGEN选项也可以在log中显示某个宏变量的值被quote了
options symbolgen; %let x=%quote(abcde); %put &x; LOG: SYMBOLGEN: Macro variable X resolves to abcde SYMBOLGEN: Some characters in the above value which were subject to macro quoting have been unquoted for printing. abcde |