Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Regex 为什么sed不';是否打印可选组?_Regex_String_Bash_Sed_Regex Group - Fatal编程技术网

Regex 为什么sed不';是否打印可选组?

Regex 为什么sed不';是否打印可选组?,regex,string,bash,sed,regex-group,Regex,String,Bash,Sed,Regex Group,我有两个字符串,分别是foo\u bar和foo\u abc\u bar。我想将两者匹配,如果第一个匹配,我想用=符号强调它。所以,我的猜测是: echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g' > foo==bar 或 但正如上面的输出所示,它们都不起作用 如何指定一个可选组,如果字符串包含该组,则该组将匹配,如果不包含,则跳过该组?解决方案: echo 'foo_abc_bar' | sed -r 's/(

我有两个字符串,分别是foo\u barfoo\u abc\u bar。我想将两者匹配,如果第一个匹配,我想用=符号强调它。所以,我的猜测是:

echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g'
> foo==bar

但正如上面的输出所示,它们都不起作用


如何指定一个可选组,如果字符串包含该组,则该组将匹配,如果不包含,则跳过该组?

解决方案:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
您以前的尝试失败的原因:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
*
是贪婪的,因此对于正则表达式
(foo)。*(abc)?*(bar)
尝试匹配
“foo\u abc\u bar”
时,
(foo)
将匹配
“foo”
,然后
*
将首先匹配字符串的其余部分(
“u abc\u bar”
)。正则表达式将继续运行,直到到达所需的
(bar)
组,这将失败,此时正则表达式将通过放弃与
*
匹配的字符来回溯。直到第一个
*
仅匹配
“abc”
,最后一组才可以匹配
“bar”
。因此,不是在捕获组中匹配字符串中的
'abc'
,而是在非捕获组中匹配

我的解决方案说明:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
首先也是最重要的一点是将
*
替换为
.
,如果您知道分隔符是什么,则无需匹配任何任意字符串。我们需要做的下一件事是准确地找出字符串的哪一部分是可选的。如果字符串<代码>“FuixAbcBar Bar”和 > FooWiBar 都是有效的,那么中间的<代码> 'abc'’/CODE >是可选的。我们可以使用
(abc?)
将其放入可选组。最后一步是确保在捕获组中仍然有字符串
'abc'
,我们可以通过将该部分包装到另一个组中来完成,因此我们最终得到
((abc)\)
。然后我们需要调整替换,因为有一个额外的组,所以我们使用
\1=\2=\3=\4
,而不是
\1=\3=\4
\2
将是字符串
'abc'
(如果匹配)。请注意,在大多数regex实现中,您也可以使用非捕获组并继续使用
\1=\2=\3
,但sed不支持非捕获组

另一种选择:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
我认为上面的正则表达式是最好的选择,因为它是最显式的(它将只匹配您感兴趣的确切字符串)。但是,您也可以通过使用惰性重复(匹配尽可能少的字符)而不是贪婪重复(匹配尽可能多的字符),来避免上述问题。您可以通过将
*
更改为
*?
来完成此操作,因此您的表达式如下所示:

echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'

也许你可以简单地使用:

echo 'foo_abc_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'
echo 'foo_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'

> foo=abc=bar=
> foo=bar=

这避免了使用
foo==bar
时出现的
foo==bar
,我发现在匹配前有时在匹配后使用
=
来显示强调有点奇怪。

为什么在表达式中使用
*
,下划线可以用任意字符串替换吗?