regex(3)
regcomp — 正则表达式库
名称
regcomp, regexec, regerror, regfree
库
Lb libc
概要
#include <regex.h>
int
regcomp(regex_t *restrict preg, const char *restrict pattern,
int cflags);
int
regexec(const regex_t *restrict preg, const char *restrict string,
size_t nmatch, regmatch_t pmatch[restrict], int eflags);
size_t
regerror(int errcode, const regex_t *restrict preg,
char *restrict errbuf, size_t errbuf_size);
void
regfree(regex_t *preg);描述
这些例程实现了 IEEE Std 1003.2 ("POSIX.2") 正则表达式("RE");参见 re_format(7)。regcomp 函数将以字符串形式编写的 RE 编译为内部形式,regexec 将该内部形式与字符串进行匹配并报告结果,regerror 将两者的错误代码转换为人类可读的消息,regfree 释放 RE 内部形式所使用的任何动态分配的存储空间。
头文件
声明了两个结构类型,regex_t 和 regmatch_t,前者用于编译后的内部形式,后者用于匹配报告。它还声明了这四个函数、一个 regoff_t 类型,以及许多名称以 "REG_" 开头的常量。
regcomp 函数编译包含在 pattern 字符串中的正则表达式,受 cflags 中的标志约束,并将结果放入 preg 所指向的 regex_t 结构中。cflags 参数是以下零个或多个标志的按位或:
REG_EXTENDED 编译现代("扩展")RE,而不是默认的过时("基本")RE。
REG_BASIC 这是 0 的同义词,作为 REG_EXTENDED 的对应项提供,以提高可读性。
REG_NOSPEC 编译时关闭所有特殊字符的识别。因此所有字符都被视为普通字符,"RE" 即为字面字符串。这是一个扩展,与 IEEE Std 1003.2 ("POSIX.2") 兼容但未被其规定,在意图移植到其他系统的软件中应谨慎使用。REG_EXTENDED 和 REG_NOSPEC 不能在同一次 regcomp 调用中同时使用。
REG_ICASE 编译用于忽略大小写区分的匹配。参见 re_format(7)。
REG_NOSUB 编译用于只需报告成功或失败而无需报告匹配内容的匹配。
REG_NEWLINE 编译用于换行符敏感的匹配。默认情况下,换行符是一个完全普通的字符,在 RE 或字符串中没有任何特殊含义。使用此标志时,[^ 括号表达式和 . 永不匹配换行符,^ 锚点除了其正常功能外,还匹配字符串中任何换行符之后的空字符串,$ 锚点除了其正常功能外,还匹配字符串中任何换行符之前的空字符串。
REG_PEND 正则表达式不在第一个 NUL 处结束,而是在 preg 所指向结构的 re_endp 成员所指向的字符之前结束。re_endp 成员的类型为 const char *。此标志允许在 RE 中包含 NUL;它们被视为普通字符。这是一个扩展,与 IEEE Std 1003.2 ("POSIX.2") 兼容但未被其规定,在意图移植到其他系统的软件中应谨慎使用。
REG_POSIX 仅编译符合 IEEE Std 1003.2 ("POSIX.2") 的表达式。除非链接 libregex,否则此标志无效。这是一个扩展,与 IEEE Std 1003.2 ("POSIX.2") 兼容但未被其规定,在意图移植到其他系统的软件中应谨慎使用。
成功时,regcomp 返回 0 并填充 preg 所指向的结构。该结构的一个成员(re_endp 除外)是公开的:re_nsub,类型为 size_t,包含 RE 中括号括起的子表达式数量(但如果使用了 REG_NOSUB 标志,该成员的值未定义)。如果 regcomp 失败,返回非零错误代码;参见诊断一节。
regexec 函数将 preg 所指向的已编译 RE 与 string 进行匹配,受 eflags 中的标志约束,并使用 nmatch、pmatch 和返回值报告结果。该 RE 必须由之前调用 regcomp 编译。编译形式在 regexec 执行期间不会被修改,因此单个编译的 RE 可同时被多个线程使用。
默认情况下,string 所指向的以 NUL 结尾的字符串被视为整行文本,不包括任何终止换行符。eflags 参数是以下零个或多个标志的按位或:
REG_NOTBOL 字符串的第一个字符被视为某一行的延续。这意味着锚点 ^、[[:<:]] 和 \< 在其之前不匹配;但参见下文的 REG_STARTEND。这不影响 REG_NEWLINE 下换行符的行为。
REG_NOTEOL 终止字符串的 NUL 不结束一行,因此 $ 锚点在其之前不匹配。这不影响 REG_NEWLINE 下换行符的行为。
REG_STARTEND 字符串被视为从 string + pmatch[0].rm_so 开始,在 string + pmatch[0].rm_eo 所指向的字节之前结束,无论 nmatch 的值如何。pmatch 和 nmatch 的定义见下文。这是一个扩展,与 IEEE Std 1003.2 ("POSIX.2") 兼容但未被其规定,在意图移植到其他系统的软件中应谨慎使用。未指定 REG_NOTBOL 时,位置 rm_so 被视为行的开头,^ 在其之前匹配;如果该位置是单词字符,则视为单词开头,[[:<:]] 和 \< 在其之前匹配。指定 REG_NOTBOL 时,位置 rm_so 处的字符被视为行的延续,如果 rm_so 大于 0,则考虑前一个字符。如果前一个字符是换行符且正则表达式使用 REG_NEWLINE 编译,^ 在字符串之前匹配;如果前一个字符不是单词字符但字符串以单词字符开头,[[:<:]] 和 \< 在字符串之前匹配。
关于在 RE 或其一部分可以匹配 string 的多个子字符串中的任何一个时实际匹配什么,参见 re_format(7) 的讨论。
通常,regexec 成功时返回 0,失败时返回非零代码 REG_NOMATCH。在异常情况下可能返回其他非零错误代码;参见诊断一节。
如果在 RE 的编译中指定了 REG_NOSUB,或者 nmatch 为 0,regexec 忽略 pmatch 参数(但指定 REG_STARTEND 的情况见下文)。否则,pmatch 指向一个包含 nmatch 个 regmatch_t 类型结构的数组。这样的结构至少有 rm_so 和 rm_eo 两个成员,均为 regoff_t 类型(一种至少与 off_t 和 ssize_t 一样大的有符号算术类型),分别包含子字符串首字符的偏移量和子字符串结束后第一个字符的偏移量。偏移量从传递给 regexec 的 string 参数的开头计算。空子字符串由相等的偏移量表示,两者都指向空子字符串之后的字符。
pmatch 数组的第 0 个成员被填充以指示 string 的哪个子字符串被整个 RE 匹配。其余成员报告 RE 中括号括起的子表达式匹配了哪个子字符串;成员 i 报告子表达式 i,子表达式按其左括号在 RE 中的顺序从左到右编号(从 1 开始)。数组中未使用的条目(对应于完全未参与匹配的子表达式,或 RE 中不存在的子表达式,即 i > preg->re_nsub)的 rm_so 和 rm_eo 均设置为 -1。如果某个子表达式参与了多次匹配,报告的子字符串是它最后一次匹配的子字符串。(特别注意,当 RE (b*)+ 匹配 bbb 时,括号括起的子表达式匹配三个 'b' 中的每一个,然后匹配最后一个 b 之后的无限个空字符串,因此报告的子字符串是其中一个空字符串。)
如果指定了 REG_STARTEND,pmatch 必须指向至少一个 regmatch_t(即使 nmatch 为 0 或指定了 REG_NOSUB),以保存 REG_STARTEND 的输入偏移量。用于输出仍完全由 nmatch 控制;如果 nmatch 为 0 或指定了 REG_NOSUB,pmatch[0] 的值不会被成功的 regexec 改变。
regerror 函数将来自 regcomp 或 regexec 的非零 errcode 映射为人类可读的可打印消息。如果 preg 非 NULL,错误代码应来自使用 preg 所指向的 regex_t,且如果错误代码来自 regcomp,它应是使用该 regex_t 的最近一次 regcomp 的结果。(regerror 可能能够使用 regex_t 中的信息提供更详细的消息。)regerror 函数将以 NUL 结尾的消息放入 errbuf 所指向的缓冲区,将长度(包括 NUL)限制为最多 errbuf_size 字节。如果整个消息无法容纳,则提供终止 NUL 之前能容纳的部分。无论哪种情况,返回值都是保存整个消息(包括终止 NUL)所需的缓冲区大小。如果 errbuf_size 为 0,忽略 errbuf,但返回值仍然正确。
如果传递给 regerror 的 errcode 先与 REG_ITOA 进行或运算,则得到的"消息"是错误代码的可打印名称,例如 "REG_NOMATCH",而不是其解释。如果 errcode 为 REG_ATOI,则 preg 应非 NULL,且其指向结构的 re_endp 成员必须指向某个错误代码的可打印名称;在这种情况下,errbuf 中的结果是错误代码数值的十进制数字(如果名称未被识别则为 0)。REG_ITOA 和 REG_ATOI 主要用作调试工具;它们是扩展,与 IEEE Std 1003.2 ("POSIX.2") 兼容但未被其规定,在意图移植到其他系统的软件中应谨慎使用。还要注意,它们被视为实验性的,可能会发生变化。
regfree 函数释放与 preg 所指向的已编译 RE 关联的任何动态分配的存储空间。剩余的 regex_t 不再是有效的已编译 RE,将其提供给 regexec 或 regerror 的效果未定义。
这些函数都不引用全局变量(常量表除外);如果参数是安全的,所有函数都可以安全地从多个线程使用。
实现选择
有许多决策由 IEEE Std 1003.2 ("POSIX.2") 留给实现者,要么明确说"未定义",要么由于 RE 语法禁止它们。本实现如下处理。
关于大小写无关匹配定义的讨论,参见 re_format(7)。
RE 的长度没有特定限制,除了内存有限。内存使用量与 RE 大致成线性关系,且在很大程度上对 RE 复杂度不敏感,但有界重复除外。参见缺陷一节中一个使用有界重复的短 RE,它将使几乎所有系统耗尽内存。
除 IEEE Std 1003.2 ("POSIX.2") 特别赋予魔法含义的反斜杠字符外(此类魔法含义仅出现在过时的 ["基本"] RE 中),其他反斜杠字符被视为普通字符。
任何未匹配的 [ 都是 REG_EBRACK 错误。
等价类不能开始或结束括号表达式范围。一个范围的端点不能开始另一个范围。
RE_DUP_MAX,即有界重复中重复计数的限制,为 255。
重复运算符(?、*、+ 或界限)不能跟在另一个重复运算符之后。重复运算符不能开始一个表达式或子表达式,也不能跟在 ^ 或 | 之后。
| 不能出现在(子)表达式的开头或结尾,也不能跟在另一个 | 之后,即 | 的操作数不能是空子表达式。空的括号括起子表达式 () 是合法的,匹配空(子)字符串。空字符串不是合法的 RE。
后跟数字的 { 被视为有界重复界限的开始,随后必须遵循界限语法。不跟数字的 { 被视为普通字符。
在过时("基本")RE 中,开始和结束子表达式的 ^ 和 $ 是锚点,而非普通字符。
诊断
来自 regcomp 和 regexec 的非零错误代码包括:
REG_NOMATCH regexec 函数匹配失败
REG_BADPAT 无效的正则表达式
REG_ECOLLATE 无效的整理元素
REG_ECTYPE 无效的字符类
REG_EESCAPE \ 应用于不可转义的字符
REG_ESUBREG 无效的反向引用编号
REG_EBRACK 方括号 [ ] 不平衡
REG_EPAREN 圆括号 ( ) 不平衡
REG_EBRACE 花括号 { } 不平衡
REG_BADBR { } 中的重复计数无效
REG_ERANGE [ ] 中的字符范围无效
REG_ESPACE 内存耗尽
REG_BADRPT ?、* 或 + 的操作数无效
REG_EMPTY 空(子)表达式
REG_ASSERT 不可能发生——你发现了一个 bug
REG_INVARG 无效参数,例如负长度字符串
REG_ILLSEQ 非法字节序列(错误的多字节字符)
参见
grep(1), re_format(7)
IEEE Std 1003.2 ("POSIX.2") 第 2.8 节(正则表达式表示法)和 B.5 节(正则表达式匹配的 C 绑定)。
历史
最初由 Henry Spencer 编写。经修改后纳入 4.4BSD 发行版。
缺陷
这是一个 alpha 版本,存在已知缺陷。请报告问题。
反向引用代码较为微妙,在复杂情况下其正确性仍有疑虑。
regexec 函数性能较差。这将在后续版本中改进。nmatch 参数超过 0 开销较大;nmatch 超过 1 更糟。regexec 函数在很大程度上对 RE 复杂度不敏感,但反向引用的开销极大。RE 长度确实有影响;特别是,将 RE 长度保持在约 30 个字符以下有显著的速度优势,大多数特殊字符大致按双倍计算。
regcomp 函数通过宏展开实现有界重复,如果计数很大或有界重复嵌套,在时间和空间上开销都很大。例如 ((((a{1,100}){1,100}){1,100}){1,100}){1,100} 这样的 RE 将(最终)使几乎所有现有机器耗尽交换空间。
对罕见错误条件的响应存在疑似问题。特别是,某些类型的内部溢出(仅由真正巨大的 RE 或多层嵌套的有界重复产生)可能处理不当。
由于 IEEE Std 1003.2 ("POSIX.2") 中的一个错误,像 a)b 这样的表达式是合法的 RE,因为 ) 只有在存在先前未匹配的 ( 时才是特殊字符。在规范修复之前无法修复此问题。
标准对反向引用的定义模糊。例如,a\(\(b\)*\2\)*d 是否匹配 abbbd?在标准澄清之前,不应依赖此类情况下的行为。
词边界匹配的实现有些取巧,词边界匹配与锚定的组合中可能隐藏 bug。
词边界匹配在多字节 locale 中无法正常工作。
最后更新于
这有帮助吗?