如何使用 awk 的 gensub 或替代方案来替换重叠匹配

如何使用 awk 的 gensub 或替代方案来替换重叠匹配

我在用:

$ echo ".a.b.c." | awk '
{
  t=gensub(/.([a-z])./,"[\\1]","g",$1); print t
}'
[a]b[c]

我想结束[a][b][c]。我怎样才能开始gensub处理重叠的比赛?

如果这是不可能的,我怎样才能实现这一目标?

答案1

你可以做类似的事情:

perl -pe 's{\.([a-z](?:\.[a-z])*)\.}{"[$1]" =~ s/\./][/gr}ge'

即把所有的 替换为.x.y.z.[x.y.z]其中.s 替换为][

同样的awk,避免使用gensub()gawk 特有的并且使用起来很麻烦,因为它不会让你知道它何时成功:

awk '
  {
    while(match($0, /(\.[abcdefghijklmnopqrstuvwxyz])+\./)) {
      s = substr($0, RSTART + 1, RLENGTH - 2)
      gsub(/\./, "][", s)
      $0 = substr($0, 1, RSTART - 1) "["s"]" substr($0, RSTART+RLENGTH)
    }
    print
  }'

如果使用当前版本mawk(尚未本地化)或最新版本,gawk或者如果在 C 语言环境中,则可以替换abcdefghijklmnopqrstuvwxyza-z.除了这些条件之外,不能保证它们是等效的。特别是,在某些语言环境中,[a-z]每个 POSIX 旨在匹配一系列整理元素, 不是人物甚至可以匹配多个字符,就像ddzs在匈牙利语言环境中一样。

无论如何,请注意,要匹配正则表达式中的文字点,您需要\.[.].否则,它本身就是一个匹配任何单个字符的正则表达式运算符。

答案2

通常正则表达式引擎不会考虑重叠匹配,这不是您建议的方式,但也不会导致后一个匹配会考虑前一个替换插入的字符。

在 Perl 中,您可以利用后视来匹配右括号的右侧,并重复替换(在整个字符串上),次数与执行的操作次数相同。在这里,假设您要匹配文字点之间的字母(正则表达式.匹配任何单个字符):

% cat test.txt
.a.b.c.
..a..
.a.b  c.d.
.a]b.
% perl -pe '1 while s/(\.|(?<=\]))([a-z])\./[$2]/'  < test.txt
[a][b][c]
.[a].
[a]b  c[d]
.a][b]

最后一行显示误报,其中现有]触发以下b.匹配。您可以通过首先插入一些不太可能出现在输入中的字节,然后用真正的括号替换它们来避免这种情况。例如字节值1,^A:

% perl -pe '1 while s/(\.|(?<=\001))([a-z])\./[$2\001/; tr/\001/]/' < test.txt
[a][b][c]
.[a].
[a]b  c[d]
.a]b.

这可能还有其他我没有碰巧发现的问题。在重复替换时,如果匹配太松,则存在陷入无限循环的风险,但这不应该是一个风险,因为替换需要一个文字点并且总是删除至少一个点。

(如果您的意思是.正则表达式中的 匹配任何字符,那么我想 egabcd应该变成[b]d,这看起来很奇怪。)

答案3

这是红宝石:

echo ".a.b.c." | ruby -pe '$_.gsub!(/(?:[.][a-z](?=\.))|\./){|m| m[/^\.$/] ? "" : "[#{m[1]}]" }'

或者 Perl:

echo ".a.b.c." | perl -pe 's/(?:[.]([a-z])(?=\.))|\./($1 eq "") ? "" : "[$1]" /ge'

要么打印:

[a][b][c]

相关内容