【问题标题】:Regex - nested patterns - within outer pattern but exclude inner pattern正则表达式 - 嵌套模式 - 在外部模式内但排除内部模式
【发布时间】:2011-09-10 09:47:38
【问题描述】:

我有一个文件,内容如下。

<td> ${ dontReplaceMe } ReplaceMe ${dontReplaceMeEither} </td>

如果 'ReplaceMe' 在 td 标记中,我想匹配它,但如果它在 ${ ... } 表达式中,则不匹配。

我可以用正则表达式做到这一点吗?

目前有:

sed '/\${.*?ReplaceMe.*?}/!s/ReplaceMe/REPLACED/g' data.txt

【问题讨论】:

  • 任何特定的语言?
  • 我更新了标签。不过要回答你的问题:bash scripting
  • Steve - 你应该更新你原来的问题,而不是再次问同样的问题(即使它的措辞略有不同)。谢谢。
  • 史蒂夫,我用适用于四种最琐碎情况的模式更新了我的答案。不过,我强烈建议您不要使用它!

标签: regex bash sed grep pattern-matching


【解决方案1】:

好吧,对于这种简单的情况,您只需要验证 行不匹配 ${.*}:

$ sed '/\${.*}/!s/ReplaceMe/REPLACED/' input
<td> REPLACED </td>
<td> ${ don't ReplaceMe } </td>

/\${.*}/ sed 地址后的! 否定条件。

OTOH,如果情况不是那么简单,我怀疑你的问题会越来越多,而正则表达式不会是最好的解决方案。

【讨论】:

  • 不匹配是否有 grep 等效项?我想使用 grep 生成一个文件列表,供 sed 循环使用。
  • @steve,这就是 sed 的作用,您可以将多个文件传递给 sed。也许 sed 's/MATCHTEXT/REPLACEDTEXT/g' *.html 将遍历所有 .html 文件。这不合适吗?
  • @Steve 是的,就是这样,它是 grep 的 -v 选项。你可以这样使用它:ls | grep -v 'PATTERN' 只列出不匹配的文件。如果你想对他们应用 sed,可以使用 sed -i.bkp 's/foo/bar/g' $(grep -v 'PATTERN')
  • @matchew 我不确定,但我认为他只想将 sed 应用于某些名称与某些模式不匹配的文件。好吧,如果我们的 cmets 没有帮助他,我建议 @Steve 再问一个问题 :)
  • 我在 Q:6272754 提出了另一个问题。我正在处理brandizzi的sed命令,他可能是对的,我的问题要复杂得多。 @matchew,谢谢。最终我需要使我的 sed 递归(在 folder1/folder2/data.txt 中的 sed 文件)。我希望种子可以做到这一点。
【解决方案2】:

&lt;td&gt;.*(?&lt;!${).*ReplaceMe(?!.*}).*&lt;/td&gt; 之类的东西应该可以工作,如果 grep 支持负面的lookbehinds(我不记得是否支持)。

【讨论】:

    【解决方案3】:
    sed -i 's/<td>\sReplaceMe\s<\/td>/<td>Replaced<\/td>/gi' input.file
    

    为我工作。

    您可以考虑使用 -i.bak 来备份旧文件,以防出错。

    或者,

    perl -pi -e 's/&lt;td&gt;\sReplaceMe\s&lt;\/td&gt;/&lt;td&gt;Replaced&lt;\/td&gt;/g' temp

    同样有效,请注意要备份的 -pi.bak。

    【讨论】:

      【解决方案4】:

      通常在涉及结构化标记时使用正则表达式是个坏主意。在某些特殊情况下可能没问题,但是有更好的工具来解析 html,然后你可以在文本节点上使用正则表达式。

      【讨论】:

      • 您能否指定一些可能有助于 HTML 中嵌入表达式的工具?我找不到它们,也不知道它们叫什么。
      • 我建议任何可以解析 dom 树的工具。例如用于 perl 的 XML::DOM::Parser 和用于 python 的 xml.dom.minidom.parse。然后将一个更简单的正则表达式应用于您想要的 dom 元素内的文本节点,忘记 s.
      【解决方案5】:

      这是不可能的。

      正则表达式可用于 Type-3 Chomsky 语言(常规语言)。
      但是,您的示例代码是 Type-2 Chomsky 语言(上下文无关语言)。

      只要涉及到任何类型的嵌套(括号),您就可以处理上下文无关的语言,正则表达式没有涵盖这些语言。

      基本上没有办法在正则表达式中定义within a pair of x and y,因为这将要求正则表达式有某种堆栈,它没有(在功能上等同于有限状态自动机)。


      Brandizzi 的挑战是找到一个至少可以匹配琐碎案例的正则表达式
      我实际上想出了这个(痛苦的hacky)正则表达式模式:

      perl -pe 's/(?<=<td>)((?:(?:\{.*?\})*[^{]*?)*)(ReplaceMe)(.*)(?=<\/td>)/$1REPLACED$3/g'
      

      对于这些情况正确 (原文如此!) 匹配

      <td> ${ dontReplaceMe } ReplaceMe ${dontReplaceMeEither} </td>
      <td> ReplaceMe ${dontReplaceMeEither} </td>
      <td> ${ dontReplaceMe } ReplaceMe </td>
      <td> ReplaceMe </td>
      

      这个失败了 (嵌套是 Chomsky Type-2,记得吗?;))

      <td>${ ${ dontReplaceMe } ReplaceMe ${dontReplaceMeEither} }</td>
      

      而且它也不能替换多个匹配项

      <td> ReplaceMe ReplaceMe </td>
      <td> ReplaceMe ${dontReplaceMeEither} ReplaceMe </td>
      

      获得领先的$ 覆盖是棘手的部分。
      这并防止Reginald/Reggy 在编写此野兽时不断崩溃。

      再次:实验性的,永远不要在生产代码中使用它!

      (……或者我会追捕你,我是否必须使用你的代码/应用程序;)

      【讨论】:

      • 你是对的 在你的回答中需要考虑一些要点。 1. 如果要标记的文本非常简单,那么将它与正则表达式匹配是很实用的,但我知道你知道 :) 2. 一些正则表达式是递归的并且可以匹配上下文无关的语法(例如 tinyurl.com/3jb2xqh ) . 3. regex虽然不匹配上下文无关语言,但sed可以匹配。实际上,我认为 sed 甚至是图灵完备的,因为它支持连接和循环。我不相信这会很实用,但看起来可能。无论如何,你的答案是正确且重要的。
      • 史蒂夫问“我可以用正则表达式来做这件事吗?”,答案仍然是“不”;)你对 sed 提出了一个有趣的观点。不知道这个,谢谢! :) 关于正则表达式能够(不安全地)在此处匹配简单案例:请参阅我的更新答案。 ;) 我现在感觉很脏。抱歉,诺姆 :(
      • 别担心,我不会在生产中使用任何看起来很老套的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-03
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2020-08-18
      • 2011-03-01
      相关资源
      最近更新 更多