楼主: pobel
19225 10

[学习分享] 关于Macro Quoting   [推广有奖]

院士

15%

还不是VIP/贵宾

-

威望
2
论坛币
14674 个
通用积分
1867.0237
学术水平
932 点
热心指数
930 点
信用等级
730 点
经验
113854 点
帖子
1287
精华
4
在线时间
3650 小时
注册时间
2008-12-10
最后登录
2024-9-27

初级热心勋章 中级热心勋章 初级信用勋章 初级学术勋章 中级信用勋章 中级学术勋章 高级热心勋章 高级学术勋章

楼主
pobel 在职认证  发表于 2014-7-1 08:21:21 |只看作者 |坛友微信交流群|倒序 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
楼 1 of 2

写在之前:
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
众所周之,这两个宏函数在编译阶段起作用。意思就是说,%STR的工作都是在宏处理器对宏进行编译过程中完成,而到了执行的时候,已经“看不到”%STR/%NRSTR了,只能看到他们的“工作成果”。这两个函数都是对参数中的特殊字符或运算符进行quote。如果参数中包含宏变量或宏的调用,那么在%STR/%NRSTR工作时是不执行这些调用的。

除这两个函数之外的Quoting函数都是在宏或宏语句执行阶段起作用。

  • %BQUOTE & %NRBQUOTE
这两个函的作用对象是“宏处理器对参数的解析结果”。也就是,如果参数中包含对宏变量或宏的调用,那宏处理器首先会执行这些调用,再将%BQUOTE/%NRBQUOTE作用于最后的结果。

宏语言中的另外两个quote函数%QUOTE和%NRQUOTE的功能都包含在%BQUOTE和%NRBQUOTE之中。

  • %SUPERQ
这个函数的参数是宏变量的名字,作用对象是该宏变量的值。如果宏变量的值里包含有对宏变量或宏的调用,宏处理器也不会去尝试执行这些调用。

  • Other functions/macros
宏语言中有一些以”Q”开头的函数或宏也可以实现对其运算结果的quote。
如函数 %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(% %% %%%%);
注:例子中的前四个%PUT语句中,quote函数的右括号之前连续的%如果是奇数个,那么在右括号之前需要一个空格,否则紧跟的右括号也会被做为字符串的一部分,这样就需要再加一个’)’来作为函数的结尾:%put %str(% %% %%%));

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)
  
对于LOG信息的解释:
  • %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
  



二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Macro CRO acr ING Mac 舞蹈

已有 6 人评分经验 学术水平 热心指数 信用等级 收起 理由
eijuhz + 20 精彩帖子
dxystata + 100 + 2 + 1 精彩帖子
木叶知秋 + 1 + 1 + 1 精彩帖子
Eternal0601 + 5 + 5 + 5 精彩帖子
hopewell + 5 + 5 + 5 精彩帖子
lovedieer + 1 + 1 + 1 精彩帖子

总评分: 经验 + 120  学术水平 + 14  热心指数 + 13  信用等级 + 12   查看全部评分

本帖被以下文库推荐

和谐拯救危机
沙发
pobel 在职认证  发表于 2014-7-1 08:21:22 |只看作者 |坛友微信交流群
楼 2 of 2

How Long does the Quoting last

以下的情况会解除对特殊字符的quote:
1. 用%UNQUOTE函数;
2. 当使用%SCAN,%SUBSTR,%UPCASE函数来操作quote之后的值时,其返回的值都会失去quote效果;
3. 当quote后的值作为SAS语言代码的一部分时,一般情况下这些代码会自动失去quote效果。只是有个别的情况下,看似没有问题的代码,SAS也不能正常运行,这时就需要用%UNQUOTE函数来恢复特殊字符的功能。(Example 4)

Some Examples

Example 1: %NRSTR and %NRQUOTE
%NRSTR和%NRQUOTE的区别在于:前者在编译阶段起作用,后者在执行阶段起作用。当参数中引用了宏变量时,二者的处理方式不同。%NRSTR不会试图解析宏变量的值,而%NRQUOTE虽然也包含了”NR”,但是宏处理器首先会尽可能地找出宏变量的值,再对结果进行quote。例如下面的例子。
  %let a=b;
  %let b=q;
  %put %nrbquote(&&&a);
  %put %nrstr(&&&a);
  
  %symdel G/nowarn;
  * two  warnings: one from %LET and one from %PUT;
  
%let company1=P&G;
  %put company1: &company1;
  
  * one  warning: from %LET;
  
%let company2=%nrbquote(P&G);
  %put company2: &company2;

  * no warning;
  
%let company3=%nrstr(P&G);
  %put company3: &company3;  

Example 2: %SUPERQ
  %symdel a b c d e/nowarn;
  %let a=&b;
  %let b=&c;
  %let c=d;
  %let d=e;

  %put 1: %str(&a);
  %put 2: %nrstr(&a);
  %put 3: %nrquote(&a);
  %put 4: %superq(a);
  %put 5: %superq(&a);

  %symdel abc/nowarn;
  %superq(abc);  
解释:
  • %put 1:%str不能quote ’&’,因此,运行时宏处理器会一直解析到d;
  • %put 2:%nrstr直接作用在字符串’&a’,并隐藏’&’的功能,因此输出&a;
  • %put 3:首先宏处理器会将’&a’一直解析到d,然后用%nrquote来quote解析的结果d;
  • %put 4:%superq直接quote宏变量a的值,并不做任何解析,因此输出&b;
  • %put 5:首先宏处理器将’&a’一直解析到d,并将d作为%superq的参数,接下来%superq会quote宏变量d的值e。
  • %superq一般情况下不会产生宏变量不能解析的warning,除非作为参数的宏变量本来就不存在。如上面例子中的%superq(abc);

Example 3: %UNQUOTE-1
  %macro mvar;
     a
  %mend;

  %macro putmvar;
      %let %mvar=aaaa;
       %put 1. &a;
       %put 2. &%mvar;
       %put 3. %unquote(&%mvar);
       %put 4. %unquote(%nrstr(&)%mvar);
       %put 5. %unquote(%nrstr(&%mvar));
       %put 6. %unquote(%nrstr(&))%mvar;
       %put 7. %unquote(%nrstr(&))a;
  %mend;
  %putmvar

  lOG
  1. aaaa
  2. &a
  3. &a
  4. aaaa
  5. &a
  6. &a
  7. aaaa
  
这个例子中的宏MVAR会返回a。宏PUTMVAR的%LET语句会将宏变量a赋值为aaaa。
  • %put 1是很简单,直观地将a的值aaaa输出;
  • %put 2输出的是&a,这涉及到宏的编译问题。宏处理器在对宏PUTMVAR进行编译时,遇到&符号后,发现后面跟的并不是作为宏变量需要的字母或下划线。因此,编译后宏处理器不会将这个&符号作为对宏变量的引用,进而在执行阶段也就不会再“等待”后面的宏变量;
  • %put 3的结果和%put 2一样,这里的%UNQUOTE需要在执行时才起作用,而在编译阶段已经认为&符号没有引用宏变量,而且这里并没有quote存在;
  • %put 4中,%NRSTR会在编译阶段隐藏&的功能。到了执行阶段,%UNQUOTE又恢复了&的功能,并和%mvar的结果a连在一起共同作为%UNQUOTE的参数。这样宏处理器就能看到&后面的宏变量名a,进而解析得到aaaa;
  • %put 5没能解析出a的值,因为经过%NRSTR和%UNQUOTE的处理之后,相当于还是把&%mvar交给了%PUT语句,和%put 2一样;
  • %put 6没有解析出a的值,是因为%UNQUOTE恢复&的功能后,后面看到的仍然是%,而不是宏变量的名字;
  • %put 7中%unquote使&符号恢复功能,而且后面连的是a,因此成功输出了a的值aaaa。

Example 4: %UNQUOTE-2
一般来说,当宏处理器产生代码后,回到SAS继续执行时,quote的效果会自动解除。但也有例外的情况,比如:
  data test;
     do num=.,1,2;
        output;
       end;
  run;

  %let mvar=%str(case num
    when . then  "Missing"
    when 1 then "One"
    when 2 then "Two"
    else " "
    end as char);

  proc sql;
     create table test1 as
  
    select distinct num,&mvar
          from test;
  quit;
  
这里提交SQL步后会提示错误信息,但是log中所说的“出错”的代码看上去却没有任何问题。问题可能存在于%str对字符串处理后,影响了SAS对代码的解读。如果改成select distinct num,%unquote(&mvar)就没有问题了。另外,通过测试,将MVAR的赋值改成以下任何一个,SQL步也会正常运行。
  %let mvar=%str(case num
       when %str(.) then   "Missing"
       when 1 then "One"
       when 2 then "Two"
       else " "
       end as char);
  %let mvar=%str(case num
       when 1 then "One"
       when 2 then "Two"
       else "Missing "
       end as char);
  %let mvar=%quote(case num
       when . then  "Missing"
       when 1 then "One"
       when 2 then "Two"
       else " "
       end as char);


已有 10 人评分经验 论坛币 学术水平 热心指数 信用等级 收起 理由
eijuhz + 20 + 1 精彩帖子
chinaeu + 3 + 1 + 2 + 2 精彩帖子
清辉了如雪 + 1 + 1 + 1 精彩帖子
phiusss + 1 + 1 + 1 精彩帖子
lovedieer + 1 + 1 + 1 精彩帖子
mingfeng07 + 1 + 1 + 1 精彩帖子
Tigflanker + 3 + 3 + 3 占评
playmore + 5 + 5 + 5 精彩帖子
FB_FLORA + 1 + 1 + 1 精彩帖子
hello_fj + 1 精彩帖子

总评分: 经验 + 20  论坛币 + 4  学术水平 + 16  热心指数 + 16  信用等级 + 15   查看全部评分

和谐拯救危机

使用道具

藤椅
playmore 发表于 2014-7-1 08:49:57 |只看作者 |坛友微信交流群
pobel 发表于 2014-7-1 08:21
楼 2 of 2

How Long does the Quoting last
沙发,p大总结的很好

另外我有一个问题,我想把一个字符串宏变量(包括多个单词,用空格分隔)中的每个单词用引号包围,并再用逗号分隔,如下所示:

%let test=Var1 Var2;
%let test=%SYSFUNC(TRANWRD(&test,%STR( ),%STR(',')));
%put &test;

会报错:
NOTE 49-169: The meaning of an identifier after a quoted string might change in a future SAS release.  Inserting
             white space between a quoted string and the succeeding identifier is recommended.

即使在单引号前加%也是一样的(按错误提示在两个单引号后各加一个空格是可以的,但那就不是我要的单词了)。所以不知道该怎么办,请p大指教
已有 1 人评分学术水平 热心指数 信用等级 收起 理由
Tigflanker + 3 + 3 + 3 QSYSFUNC可用,同等解释。

总评分: 学术水平 + 3  热心指数 + 3  信用等级 + 3   查看全部评分

使用道具

板凳
pobel 在职认证  发表于 2014-7-1 11:31:14 |只看作者 |坛友微信交流群
playmore 发表于 2014-7-1 08:49
沙发,p大总结的很好

另外我有一个问题,我想把一个字符串宏变量(包括多个单词,用空格分隔)中的每个 ...
试一下用%QSYSFUNC:

%let test=Var1 Var2;
%let test=%QSYSFUNC(TRANWRD(&test,%STR( ),%STR(',')));
%put &test;

使用道具

报纸
playmore 发表于 2014-7-1 12:58:24 |只看作者 |坛友微信交流群
pobel 发表于 2014-7-1 11:31
试一下用%QSYSFUNC:

%let test=Var1 Var2;
多谢,好用

之前我一直用的正则作的替换,正则好就好在用一个\可以转义一切metacharacter了
不像SAS有那么多的宏函数

使用道具

地板
Tigflanker 发表于 2014-7-1 15:24:43 |只看作者 |坛友微信交流群
个人认为Quoting是SAS Base最难最严密的部分了(相对本人),现在就开始研读下。

另外我有时会遇到一些很恶心的问题,在我有空时,我会模拟出原型,向您提问。

例如,我曾在处理一个连接型宏变量的时候,遇到过类似 %let x = a | b | i' c | half (;
这类问题。

在我用正则取串的时候,例如用%sysfunc(prxchange(&prx.,-1,&x.))的时候,貌似问题直接出在prxchange的中间,
貌似他中间抛出的过程串有时会有干扰。。

我目前用的方法很让步,在prxparse阶段,我被迫使用\'.*?\'?或者\(.*?\)?这种方法来折中,
有时感觉SAS的中间方法不够强,或者最终还是少考虑了哪个小细节,反正源源不断的问题。

使用道具

7
alex1206 发表于 2015-8-19 02:15:38 |只看作者 |坛友微信交流群
感谢楼主分享!!!

使用道具

8
木叶知秋 发表于 2015-11-15 14:28:42 |只看作者 |坛友微信交流群
不明白mvar1和mvar2为什么相等,mvar2前面不是有空格吗?
%let mvar1=a,b,c;
%let mvar2=%str( a,b,c );

使用道具

9
pobel 在职认证  发表于 2015-11-17 09:51:08 |只看作者 |坛友微信交流群
木叶知秋 发表于 2015-11-15 14:28
不明白mvar1和mvar2为什么相等,mvar2前面不是有空格吗?
%let mvar1=a,b,c;
%let mvar2=%str( a,b,c );
可以去读一下帮助文档里的这部分 Understanding and Using the Macro Facility / Macro Quoting / How Macro Quoting Works. 里面有这句:
... ...
Macro functions, such as %EVAL and %SUBSTR, ignore the prefix and suffix characters. Therefore, the prefix and suffix characters do not affect comparisons.

使用道具

10
长水滔滔 发表于 2019-7-27 11:32:16 |只看作者 |坛友微信交流群
感谢总结

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-11-6 08:19