为什么我的正则表达式在 X 中有效但在 Y 中无效?

为什么我的正则表达式在 X 中有效但在 Y 中无效?

我编写了一个在某个程序中运行良好的正则表达式(grep、sed、awk、perl、python、ruby、ksh、bash、zsh、find、emacs、vi、vim、gedit,...)。但是当我在不同的程序(或不同的 UNIX 变体)中使用它时,它会停止匹配。为什么?

答案1

不幸的是,由于历史原因,不同的工具具有稍微不同的正则表达式语法,有时某些实现具有其他工具不支持的扩展。虽然有共同点,但似乎每个工具编写者都做出了一些不同的选择。

结果是,如果您的正则表达式适用于一种工具,则可能需要修改它才能适用于另一种工具。常用工具之间的主要区别是:

  • 运算符是否+?|(){}需要反斜杠;
  • .[]*^$除了基本功能之外,通常还支持哪些扩展+?|()

在这个答案中,我列出了主要标准。有关详细信息,请检查您正在使用的工具的文档。

维基百科的正则表达式引擎的比较有一个表格列出了常见实现支持的功能。

基本正则表达式(布雷)

基本正则表达式由POSIX标准。这是使用的语法grep,sedvi。该语法提供以下功能:

  • ^并且$仅在行的开头和结尾匹配。
  • .匹配任何字符(或除换行符之外的任何字符)。
  • […]匹配括号内列出的任何一个字符(字符集)。如果左括号后的第一个字符是 a ^,则匹配未列出的字符。要包含],请将其立即放在开头之后[[^如果是负集,则放在后面)。如果-是在两个字符之间,则表示一个范围;要包含文字-,请将其放在无法解析为范围的位置。
  • ^$.*\[任何引用下一个字符之前的反斜杠。
  • *与前面的字符或子表达式匹配 0、1 次或多次。
  • \(…\)是一个语法组,与*运算符或反向引用和\DIGIT替换一起使用。
  • 反向引用\1, \2, ... 与相应组匹配的确切文本匹配,例如\(fo*\)\(ba*\)\1匹配foobaafoo但不匹配foobaafo。没有标准的方法来引用第 10 组及之后的组( 的标准含义\10是第一组后跟0)。

以下功能也是标准功能,但在某些受限实现中缺少:

  • \{m,n\}匹配前面的字符或子表达式n次;n或者可以省略,并且表示准确\{m\}
  • 括号内,字符类可以使用,例如[[:alpha:]]匹配任何字母。现代实现括号表达式)还包括整理元素like[.ll.]和等价类 like [=a=]

以下是常见的扩展(尤其是在 GNU 工具中),但并非在所有实现中都可以找到它们。检查您正在使用的工具的手册。

  • \|对于交替:foo\|bar匹配foobar
  • \?( 的缩写\{0,1\}) 和\+( 的缩写\{1,\}) 分别与前面的字符或子表达式最多匹配 1 次或至少 1 次。
  • \n匹配换行符、\t匹配制表符等。
  • \w匹配任何单词成分(缩写[_[:alnum:]]但在本地化方面有变体)并\W匹配任何不是单词成分的字符。
  • \<\>分别仅在单词的开头或结尾匹配空字符串;\b要么匹配,要么\B匹配\b不匹配的地方。

请注意,没有运算符的工具\|不具有正则表达式的全部功能。反向引用允许做一些数学意义上正则表达式无法完成的额外事情。

扩展正则表达式(埃雷)

扩展正则表达式由POSIX标准。与 BRE 相比,它们的主要优势是规律性:所有标准运算符都是纯标点符号,标点符号之前的反斜杠总是引用它。这是使用的语法awk,grep -Eegrep、 BSD (以及 GNU 和很快的 POSIX)sed -E(以前sed -r在 GNU 中sed),以及bash / ksh93 / yash / zsh 的=~操作员。该语法提供以下功能:

  • ^并且$仅在行的开头和结尾匹配。
  • .匹配任何字符(或除换行符之外的任何字符)。
  • […]匹配括号内列出的任何一个字符(字符集)。首字母^和范围的补足工作类似于 BRE(见上文)。字符类可以使用,但在一些实现中缺失。现代实现还支持等价类和整理元素。在某些但不是所有实现中,括号内的反斜杠引用下一个字符;用于\\表示可移植性的反斜杠。
  • (…)是一个句法组,用于与*\DIGIT替换一起使用。
  • |对于交替:foo|bar匹配foobar
  • *+?多次匹配前面的字符或子表达式: 0 次或多次*, 1 次或多次+, 0 或 1 次?
  • 如果下一个字符不是字母数字,则反斜杠将其引用。
  • {m,n}匹配前面的字符或子表达式n次(某些实现中缺少);n或者可以省略,并且表示准确{m}
  • BRE 中的一些常见扩展:反向引用(尤其是 awk 中不存在,除了在 busybox 实现中可以使用);特殊字符、等;词边界and , 词成分and , ...\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE(与 Perl 兼容的正则表达式)

PCRE 是 ERE 的扩展,最初由珀尔并被 GNU 采用grep -P许多现代工具和编程语言,通常通过聚合酶链式反应图书馆。请参阅Perl 文档带有示例的良好格式。 PCRE 并不支持最新版本 Perl 的所有功能(例如,仅 Perl 支持 Perl 代码执行)。请参阅PCRE手册了解受支持功能的摘要。 ERE 的主要新增内容包括:

  • (?:…)是一个非捕获组:类似于(…),但不计入反向引用。
  • (?=FOO)BAR(前瞻)匹配BAR,但前提是也存在从相同位置开始的匹配FOO。这对于锚定匹配而不在匹配中包含以下文本最为有用:foo(?=bar)匹配foo但前提是其后跟bar
  • (?!FOO)BAR(负向前瞻)匹配,但在同一位置BAR没有匹配。FOO例如,(?!foo)[a-z]+匹配任何不以foo;开头的小写单词。[a-z]+(?![0-9)匹配任何后面不跟数字的小写单词(因此在 中foo123,它匹配fo但不匹配foo)。
  • (?<=FOO)BAR(lookbehind) 匹配BAR,但前提是它前面紧接着是 的匹配项FOOFOO必须具有已知长度(不能使用重复运算符,例如*)。这对于锚定匹配而不在匹配中包含前面的文本非常有用:(?<=^| )foo匹配foo,但前提是前面有空格或字符串开头。
  • (?<!FOO)BAR(负向后查找)匹配BAR,但前提是它前面没有紧接着 的匹配项FOOFOO必须具有已知长度(不能使用重复运算符,例如*)。这对于锚定匹配而不在匹配中包含前面的文本非常有用:(?<![a-z])foo匹配foo,但前提是前面没有小写字母。

Emacs

Emacs 的语法介于 BRE 和 ERE 之间。除了 Emacs 之外,它是-regexGNU find 的默认语法。 Emacs 提供以下运算符:

  • ^, $, ., […], *, +,?如 ERE 中所示
  • \(…\), \|, \{…\},如 BRE 中所示\DIGIT
  • 更多反斜杠字母序列;\<以及\>单词边界;以及最新版本的 Emacs 中的更多功能,而其他具有类似 Emacs 语法的引擎通常不支持这些功能。

球体

Shell glob(通配符)使用与正则表达式完全不同且功能较弱的语法执行模式匹配。除了 shell 之外,这些通配符还可用于其他工具,例如find -name和 rsync 过滤器。POSIX 模式包括以下功能:

  • ?匹配任何单个字符。
  • […]是常见正则表达式语法中的字符集。某些 shell 不支持字符类。有些 shell 需要!而不是^否定该集合。
  • *匹配任何字符序列(通常/匹配文件路径时除外;如果/从 中排除*,则**有时包含/,但请检查工具的文档)。
  • 反斜杠引用下一个字符。

Ksh 优惠附加功能这使其模式匹配具有正则表达式的全部功能。这些功能在 bash 中运行后也可用shopt -s extglob。 Zsh 有一个不同的语法但也可以支持ksh之后的语法setopt ksh_glob


1 除非rematchpcre启用该选项,在zsh这种情况下=~会使用 PCRE。 ksh93 的扩展正则表达式还支持一些 Perl 的扩展运算符,例如环视运算符。

相关内容