【问题标题】:Vim :s replace specific N < g occurrences on a lineVim :s 替换一行中出现的特定 N < g
【发布时间】:2021-12-16 13:17:57
【问题描述】:

我尝试使用逗号 (,) 来分隔前两个数字字段中的千位,使用 vim command

%s/\([0-9]\)\([0-9]\)\([0-9]\)\([0-9]\);/\1,\2\3\4;/g

但在这种情况下,它还会在第二行的 9,995 中添加逗号,我可以用什么来替换特定的 N

输入

BitstreamCyberCJK;Freeware;30275;28686;v2.0 ;beta (1998-03-17)
Y.OzFontN;Freeware;21957;7621;v13.00 sfnt rev 9995; Pen-Ji (2010-08-24)

预期输出

 BitstreamCyberCJK;Freeware;30,275;28,686;v2.0 ;beta (1998-03-17)
 Y.OzFontN;Freeware;21,957;7,621;v13.00 sfnt rev 9995; Pen-Ji (2010-08-24)

【问题讨论】:

  • 您的 %s 命令给出的结果与您“预期的”完全相同,9995 没有被触及,因为它不是以 ; 结尾,但 7621 已按照您的预期进行了修改。
  • 我编辑了这个问题,但无论如何我该怎么做?任何想法

标签: linux vim vi


【解决方案1】:

还有一种方法可以重复上一条命令:@:,也可以指定重复次数,例如:10@:。

所以,从只替换第一个匹配开始:%s/\([0-9]\)\([0-9]\)\([0-9]\)\([0-9]\);/\1,\2\3\4;/

然后,由于我们已经完成了 N 次替换,重复 N-1 次。

例如,要替换前 10 个数字,请使用:

%s/\([0-9]\)\([0-9]\)\([0-9]\)\([0-9]\);/\1,\2\3\4;/
9@:

【讨论】:

    【解决方案2】:

    我会使用更短且更易于管理的搜索模式:

    :%s/\(\d\)\(\d\{3}\);/\1,\2;/g
    

    然后,我将删除 /g 标志以仅替换每行上的第一个匹配项,因为目标明确 不 替换所有行上的所有匹配项:

    :%s/\(\d\)\(\d\{3}\);/\1,\2;
    

    然后,我会在每一行重复最后一次替换:

    g&
    

    请参阅:help \d、:help \{、:help :s_flags 和 :help g&amp;。

    【讨论】:

      【解决方案3】:

      首先我会建议你清理你的正则表达式:

      :%s/\([0-9]\)\([0-9]\{3}\);/\1,\2;/g
      

      其次,最后的 g 标志使 vim 替换所有出现。没有标志,它只会替换第一个。由于% 在每一行上运行替换,%s//g 将替换每一行的第一个匹配项。

      由于您想替换前两次出现的情况,您只需运行两次即可。然而,我们想要在这里有点优雅。有:&amp; 命令可以重复最后一次替换。我们甚至可以将它们链接在一起:

      :%s/\([0-9]\)\([0-9]\{3}\);/\1,\2;/ | norm %&
      

      更新: 其他解决方案也可以这样使用:

      :%s/\([0-9]\)\([0-9]\{3}\);/\1,\2;/ | norm g&
      

      或

      :%s/\([0-9]\)\([0-9]\{3}\);/\1,\2;/ | norm :@
      

      【讨论】:

      • 一个|就够了。
      • @romainl 谢谢你,这是一个错字,给了该行一个错误的含义!无论如何,我刚刚看到你的答案,对不起,我已经在写我的了,它们非常相似。我喜欢您使用的g&amp; 运算符,因为有标志,它似乎更干净。但是,如果链接在一起,它确实会打开一个快速修复窗口。你知道为什么吗?这样我仍然会选择上下文敏感的%&amp;
      • 嗯,这三个答案非常相似,并且展示了 Vim 的丰富性和表现力。至于链接g&amp;,它是一个普通模式的命令,所以它不能在没有:help :normal 的情况下直接在命令行上使用。如果您想要链接,:%s 绝对是您的最佳选择。另请注意,第一个答案以我们没有的方式解决了问题的N 部分。
      • 我现在有点困惑。 %&amp; 如果示例中的行数超过 2 行(只是复制它们),也会打开 quickfix。所以我也必须使用norm。 | @: 和 | %&amp; 似乎没有在 2 行上打开 quickfix。但他们在更多的线路上......这可以用norm 来抑制。所以我想我的答案在这里是最糟糕的,因为它不会接管像 | norm g&amp; 这样的标志,并且对于像 | @: 这样的更多重复也不是通用的。
      【解决方案4】:

      上面已经有了很好的答案,但我将分享我将如何处理它。 (因为我不想数“N”有多少个,也不想重复替换却发现我做了一个太多等等)

      我会使用上面列出的替换之一,例如:%s/\(\d\)\(\d\{3}\);/\1,\2;/g。然后我会运行第二次替换,搜索“rev”并删除逗号(如果存在):

      %s/\(rev \d\),\(\d\{3\};\)/\1\2/g
      %                               g  all lines, and all occurences on a line
       s/                       /    /   substitute
         \(      \) \(        \)         backref capture groups
           rev \d                        text "rev " and a digit (backref 1)
                   ,                     comma
                      \d\{3\};           three digits and a semicolon (backref 2)
                                 \1\2    backrefs 1 and 2
      

      注意事项 --

      • 如果您知道“rev”在一行中只出现一次,那么您可以在替换结束时省略 g。
      • 如果“rev”以某种方式出现在其他 文件中的上下文——可能是像“Foo bar abbrev 4,321”这样的文本—— 那么您需要考虑这一点。
      • 如果“rev”后跟一个数字和一个逗号没有匹配,那么 Vim 将给出“找不到模式”消息。

      此外,这会搜索一个带逗号的四位数字。如果有可能需要删除逗号的五位或六位数字,那么您将使用它,这几乎是相同的:

      %s/\(rev \d\{1,3\}\),\(\d\{3\}\);/\1\2/g
               \d\{1,3\}  ,                     1 to 3 digits followed by a comma
      

      【讨论】:

        猜你喜欢
        • 2017-03-15
        • 2011-03-05
        • 2021-09-29
        • 2014-11-08
        • 1970-01-01
        • 1970-01-01
        • 2021-08-31
        • 2023-03-10
        • 1970-01-01
        相关资源
        最近更新 更多