Vim regex 将多个连续空格替换为只有一个空格

我经常处理文本文件,这些文本文件使用可变量的空格作为单词分隔符(像 Word 这样的文本处理器就是这样做的,它们公平地分配由于特定字体中字母大小不同而产生的空格数量,即使以纯文本形式保存,它们也会把这些恼人的可变量空格数量放在一起)。

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

我使用的是 vim,因此如果可行的话,使用 vim regex 方言的 regex 对我来说非常有用。

我目前的进展是这样的:

:%s/ \+/ /g

但它不能正常工作。

我还在考虑编写一个 vim 脚本,可以逐行解析文本行,逐字符处理每一行字符,并跳过第一行之后的空白,但我有一种感觉,这将是过分的。

84128 次浏览

这将取代2个或更多的空格

s/ \{2,}/ /g

或者你可以在 \+之前给你的版本增加一个额外的空间

s/  \+/ /g

出于实用主义的考虑,我倾向于把这个过程分为三个阶段:

: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

这个有用吗?

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

这样就行了:

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

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

由于在行首空格前面不可能有非空格字符,所以它不会与模式匹配,因此替换将不会替换它。很简单。

我喜欢这个版本-它类似于前瞻性版本的亚里士多德帕格尔齐斯,但我发现它更容易理解。(可能只是我对 Z 不熟悉)

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

或者所有的空格

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

我把它理解为“用某物和单个空格替换除空格之外的所有出现的事物,后面跟着多个空格”。

这里有很多好的答案(尤其是亚里士多德的: \zs\ze非常值得学习)。为了完整起见,您还可以使用一个负面的后视断言来完成这项工作:

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

这意味着“找到两个或更多的空格(' \{2,}') ,它们的前面没有‘行的开始后面跟着零个或更多的空格’”。如果你想减少反斜杠的数量,你也可以这样做:

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

但只能救你们两个人!你也可以用 ' +'代替 ' {2,}',如果你不介意它做一些冗余的修改(比如把一个空格改成一个空格)。

你也可以使用负向后看来检查一个单独的非空格字符:

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

这和亚里士多德的版本差不多(亚里士多德对空格和制表符进行了一些修改,以便节省一些输入) :

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

参见:

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

还有(全部读完!) :

:help pattern.txt

我回答了,不过我还是要把我的工作流程加进去。

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

快速和简单的记忆。有一个更加优雅的解决方案上面,但只是我的0.02。