【问题标题】:Overlapping text substitution with Perl regular expression使用 Perl 正则表达式进行重叠文本替换
【发布时间】:2012-01-17 19:45:07
【问题描述】:

我有一个包含一堆句子的文本文件。句子包含空格(空格、制表符、换行符)以分隔由字母和/或数字组成的单词。 我想找到单词“123”或“-123”并在数字开始前插入一个点 (.)。因此,所有出现的“123”和“-123”都将转换为“.123”和“-.123”。

我正在尝试以下方法:

$line =~ s/(\s+-*123\s+)/getNewWord($1)/ge

其中 $line 包含从文件中读取的一行,getNewWord word 函数会将点 (.) 放在匹配单词的适当位置。

但它不适用于有两个连续的“123”如“123 123”的情况。由于第一个“123”被替换为“.123”,因此单词后面的空格已经匹配,而第二个“123”不匹配,因为正则表达式引擎无法将前面的空格与该单词匹配。

谁能帮我解决这个问题?谢谢!

【问题讨论】:

  • 我没有看到这个问题中的“重叠”是什么?
  • 可能这是一个更简单的解决方案 s/(-?)\b(\d+)\b/$1.$2/g

标签: regex perl


【解决方案1】:

我同意 MRAB 的观点(并为他/她的回答 +1),但实际上并不需要 getNewWord 函数。我会将整个语句更改为以下内容之一:

$line =~ s/((?:^|\s)-?)(123)(?=\s|$)/$1.$2/g;

$line =~ s/(?:^|(?<=\s))(-?)(123)(?=\s|$)/$1.$2/g;

$line =~ s/(?:^|(?<=\s)|(?<=\s-))(?=123(?:\s|$))/./g;

【讨论】:

  • ruakh,谢谢你的好回答。我实际上在这个问题上犯了一个小错误。我想要输入“-123”的“-.123”,而不是“.-123”!除了这件小事之外,您的第三个答案似乎可以完成这项工作。谢谢!
  • @ruakh 是的,从我们的角度来看,它是等效对称的。 =) 但是,我的观点是正确的。由于您的核心匹配需要空格,^123(开始)和123$(结束)将不匹配,这可能是个问题。
  • @GolamKawsar:澄清一下:我的版本中 none 与行首的“123”匹配。这是故意的;你原来的正则表达式有那个属性,我想我应该保留它。问题不在于我的回答做了什么,而在于你想要它做什么。
  • @GolamKawsar:感谢您的澄清;我已经相应地更新了我的答案。
  • @TLP:这不是“逻辑”问题,而是前提问题。问题文本中描述的行为与问题中源代码的行为不匹配。你的前提是文本是规范的,源代码信息丰富;我的前提是源代码是规范的(除了由 OP 确定的特定错误)并且文本信息丰富。鉴于这些相反的前提,我们都在逻辑上得出了相反的结论。没有必要对此感到厌烦。 ;-)
【解决方案2】:

它可能会稍微快一些(没有显式捕获)并且它允许文件没有前导/尾随空格:

$ echo '123 -123 -123  123' | perl -pe's/(?:^|\s+)\K(?=-?123\b)/./g'
.123 .-123 .-123  .123

. 放在- 之后:

$ echo '123 -123 -123  123' | perl -pe's/(?:^|\s+)-*\K(?=123\b)/./g'
.123 -.123 -.123  .123

【讨论】:

  • 注意:\K 需要 Perl 5.10 或更新版本。
  • 请注意,单词边界断言\b 将匹配的不仅仅是过渡到空白,例如123?123-123
  • +1 是除了我之外唯一考虑行首的人。
  • @TLP: \b 是为了避免匹配 1234 而设计的。插入 . 只有前导空格很重要。
【解决方案3】:

尝试使用像这样的积极前瞻:(\s+-*123)(?=\s)

【讨论】:

  • 原来的正则表达式也不会!
【解决方案4】:

这让我想起了这个问题:Search html file for random string using regex,在那里我发现(显示)否定环视断言的一个很好的用途,即匹配可选分隔符避免部分匹配。

匹配-?123很简单,问题是

  1. 部分字符串不匹配
  2. 避免行首/行尾不匹配
  3. 避免移动 \G 锚点
  4. 对可选破折号-? 进行后向断言

我没有设法解决 #4,因为不支持可变长度后向断言,所以修复是使用捕获组。

请注意,此问题的其他一些答案并未解决这些问题。

说明:

非空白的否定后向断言匹配空白和字符串的开头,并确保我们不匹配部分字符串。然后跟随捕获组中的可选破折号。匹配的结尾是嵌套的前瞻,我们必须匹配123,后跟任何非非空白字符。

代码:

use strict;
use warnings;

while(<DATA>) {
    s/(?<!\S)(-?)(?=123(?!\S))/$1./g;
    print;
}

__DATA__
r 123 z123 "123" -1233 d123 123-123
123 -123 -123 123 123

输出:

r .123 z123 "123" -1233 d123 123-123
.123 -.123 -.123 .123 .123

【讨论】:

  • TLP,感谢您抽出宝贵时间详细回答问题!
  • @GolamKawsar 一个有趣的问题需要解决是它自己的奖励。
【解决方案5】:

或者只是这个?这不会打扰空格,并且适用于 perl 5.8。

echo '123 -123 -123  123' | perl -pe's/(-)?(123)/$1.$2/g'

【讨论】:

  • 匹配太多了。它还将匹配321123
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-13
  • 2017-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多