为什么正则表达式 [0-9]{0,2} 在 sed 中不贪婪?

run*_*rin 3 sed shell-script regular-expression

echo '123980925sriten34=ienat' | sed -e 's/^.*\?\([1-9][0-9]\{0,2\}\+\)\([%=+-]\).*/ \1 \2 /'
Run Code Online (Sandbox Code Playgroud)

正在给出结果:

 4 =
Run Code Online (Sandbox Code Playgroud)

我期待:

 34 =
Run Code Online (Sandbox Code Playgroud)

我不明白什么?

(哦,我什至添加了+?以加倍确定,但是{0,2}没有它们afaik应该是贪婪的。)

G-M*_*ca' 11

正如steeldriver 所说,问题不在于[0-9]{0,2}它是非贪婪的;问题是它.*?之前是贪婪的。  sed 支持 BRE 和 ERE,两者都不支持非贪婪匹配。这是 PCRE 的一个特性。例如,以下命令:

$ echo 'aQbQc' | sed    's/.*\?Q/X/'
$ echo 'aQbQc' | sed    's/.*Q/X/'
$ echo 'aQbQc' | sed -r 's/.*?Q/X/'
$ echo 'aQbQc' | sed -r 's/.*Q/X/'
Run Code Online (Sandbox Code Playgroud)

所有输出

$ echo 'aQbQc' | sed    's/.*\?Q/X/'
$ echo 'aQbQc' | sed    's/.*Q/X/'
$ echo 'aQbQc' | sed -r 's/.*?Q/X/'
$ echo 'aQbQc' | sed -r 's/.*Q/X/'
Run Code Online (Sandbox Code Playgroud)

(我不确定为什么它只是忽略了?.)

请参阅与 SED regex (emulate perl's .*?) 的非贪婪匹配

您对要执行的功能的描述很肤浅,但我相信我已经对其进行了逆向工程。您可以通过在找到数字之前不匹配要匹配的数字之前的字符来获得所需的效果 :

$ echo '123980925sriten34=ienat' | sed -e 's/\([1-9][0-9]\{0,2\}\+\)\([%=+-]\).*/! \1 \2 /' -e 's/.*!//'
 34 =
Run Code Online (Sandbox Code Playgroud)

!任何已知不会出现在输入数据中的字符串替换。如果您没有这样的字符串,但您使用的是 GNU sed,则可以使用换行符:

$ echo '123980925sriten34=ienat' | sed -e 's/\([1-9][0-9]\{0,2\}\+\)\([%=+-]\).*/\n \1 \2 /' -e 's/.*\n//'
 34 =
Run Code Online (Sandbox Code Playgroud)

当然,它不能出现任何一行中。

  • wrt `我不知道为什么它只是忽略 ?` - 因为在另一个重复 RE 元字符(在这种情况下为 `*`)之后`?` 是每个 POSIX 的未定义行为,并且因为在其他上下文中它意味着`零或-1 ` 忽略它和任何方法一样合理。本质上,`.*?` 应该被视为正则表达式中的错误,因为它没有任何合理的含义(任何字符的零次或多次重复的零次或一次重复 - 嗯?)。 (5认同)
  • 在 GNU 系统上,重复运算符堆栈,`+` 之后的 `?`(使用 ERE 语法)使整个事物成为可选的(与 `*` 相同),并且 `{2,4}?` 匹配 0, 2, 3或 4 次重复(尝试对 `bb`、`bab`、`baab` 使用 `grep -Eoe 'ba{2,4}?b'`)。`*?` 与 `*` 相同,因为 `*` 已经可以匹配零次重复。`?` 使模式变得非贪婪是 Perl 正则表达式的一个特性。 (5认同)