【问题标题】:RegEx, Substituting a variable number of replacementsRegEx,替换可变数量的替换
【发布时间】:2021-06-02 20:44:53
【问题描述】:

希望我遗漏了一些明显的东西。

我有一个文件,其中包含如下几行:

| A | B | C |
|-----------|
Ignore this line
| And | Ignore | This |
| D | E | F | G |
|---------------|

我想找到|----| 行,删除那些...并用^ 替换前一行中的所有| 字符。例如

^ A ^ B ^ C ^
Ignore this line
| And | Ignore | This |
^ D ^ E ^ F ^ G ^

到目前为止,我得到了:

perl -0pe 's/^(\|.*\|)\n\|-+\|/$1/mg'

这需要来自标准输入的输入(sed 已经发生了一些其他修改)...并且它使用 -0/m 来支持多行替换。

匹配似乎是正确的,它删除了|----| 行,但我看不出如何用$1(或\1)反向引用替换|^ .

我不记得我以前在哪里做的,但是另一种语言允许我使用${1/A/B} 将 A 替换为 B,但这让 perl 感到不安。

我一直想知道这是否可以使用 /e/ee,但我对 perl 不太熟悉如何做到这一点。

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    你可以使用

    perl -0pe 's{^(.*)\R\|-+\|$\R?}{$1 =~ s,\|,^,gr}gme' t
    

    详情:

    • ^(.*)\R\|-+\|$\R? - 匹配所有匹配项(参见末尾的 g 标志)
      • ^ - 行首(注意 m 标志使 ^ 匹配行首,$ 匹配行尾)
      • (.*) - 第 1 组:整行
      • \R - 换行序列
      • \| - | 字符
      • -+ - 一个或多个 - 字符
      • \| - 一个 | 字符
      • $ - 行尾
      • \R? - 一个可选的换行序列。

    一旦找到匹配项,所有| 都将使用$1 =~ s,\|,^,gr 替换为^,这将在Group 1 值内进行替换。此语法通过 e 标志启用。

    【讨论】:

    • 太棒了,谢谢 Wiktor,我明天必须完成它(这是我今天的最后一次努力,而不是现在在我的电脑上),但看起来它会起作用(从我有限的对 Perl 的理解)。
    • @CraigFrancis 我在 Ubuntu 18.04 中使用名为 t 的文件进行了测试。如果要修改t文件需要添加-i选项:perl -i -0pe 's{^(.*)\R\|-+\|$\R?}{$1 =~ s,\|,^,gr}gme' t
    • 谢谢你,它工作得很好,并给了我一个很好的例子来说明如何使用评估修饰符。
    【解决方案2】:

    我可以看到这是使用 2 个替换完成的:

    \|(?=.*[\r\n]+\|-+\|$)
    

    https://regex101.com/r/x7d15d/1/

    然后:

    ^\|-+\|(?:[\r\n]+|$)
    

    https://regex101.com/r/ZdEzuM/1/

    【讨论】:

    • 谢谢@MonkeyZeus,我明天必须检查一下(远离电脑),虽然我认为 Wiktor 的解决方案可能是我的偏好(因为我认为它可以一次性完成),我' 仍然会看看这是否也有效(我真的陷入了不确定的替换数量,我认为你已经解决了)。
    • @CraigFrancis 我毫不怀疑 Wiktor 的回答有效。当 Wiktor 回答时,我已经完成了一半,但我只是想展示我们普通的正则表达式可以实现的目标。有 2 个解决方案永远不会伤害 =)
    • @MonkeyZeus 请注意,我也是一个普通的正则表达式 :)
    • @WiktorStribiżew 如果我们在同一架飞机上,那么我会珍惜这句话作为恭维 =)
    • 就我自己的理解,为什么第一个开头是'(?=\|).' ...我只是想知道是否可以只是'\|',因为我不确定积极的前瞻断言用于什么(其余的很有意义,再次感谢)。
    【解决方案3】:

    使用一种模式检查前瞻断言中的下一行:

    perl -0pe 's/\|(?=.*\R\|-+\|$)(?:\R.*)?/^/gm' file
    

    如果你绝对想使用评估,你可以用这个模式在替换部分放一个音译:

    perl -0pe 's#^(.*)\R\|-+\|$#$1=~y/|/^/r#gme' file
    

    【讨论】:

    • 谢谢卡西米尔,你能为我解释一下吗...我得到了最初的 \| 匹配,(?=.*\R\|-+\|$) 向前看下一行...但是非捕获子模式(?:\R.*)?,可以选择匹配/包含下一行,我可以看到它如何与第一个匹配项一起工作;但如果发生这种情况,那不会删除'|----|'线?那么第二个“|”如何匹配工作与现在缺少的后续行? (这取决于我不知道 Perl RegEx 引擎是如何工作的)
    • @CraigFrancis:这就是诀窍!仅当到达该行的最后一个字符(最后一个 |)时,(?:\R.*)? 才会成功。在您的示例 | A | B | C | 中,(?:\R.*)? 使用前三个管道失败(由于 \R 换行序列别名),因为未到达行尾。只有在到达最后一个管道时才会删除下一行。
    • 哦,哇,是的,我明白了,所以第一组管道被替换为与管道匹配的管道,并且“肯定的前瞻断言”只是检查下一行,但实际上并没有替换它...而最后一个管道,虽然它的作用相同,但它也匹配紧随其后的行,因此它可以通过删除它来完成该过程...这很聪明。
    • @CraigFrancis:是的,你明白了。这种方法的代价是您必须检查每个管道的前瞻,但由于模式以文字字符(管道)开头,因此可以使用快速算法找到存在管道的位置,并且仅在这些位置测试模式位置(而不是字符串中的所有位置)。
    猜你喜欢
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-11
    • 1970-01-01
    相关资源
    最近更新 更多