【问题标题】:mark list of items in string without overlap标记字符串中的项目列表而不重叠
【发布时间】:2015-08-10 13:15:16
【问题描述】:

我有一个文本示例:

my $text = 'a bb cc xx aa a b c a';

以及可能在文本中出现的术语列表:

my @words = ('bb cc',
    'a bb cc',
    'xx aa a b',
    'a b',
    'a'
);

我需要找到这些单词的出现次数,尽可能使用最长的匹配项,并且不要将任何内容标记两次。因此,如果我在上面的文本中标记了匹配项,它将如下所示:

<a bb cc> <xx aa a b> c <a>

请注意,我没有标记 bb cc,因为这是更大匹配 a bb cc 的一部分。

关于如何做到这一点的任何想法?感觉应该已经遇到过很多次了。

【问题讨论】:

  • 您可以将@words 与$text 进行比较,然后过滤掉剩余的重叠@words 元素。
  • 你想找到一个好的算法来找到一个解决方案,还是一个只适用于正则表达式引擎的解决方案?另外,你如何计算最长的匹配?考虑您必须在acab 中找到字符串abaccab:使用&lt;ac&gt;&lt;ab&gt;,您会找到匹配总和最长的解决方案,而使用a&lt;cab&gt; 您会找到了一个最先应用最长匹配的。
  • @PatrickJ.S.最长的匹配是a。我想领带可能会按字母顺序断开。我想我想要最简单的解决方案,但欢迎所有解决方案。

标签: perl text-processing


【解决方案1】:

应该做一个简单的替换,你必须按长度排序:

my $re = '('.join('|', sort {length $b <=> length $a} map(quotemeta,@words)).')';
$text =~ s/$re/<$1>/g;
say $text;

按预期输出 5.20.2,目前无法检查其他版本。 您提供的示例实际上并不需要 quotemeta 部分,它用于转义正则表达式中具有特殊含义的字符。

【讨论】:

  • 我想我想多了。您的示例几乎可以正常工作; @words 需要按长度排序。显然引擎并不像我们想象的那么贪婪!如果@words 包含('a', 'a b') 并且文本是a b,那么匹配将是&lt;a&gt; b。将@words 变为('a b', 'a') 使其正确匹配为&lt;a b&gt;
  • 我使用了错误的测试来检查引擎是否贪心。当然,它检查左优先。我在回答中更正了它。
  • 顺便说一句,我认为我自己使用正则表达式的陷阱是在做while(s///g){},它以嵌套标签结束。添加 c 标志可以解决此问题。
  • @NateGlenn 您可以摆脱 while,它会应用循环,直到找不到替换。 s///g 运算符返回替换的数量。如果你添加一个c 标志,循环只运行一次——你也可以在那里放一个if
  • 啊,是的,但我正在 while 循环内对匹配项进行其他处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-25
  • 1970-01-01
  • 2022-10-17
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
相关资源
最近更新 更多