vim regex用一个空格替换多个连续的空格


69

我经常处理文本文件,这些文本文件中的单词分隔符具有可变的空白量(像Word这样的文本处理器会这样做,由于某些字体中的字母大小不同,会公平地分配空白量,即使在另存为纯文本)。

我想自动化将具有可变长度的空白序列替换为单个空格的过程。我怀疑正则表达式可以做到这一点,但是在段落的开头也有空格(通常是四个空格,但并非总是如此),我想让其保持不变,因此,基本上我的正则表达式也不应碰到开头的空格,并且增加了复杂性。

我正在使用vim,因此如果可行,vim regex方言中的regex对我将非常有用。

我当前的进度看起来像这样:

:%s/ \+/ /g

但它不能正常工作。

我也在考虑编写一个vim脚本,该脚本可以逐行解析文本行,逐字符处理每行字符,并在第一个字符之后跳过空格,但是我觉得这可能会过大。


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

Answers:


43

为了实用主义,我倾向于将其分为三个阶段进行:

: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


2
是的,我的纯粹主义者/理想主义者很久以前就开始坐后座。现在,我只是想完成工作,尤其是如果替代方法是具有600个字符的正则表达式并具有回溯和超前功能的话,我不明白何时需要在三个月内再次进行调试:-)
paxdiablo 2010年

我在上面使用了一个变体::g / \ + / s // / g我理解空格和\ +可以匹配一个或多个,不知道/ s /是什么,有人知道吗?
anteatersa 2012年

1
@anteatersa,s是替代命令本身。如果您阅读了我的答案的最后一部分,它说明了g简单地选择行,然后对它们中的每一个执行任意命令的s可能性,这是一种可能。例如,:g/^$/dd在所有空行上运行命令(删除行)。您可以享受各种乐趣,例如:g/^/m0:-)
paxdiablo 2012年

太棒了!只是想知道是否可以将其转换为Vim中的函数,而不是一次复制粘贴三个命令?
Rushi Agrawal

117

这将替换2个或更多的空格

s/ \{2,}/ /g

或者您可以在\+版本之前添加额外的空间

s/  \+/ /g

9
我认为这可能是最好和最简单的答案。它还具有在其他RegEx方言中工作的额外好处!
TrinitronX 2012年

1
这绝对是最好,最简单的答案。
RubyFanatic

同意-这是最佳答案。
约翰·迈耶

为什么我们需要在\之前添加{2,}
BiBi

2
“也不应触摸前导空格”是此答案似乎无法满足的要求。
paxdiablo

63

这将达到目的:

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

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

由于无法在行首空格前使用非空格字符,因此该模式将不会与之匹配,因此替换将不会替换它。简单。


1
我想提出此替代方案:%s!\S\@<= \+! !g。它\@<=是如此美丽,我喜欢使用它。另请参阅:help /\@<=
Benoit 2010年

1
我只是喜欢减少zs打字而不是打字的杂技@<=……以与E(scape)M(eta)A(lt)C(ontrol)S(hift)更好的方式享受Vim的方式几乎相同(如果程度较小)。:)太太,一个人的品味总是值得付出的,所以请放轻松。
亚里斯多德·帕加尔齐斯

如同一位老板。谢谢。
尼克·

7

这里有很多很好的答案(尤其是亚里斯多德的答案\zs\ze非常值得学习)。仅出于完整性考虑,您还可以使用否定的后向断言来做到这一点:

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

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

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

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

您还可以使用负向后看来检查单个非空格字符:

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

这与(Aristotle的略微修改版本,将空格和制表符视为相同,以节省一些键入内容)相同:

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

看到:

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

和(阅读全部!):

:help pattern.txt

3

已回答;但是尽管如此我还是会折腾我的工作流程。

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

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


2
这不是一个好的解决方案:首先,它将删除前导空格,该问题的作者希望避免。其次,您可以执行100 @:以运行100倍的register:内容(这是最后一个ex命令)
Benoit 2010年

1
因此,我说这不是我答复中的最佳答案:)
wom 2010年

2
即使它不能很好地回答OP的问题,我仍然认为此答案很有用。
Vladislavs Dovgalecs 2015年

谢谢。您的解决方案很容易记住。Vim Wiki页面上的删除不需要的空白说明了如何组合\s以及\+寻找许多空白和制表符。使用示例在选择中将所有空格替换为1 :'<,'>s/\s\+/ /g。现在,结合亚里士多德的方法在行的开头保持缩进::'<,'>s/[^\s]\zs\s\+/ /g
Paul Rougieux


2

我喜欢这个版本-与Aristotle Pagaltzis的前瞻性版本相似,但我觉得它更容易理解。(可能只是我对\ zs不熟悉)

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

或所有空白

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

我将其理解为“将某个空间以外的所有事物替换为一个事物,然后将多个空间替换为一个事物”。


当然,此版本的键入和即时制定要严格得多,而且几乎是一种琐碎的模式。您将很好地熟悉\zs\ze,他们会为更复杂的模式的可写性和可读性(特别是当您有理由同时使用两者时!)带来奇迹。
亚里斯多德·帕加尔齐斯

我肯定会看\zs\ze,但我也经常在python和sed中使用我的正则表达式。因此,拥有一个可以在多个应用程序中使用的解决方案会很好。
Michael Anderson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.