使用 sed 更改找到的模式中的任意数量的分隔符

Change any number of delimiters in found pattern with sed

我想用 sed 将每个 . 更改为 @.@ ,但前提是 . 包含在数字中。
例如:

This sentence ends with a dot. 1.2.3
Dot. 1.2.3.4.5 Dot.

目标:

This sentence ends with a dot. 1 @.@ 2 @.@ 3
Dot. 1 @.@ 2 @.@ 3 @.@ 4 @.@ 5 Dot.

模式可以包含任意数量的整数。

我试过了:

sed -E 's/([0-9]+)\.([0-9]+)/ @\.@ /g'

但它只适用于模式中的前两个数字。

使用您显示的示例,请尝试执行以下操作。在 GNU awk.

中编写和测试
awk -v RS='([0-9]+.)+[0-9]+' '{gsub(/\./," @.@ ",RT);ORS=RT;print}' Input_file

解释: 只需将记录分隔符作为数字(一次或多次出现)和点(.)的一次或多次出现后跟 1 个或多个数字出现。然后根据 OP 的要求用 @.@ 替换最后出现的点,然后重置 ORS 并打印该行。要获得有关 awk 的更多信息,您也可以查看 man awk 页面。

对于重复模式(数字-点-数字-点-数字...)替换不起作用,因为点后面的数字是 “consumed”,所以引擎沿着字符串移动,所以它看到的下一个字符是一个点,而不是所需的 num-dot-num 模式。

一种解决方案是使用 lookarounds,它们是“零宽度”断言,因此引擎不会消耗匹配项并且不会t 继续前进,但它只是从字符之间的“点”“看”来断言模式(前面或后面)匹配,可以说

s/ (?<=[0-9]) \. (?=[0-9]) / @.@ /gx;

对于可测试的示例(在 Perl 中,如标记)

perl -wE'$_=q(Dot. 1.2.3.4.5 Dot.); say; s/(?<=[0-9])\.(?=[0-9])/ @.@ /g; say'

打印

Dot. 1.2.3.4.5 Dot.
Dot. 1 @.@ 2 @.@ 3 @.@ 4 @.@ 5 Dot.

但是lookbehind 不适用于由一个以上数字组成的“number”,从那时起我们需要 [0-9]+,它具有变量和无限长度,whiat lookbehinds 不能(还)做。

如果在您的情况下确实可能有多位数字,那么需要捕获 . 之前的数字——这仍然适用于 before[=42] =] 点 -- 然后放回去

s/([0-9]+)\.(?=[0-9])/ @.@ /g;

当然,这无论如何都可以做到,即使它总是个位数;我最初使用 lookbehind 只是为了与另一边对称(需要 lookahead)


在支持它们的工具中,据我所知 sed 不是。 (感谢 potongEd Morton 的评论告知)我仍然提供这个解决方案,因为 Perl 是标记语言之一。

至于第一行,正则表达式匹配 1.2 进行第一次试验。 下一个模式匹配以字符 . 开始,紧跟在 上一场比赛失败了。
使用 sed 请尝试:

sed -E '
:l
s/([[:digit:]])\.([[:digit:]])/ @.@ /
t l
' file

从字符串的开头迭代模式匹配。

当您在标签中添加 perl 时,这里有一个替代方法 perl:

perl -pe 's/(?<=\d)\.(?=\d)/ @.@ /g' file

这是一个 POSIX awk 解决方案:

awk '{while (match([=10=], /[0-9]\.[0-9]/))
   [=10=] = substr([=10=], 1, RSTART) " @.@ " substr([=10=], RSTART+2)} 1' file

This sentence ends with a dot. 1 @.@ 2 @.@ 3
Dot. 1 @.@ 2 @.@ 3 @.@ 4 @.@ 5 Dot.

在每个 Unix 机器上的任何 shell 中使用任何 sed:

$ sed 's/\([0-9]\)\.\([0-9]\)/ @.@ /g; s/\([0-9]\)\.\([0-9]\)/ @.@ /g' file
This sentence ends with a dot. 1 @.@ 2 @.@ 3
Dot. 1 @.@ 2 @.@ 3 @.@ 4 @.@ 5 Dot.

您需要输入 2 次,因为 1.2.3 的给定输入只有 1.2 会与 [0-9]\.[0-9] 的第一遍匹配,2.3 不会在那次通过中被识别,因为 2 已经被第一场比赛消耗掉了,所以在第一场比赛中剩下的输入是 .3,你需要第二次通过(现在反对 1 @.@ 2.3 ) 匹配 2.3.