使用vim替换不匹配的字符串,这些字符串发生的次数可变

时间:2015-07-21 16:38:56

标签: regex vim replace

我希望使用vim从包含以下示例文本的文件中提取方括号和内部数字:

13_[4]_3_[4]_[1]_5_[1]_29_[3]_4_[2]_9_[1]_6_[2]_4
14_[4]_28_[3]_4_[2]_12_[1]_8_[2]_2
[1]_[4]_15_[1]_16_[3]_4_[2]_11_[1]_16_[2]_2
9_[4]_3_[4]_3_[4]_9_[4]_4_[4]_7_[1]_12_[3]_4_[2]_9_[1]_[2]_2
14_[4]_30_[3]_4_[2]_5_[1]_19_[1]_3_[1]_8_[2]_10_[1]_4_[1]_3_[1]_2

因此,对于第一个示例行,我想要一个输出行,如下所示:     [4] [4] [1] [1] [3] [2] [1] [2]。

我可以使用以下方法轻松删除方括号:

:%s/\[\d\]//g

但我在尝试删除所有与[/ d]不匹配的文本时遇到了麻烦。大多数使用否定的vim命令(例如:v)似乎只在整行而不是单个字符串上运行,并且使用%s与组匹配:

:%s/\v(.*)([\d])(.*)/\2

还匹配并删除方括号。

有人有建议解决我的问题吗?

2 个答案:

答案 0 :(得分:4)

你很亲密。你需要引用方括号并使用比.*更少贪心的东西。

:%s/\v[^[]*(\[\d\])[^[]*/\1/g

概述

匹配前导文字+ [ +数字+ ] +尾随文字。捕获[ +数字+ ]。替换匹配捕获组。只留下括号和数字。

细节的荣耀

  • 使用\v非常神奇。请参阅:h magic
  • [...]是一个括号内的字符类,它匹配里面的任何字符。例如fooba[rs]匹配foobarfoobas,但不匹配foobaz。见:h /\[。 (注意Vim可能会将此称为集合。)
  • [^...]是一个否定括号的字符类,因此不会在括号内匹配任何字符。例如fooba[^rz]匹配foobas,但不匹配foobazfoobar
  • [^[] - 匹配任何非[字符。 (这看起来很有趣)
  • [^[]* - 匹配为非[字符零次或多次。这将匹配我们要删除的主要文字。
  • (...) - 捕获组
  • \[& \]代表文字[ / ]。我们必须逃避以防止角色类。
  • \d匹配1位数。
  • [^[]* - 匹配要删除的尾随文本
  • \1替换将是我们的捕获组,也就是括号内的数字。
  • 使用g标志可以全局或更明显地多次执行此操作。
  • 使用%范围对整个文件:s进行替换1,$

那么为什么:%s/\v(.*)([\d])(.*)/\2会失败?

tl; dr:你的模式并不匹配。试试/[\d]

长版:

  • 第一个.*将捕获太多而只留下最后一部分。例如[2]...
  • [\d]创建一个括号中的字符类,其中包含以下字符之一:d\
  • 第二个.*在使用g标志时遇到与第一个问题相同的问题。
  • 为什么不是3个捕获组?你当然可以有更多的捕获组,但在这种情况下它们是不必要的,所以删除它们。
  • 缺少g标志。这意味着该命令每行只会进行1次替换,这将留下大量文本。

一般正则表达式和替换建议

使用棘手的正则表达式模式时,通常最好先使用搜索/,而不是替换。这允许您事先查看匹配的位置。您可以通过/并按<up><c-p>来调整搜索。或者甚至更好地使用q/打开command-line-window,以便您编辑模式,例如编辑任何文本。您还可以在命令行中使用<c-f>(包括/)来调出command-line-window

一旦你有你的模式,那么你想开始替换。 Vim提供了使用空模式使用当前搜索的快捷方式。例如:%s//\1/g

此技巧特别与set incsearchset hlsearch结合使用,意味着您可以在进行替换之前以交互方式查看匹配项。此技巧显示在以下Vimcast剧集中:Refining search patterns with the command-line window

需要了解更多正则表达式语法?见:h pattern。这是一个非常长而密集的阅读,但将来会极大地帮助你。我还发现通过perldoc perlre阅读Perl的正则表达式文档也是一个好看的地方。注意:Perl的正则表达式与Vim的正则表达式不同(参见:h perl-patterns),但Perl兼容的正则表达式(PCRE)非常常见。

思想

您也可以考虑grep -o。例如%!grep -o '\[\d\]'

更多帮助

:h :s
:h range
:h magic
:h /\[
:h /\(
:h s/\1
:h /\d
:h :s_flags
:h 'hlsearch'
:h 'incsearch'
:h q/
:h command-line-window
:h :range!

答案 1 :(得分:1)

另一种方法:

:%s/\v[^[]*(%(\[\d\])?)/\1/g