【问题标题】:vim regex replace multiple consecutive spaces with only one spacevim regex 用一个空格替换多个连续的空格
【发布时间】:2011-04-21 02:32:01
【问题描述】:

我经常使用具有可变数量的空格作为单词分隔符的文本文件(像 Word 这样的文本处理器这样做是为了公平地分配由于某些字体中字母大小不同而导致的空格数量,并且他们将这个烦人的可变数量的即使保存为纯文本也有空格)。

我想用单个空格自动替换这些长度可变的空格序列的过程。我怀疑正则表达式可以做到这一点,但段落开头也有空格(通常是四个,但并非总是如此),我想让它们保持不变,所以基本上我的正则表达式也不应该触及前导空格,这增加了复杂性。

我正在使用 vim,所以如果可行的话,vim 正则表达式方言中的正则表达式对我非常有用。

我目前的进度是这样的:

:%s/ \+/ /g

但它不能正常工作。

我也在考虑编写一个 vim 脚本,它可以逐行解析文本行,逐个字符处理每一行,并在第一行之后跳过空格,但我觉得这有点矫枉过正。

【问题讨论】:

  • 适合重新格式化垂直对齐的代码:)

标签: regex vim


【解决方案1】:

这将替换 2 个或更多空格

s/ \{2,}/ /g

或者您可以在 \+ 之前为您的版本添加一个额外的空格

s/  \+/ /g

【讨论】:

  • 我认为这可能是最好和最简单的答案。它还具有使用其他 RegEx 方言的额外好处!
  • 这绝对是最好最简单的答案。
  • 同意 - 这是最好的答案。
  • 为什么要在{2,}之前加上\
  • “也不应触及前导空格”是此答案似乎无法满足的要求。
【解决方案2】:

这样就可以了:

%s![^ ]\zs  \+! !g

通过使用\zs\ze 元序列,在 Vim 中可以比其他正则表达式方言更容易地完成许多替换。他们所做的是从最终结果中排除部分匹配,无论是序列之前的部分(\zs,“s”表示“从这里开始”)或之后的部分(\ze,“e”表示“结束”这里”)。在这种情况下,模式必须首先匹配一个非空格字符 ([^ ]),但下面的 \zs 表示最终匹配结果(将被替换)从该字符开始 .

由于没有办法在行前空格前面有一个非空格字符,它不会被模式匹配,所以替换不会替换它。很简单。

【讨论】:

  • 我想提出这个替代方案:%s!\S\@<= \+! !g\@<= 是一只非常漂亮的鸭子,我喜欢使用它。另见:help /\@<=
  • 我只是更喜欢 zs 的简化手指杂技而不是输入 @<=... 就像我更喜欢 Vim 比 E(scape)M(eta )A(lt)C(控制)S(hift)。 :) OTOH,一个人的天赋感总是值得一些牺牲,所以请随意。
  • 像老板一样。谢谢。
【解决方案3】:

出于实用主义的考虑,我倾向于将其分为三个阶段:

:g/^    /s//XYZZYPARA/g
:g/ \+/s// /g
:g/^XYZZYPARA/s//    /g

我不怀疑可能有更好的方法(可能使用宏,甚至是纯正则表达式方法),但我通常会在我赶时间的时候发现这种方法有效。当然,如果您有以XYZZYPARA 开头的行,您可能需要调整字符串:-)

转身就够了:

    This is a new paragraph
spanning       two lines.
    And    so    is   this but on one line.

进入:

    This is a new paragraph
spanning two lines. 
    And so is this but on one line.

旁白:如果你想知道我为什么使用:g 而不是:s,那主要是习惯。 :g 可以做任何事情 :s 可以,还有更多。这实际上是一种在选定行上执行 任意 命令的方法。在这种情况下,要执行的命令恰好是s,所以没有真正的区别,但是,如果你想成为vi 高级用户,你应该在某个时候查看:g

【讨论】:

  • 是的,我内心的纯粹主义者/理想主义者很久以前就开始退居二线了。现在我只想完成工作,特别是如果替代方案是一个 600 个字符的正则表达式,具有回溯和前瞻功能,我不明白什么时候必须在三个月内回来调试它 :-)
  • 我在上面使用了一个变体: :g/ \+/s// /g 我理解空格和 \+ 来匹配一个或多个,不知道 /s/ 做什么,任何人知道吗?
  • @anteatersa,s 本身就是替代命令。如果您阅读了我的答案的最后一部分,它会简单地解释 g 行,然后对每行执行任意命令,其中s 是一种可能性。例如:g/^$/d 将在所有空行上运行d 命令(删除行)。您可以享受各种乐趣,例如 :g/^/m0 :-)
  • 太棒了!只是想知道是否可以将其转换为类似于 Vim 中的函数,而不是一次复制粘贴三个命令?
【解决方案4】:

这里有很多很好的答案(尤其是亚里士多德的:\zs\ze 非常值得学习)。为了完整起见,您也可以使用否定的后向断言来执行此操作:

:%s/\(^ *\)\@<! \{2,}/ /g

这表示“找到 2 个或多个空格 (' \{2,}'),它们前面没有‘行首后跟零个或多个空格’”。如果您希望减少反斜杠的数量,也可以这样做:

:%s/\v(^ *)@<! {2,}/ /g

但它只会为你节省两个字符!如果您不介意它进行大量冗余更改(即将单个空格更改为单个空格),您也可以使用 ' +' 而不是 ' {2,}'

您也可以使用否定的look-behind 来检查单个非空格字符:

:%s/\S\@<!\s\+/ /g

这与(亚里士多德的略微修改版本,将空格和制表符视为相同以节省一点打字)大致相同:

:%s/\S\zs \+/ /g

见:

:help \zs
:help \ze
:help \@<!
:help zero-width
:help \v

并且(全部阅读!):

:help pattern.txt

【讨论】:

    【解决方案5】:

    已回答;但无论如何我都会放弃我的工作流程。

    %s/  / /g
    @:@:@:@:@:@:@:@:@:@:@:@:(repeat till clean)
    

    快速且简单易记。上面有一个更优雅的解决方案;但只是我的 .02。

    【讨论】:

    • 这不是一个好的解决方案:首先它会删除前导空格,问题的作者希望避免这种情况。其次,您可以执行 100@: 运行 100 次寄存器内容:(这是最后一个 ex 命令)
    • 因此我在回复中说这不是最佳答案:)
    • 我仍然觉得这个答案很有用,尽管它不能很好地回答 OP 的问题。
    • 谢谢。您的解决方案很容易记住。 removing unwanted white space 上的 Vim wiki 页面解释了如何组合 \s\+ 来查找许多空白和制表符。用于将所选内容中的所有空格替换为 1 的示例::'&lt;,'&gt;s/\s\+/ /g。现在结合亚里士多德在行首保持缩进的方法::'&lt;,'&gt;s/[^\s]\zs\s\+/ /g.
    【解决方案6】:

    这行得通吗?

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

    【讨论】:

    • 在这种情况下最好使用%s/[^ ]\zs \+/ /g (:help /\zs)
    • 啊!好的。我同意好多了。谢谢。
    【解决方案7】:

    我喜欢这个版本 - 它类似于 Aristotle Pagaltzis 的前瞻版本,但我发现它更容易理解。 (可能只是我对 \zs 的不熟悉)

    s/\([^ ]\) \+/\1 /g
    

    或所有空格

    s/\(\S\)\s\+/\1 /g
    

    我将其解读为“用某物和一个空格替换所有出现的东西,而不是空格后跟多个空格”。

    【讨论】:

    • 当然,这个版本在打字和动态制定方面要复杂一个数量级——这几乎是一个微不足道的模式。熟悉\zs\ze 会很有帮助,它们可以为更复杂模式的可写性和可读性创造奇迹(特别是当你有理由同时使用这两种模式时!)。
    • 我肯定会看看\zs\ze,但我也经常在python 和sed 中使用我的正则表达式。因此,拥有一个可以跨多个应用程序工作的解决方案可能会很好。
    猜你喜欢
    • 1970-01-01
    • 2011-10-22
    • 2015-03-27
    • 2010-11-19
    • 1970-01-01
    • 2014-05-31
    • 2011-05-13
    • 2013-05-30
    • 2013-07-03
    相关资源
    最近更新 更多