SAS - 正则表达式
SAS - 正则表达式
正则表达式(Regular Expression)是一种文本模式,可用于文本的匹配、查找和替换。Base SAS 内置了正则表达式的实现,并提供了一系列 PRX- 函数及 CALL 子程序,便于对文本进行更为灵活的处理。
正则表达式的结构
正则表达式由普通字符和元字符组成。元字符用于匹配符合特定规则的字符集合,例如:数字、字母、标点符号等字符集合。
元字符又分为:基本元字符、定界符、限定符等。
基本元字符
常用的基本元字符如下:
- \d : 匹配数字0-9
- \n : 匹配换行符
- \s : 匹配空白字符,包括空格、制表符等
- \t : 匹配制表符
- \w : 匹配任何单词字符、数字字符、下划线
例如:\d\d
将匹配两个连续的数字。
定界符
定界符用于限定匹配字符串的边界。
- ^ : 匹配字符串的开头
- $ : 匹配字符串的结尾
- \b : 匹配一个单词的边界
- \B : 匹配一个单词的非边界
例如:^apple$
只能匹配单词 apple,这是因为使用了定界符,^ 限制匹配的单词只能以 a 开头,$ 限制匹配的单词只能以 e 结束。如果不加定界符,apple
不仅可以匹配单词 apple,还可以匹配单词 pineapple 中的 apple。
限定符
限定符用于限制某个匹配模式的重复次数,不使用限定符的情况下是匹配一次。
- {n} : 匹配 n 次
- {n,} : 匹配至少 n 次
- {n,m} : 匹配至少 n 次,至多 m 次
- * : 匹配 0 次或更多次,等价于 {0,}
- + : 匹配 1 次或更多次,等价于 {1,}
- ? : 匹配 0 次或 1 次,等价于 {0,1}
例如:\d+
表示匹配至少一个数字; ^app\w{0,2}$
表示匹配以 app 开头的长度不超过 5 的字符串,包括 apple、apply 等。
限定符的匹配默认是贪婪(Greedy)的,即尽可能匹配更多的字符,但这种默认行为有时候并不是我们想要的。这时候可以使用非贪婪(Lazy)匹配,只需要在贪婪匹配的限定符后添加一个 ?
即可。
例如:fo{1,3}?
会匹配字符串 foooooo 中的 fo,而 fo{1,3}
则会匹配到 fooo,因为这是一个贪婪模式,会尽可能匹配更多的字符 o
。
其他元字符
- | : 匹配两个模式之一
- . : 匹配任何字符,除了换行符
- () : 括号内部是一个匹配模式,表示一个组合,该组合匹配到的字符串将被捕获
- (?: ) : 括号内部是一个匹配模式,表示一个组合,该组合匹配到的字符串不会被捕获
- \ : 匹配一个特殊字符,类似于转义
例如:
dog|t
表示匹配字符串 dog 或 tdo(g|t)
表示匹配字符串 dog 或 dot;.*
表示匹配任意多个字符,不包括换行符\.
表示匹配字符 "." 本身
集合
如果需要匹配多个字符中的任何一个,一种解决办法是连续使用多个 |
,但更推荐的是使用集合。使用 []
表示一个集合,其中包含需要匹配的若干字符。例如:[abcde]
表示匹配字母 a~e,等价于 (a|b|c|d|e)
。如果需要进行反向匹配,只需要在开头加上 ^
符号,例如:[^abcde]
匹配任何字母 a~e 之外的字符。
- [...] : 匹配集合中的任意字符
- [^...] : 匹配集合外的任意字符
对于常见的集合,SAS 提供了更为简洁的表达方式:
- [a-z] : 匹配小写字母 a~z
- [A-Z] : 匹配大写字母 A~Z
- [0-9] : 匹配数字 0~9
- [[:alpha:]] : 匹配单个字母
- [[:alnum:]] : 匹配单个字母或数字
- [[:blank:]] : 匹配单个空白字符
- [[:digit:]] : 匹配单个数字
- [[:lower:]] : 匹配单个小写字母
- [[:upper:]] : 匹配单个大写字母
- [[:punct:]] : 匹配单个标点符号
例如:[A-Za-z_0-9_][A-Za-z0-9]{0,7}
可以匹配一个 SAS V5 下合法的变量名。
Tips: 更多正则表达式语法可查阅帮助文档:SAS 系统文档 > SAS 产品 > Base SAS > SAS® 9.4 Function and CALL Routines: Reference, Fifth Edition > Appendixes > Tables of Perl Regular Expression(PRX) Metacharacters
正则表达式的构建
通过构建合适的正则表达式,可以实现复杂规则的字符串的匹配和替换操作。下面通过一个实例,来理解正则表达式的完整构建过程。
创建包含若干文本型日期的数据集,其中某些日期是不完整的,使用 “uk” 进行替代:
data test;
input dtc $10.;
cards;
2023-07-14
2003/07/UK
1999-07-uk
2015/UK/uk
1982-UK-17
ukuk-04-uK
ukuk/UK-uk
;
run;
现在,需要使用正则表达式对所有文本型日期进行模式匹配。观察数据,可以发现,这些文本型日期都有共同的规律,即按年、月、日的顺序呈现,使用 “-” 或 “/” 作为分隔符。
首先,使用 d{4}
和 \d{2}
可以匹配完整的年、月、日;使用 (\/|\-)
可以匹配年、月、日之间的分隔符;因此,可以用以下正则表达式匹配一个完整的日期:
\d{4}(\/|\-)\d{2}(\/|\-)\d{2}
其次,部分不完整的日期使用“uk”替代,因此还需要额外使用 [Uu][Kk]
进行匹配,上述正则表达式进一步演变为:
(\d{4}|([Uu][Kk]){2})(\/|\-)(\d{2}|[Uu][Kk])(\/|\-)(\d{2}|[Uu][Kk])
最后,加上定界符,限制匹配的开头和结尾:
^(\d{4}|([Uu][Kk]){2})(\/|\-)(\d{2}|[Uu][Kk])(\/|\-)(\d{2}|[Uu][Kk])$
至此,一个用于匹配不完整文本型日期的正则表达式就构建完成了。
字符串匹配
正则表达式构建完成后,就可以应用于字符串的匹配。SAS 提供了 PRXMATCH
函数用于正则表达式的模式匹配。
我们可以在函数 PRXMATCH
中直接使用正则表达式,也可以先使用函数 PRXPARSE
对正则表达式进行解析,获得解析后的 ID,然后在函数 PRXMATCH
中使用这个 ID。
在 SAS 中,正则表达式本身也是一个字符串,为了不与普通字符串相混淆,需要在正则表达式两端添加斜杠 / 以示区分,以下为前一节中的正则表达式在匹配模式下的呈现形式:
/^(\d{4}|([Uu][Kk]){2})(\/|\-)(\d{2}|[Uu][Kk])(\/|\-)(\d{2}|[Uu][Kk])$/
完整 SAS 代码如下:
data match;
set test;
id = prxparse("/^(\d{4}|([Uu][Kk]){2})(\/|\-)(\d{2}|[Uu][Kk])(\/|\-)(\d{2}|[Uu][Kk])$/");
if prxmatch(id, dtc) then do;
flag = "匹配成功";
end;
else do;
flag = "匹配失败";
end;
run;
匹配结果:
字符串替换
除了使用正则表达式匹配字符串外,通常我们还希望能够实现字符串的替换。SAS 提供的 PRXCHANGE
函数可以帮助我们完成这一任务。
在 SAS 中,字符串的替换需要使用替换模式的正则表达式,我们需要在匹配模式的正则表达式开头添加字符 s
,并在末尾追加替换结果的正则表达式。
例如:s/(\d{4})-(\d{2})-(\d{2})/$1$2$3/
表示匹配 “YYYY-MM-DD” 的日期,并删除分隔符 “-”。其中 $1
, $2
, $3
表示对缓冲区字符串的引用,缓冲区的字符串是通过元字符 ()
来捕获的,左半括号出现的位置代表了缓冲区的序号,即 $1
表示捕获年份 “YYYY” 的第一个缓冲区, $2
表示捕获月份 “MM” 的第二个缓冲区,以此类推。括号的捕获行为是默认的,如果仅仅是表示一个组合而不进行捕获,可以在括号内开头添加 ?:
,代表不进行捕获,不会生成缓冲区的序号。
回到前面匹配不完整文本型日期的例子,现在,我们需要在匹配完成之后,将日期的展示格式统一修改为 “MM-DD-YYYY” 的形式。以下为这个例子中的正则表达式在替换模式下的呈现形式:
s/^(\d{4}|(?:[Uu][Kk]){2})(?:\/|\-)(\d{2}|[Uu][Kk])(?:\/|\-)(\d{2}|[Uu][Kk])$/$2-$3-$1/
完整 SAS 代码如下:
data replace;
set test;
id = prxparse("s/^(\d{4}|(?:[Uu][Kk]){2})(?:\/|\-)(\d{2}|[Uu][Kk])(?:\/|\-)(\d{2}|[Uu][Kk])$/$2-$3-$1/");
dtc_new = prxchange(id, -1, dtc);
run;
替换结果:
其他函数和子程序
除了匹配和替换字符串之外,SAS 还提供了一些其他正则表达式相关的函数,例如:
PRXPOSN
: 获取指定缓冲区字符串的函数CALL PRXPOSN
:PRXPOSN
的子程序版本CALL PRXSUBSTR
: 获取匹配指定模式的字符串位置和长度信息CALL PRXNEXT
: 循环迭代获取匹配指定模式的字符串...
相关函数和子程序的具体说明可查阅 SAS 帮助文档。
正则表达式的可视化
正则表达式虽然很强大,但通常被认为是易写难读的,在提高字符串处理效率的同时牺牲了部分可读性。一些可视化工具可以帮助我们更好地理解正则表达式,在构建正则表达式的时候提供些许辅助作用。
Jex.im
这是一个 JavaScript 正则表达式(大部分语法也适用于 SAS)的可视化工具,将正则表达式拆解并以图形的方式进行呈现,可以帮助理解正则表达式的具体含义。
Runoob 在线测试工具
网址:https://c.runoob.com/front-end/854/
这是一个正则表达式在线测试工具,将正则表达式应用到待匹配文本中,并返回匹配结果,支持替换文本。