如何通过Perl正则提取SAS程序中符合规则的已使用函数
原实现的匹配偏差来源
逐行对比原抓取结果和预期结果的差异,所有误匹配都来自原有正则的覆盖范围过宽:
- 误抓
class:来自sashelp.class(firstobs=2)的场景,该位置是逻辑库前缀+数据集名的结构,后续括号是数据集选项,并非函数调用 - 误抓
sysfunc:来自带%前缀的宏函数,按规则需要排除 - 误抓
execute:来自call关键字引导的SAS子程序,按规则需要排除
原正则没有对上述场景做边界判断,同时也缺少对宏变量形态无效匹配的过滤。
满足规则的正则调整思路
不需要做全语法解析,仅通过给原有匹配模式加前置负向断言、微调匹配边界,即可覆盖所有要求:
- 加负向断言
(?<!\.):排除左侧紧邻点号的标识符(即libname.member(...)这类带选项括号的数据集/变量名) - 加负向断言
(?<!%):排除左侧紧邻%的宏函数调用 - 加负向断言
(?<!&):排除左侧紧邻&的宏变量形态匹配(如&function()这类无效场景) - 加负向断言
(?<!\bcall\s+):排除call关键字引导的子程序,要求call为独立单词,允许call和目标标识符之间存在任意空白、制表符 - 微调匹配主体:将原有的“标识符紧跟左括号”,调整为“标识符后允许任意空白再跟左括号”,适配SAS语法中函数名和左括号间可存在空白的写法
- 正则加
/i修饰符开启不区分大小写匹配,适配SAS大小写不敏感的语法特性
修正后的可直接运行的SAS代码
data _null_; infile 'test.sas' truncover; input prog $1024.; start = 1; stop = length(prog); * 修正后的函数匹配正则,覆盖所有排除规则; FuncPat = prxparse('/(?<!\.)(?<!%)(?<!&)(?<!\bcall\s+)([a-zA-Z_]\w*)\s*\(/i'); call prxnext(FuncPat, start, stop, prog, position, length); do while (position > 0); * 提取匹配到的函数名,去掉匹配到的空白和左括号部分; raw_match = substr(prog, position, length); found = prxchange('s/\s*\($//', -1, raw_match); put found=; call prxnext(FuncPat, start, stop, prog, position, length); end; run;
匹配效果验证
用提供的测试文件运行修正后的代码,输出结果完全符合预期:
found=rand found=sign found=round found=date
同时满足其他规则要求:
- 不会漏抓用户自定义函数:只要是
自定义函数名(...)的调用,都会被正常匹配,不会被排除规则误伤 - 可以正常识别赋值语句场景的函数调用(如
x=substr(...)),这类场景下函数名左侧是等号,不会触发任何排除断言 - 会自动过滤
&func()形式的宏变量无效匹配
内容的提问来源于stack exchange,提问作者whymath
相关产品推荐
相关产品推荐

