如何在Vim中将一个字符替换为换行符

2382

我正在尝试将当前文件中的每个,替换为一个新行:

:%s/,/\n/g 

但它插入的看起来像是^@而不是实际的换行符。该文件并非处于DOS模式或其他模式。

我该怎么办?

如果你像我一样好奇,也可以查看这个问题:为什么\r在Vim中是一个新行符?


2
Stack Overflow 是一个面向编程和开发问题的网站。这个问题似乎不属于编程或开发范畴。请参考帮助中心中的可以在这里提问的主题有哪些?。也许超级用户Unix和Linux堆栈交换会更适合提问。 - jww
8
这个问题已经有10年了,好像太旧了无法迁移。类似这样的问题有很多,例如:https://dev59.com/4Gkw5IYBdhLWcg3wDWIk - Vinko Vrsalovic
45
@jww Vim是程序员常用的工具,与程序员常用的工具有关的问题可以在[SO]上讨论。尽管如此,这个问题更适合在[vim.se]上讨论。 - user202729
12个回答

3076

使用\r代替\n

\n代替会在文本中插入一个空字符。要获得换行符,请使用\r。但是,在搜索换行符时,仍需使用\n。这种不对称是由于\n\r 执行的操作略有不同

\n匹配行尾(换行符),而\r匹配回车符。另一方面,在替换中,\n插入一个空字符,而\r插入一个换行符(更精确地说,它被视为输入的CR)。以下是一个小型、非交互式的示例,以说明这一点,使用Vim命令行功能(换句话说,您可以将以下内容复制并粘贴到终端中运行)。xxd显示生成文件的十六进制转储。

echo bar > test
(echo 'Before:'; xxd test) > output.txt
vim test '+s/b/\n/' '+s/a/\r/' +wq
(echo 'After:'; xxd test) >> output.txt
more output.txt
Before:
0000000: 6261 720a                                bar.
After:
0000000: 000a 720a                                ..r.

换句话说,\n 插入了字节0x00到文本中;\r 插入了字节0x0a。


148
/r 被视为按下回车键。它在所有平台上都有效。 - Luka Marinko
86
请参阅 为什么在Vim中\r代表换行符? - Andrew Marshall
18
我希望这个能用于经典的vi编辑器。在AIX v6.1上,\r不能像这样使用。但是你可以在输入\r的位置按Ctrl-VEnter键,这样就可以实现相同的效果。 - eksortso
6
我迟到了。如果\r插入<CR>\n插入空字符,如何用回车替换某些内容? - Mr. Llama
7
@SunnyRaj,你对CR和LF的历史确定吗?我印象中最初LF只是将纸张向前移动一行,但未移动打印头,而CR移动了打印头但未移动纸张。因此,如果您的操作系统在打印之前未转换输入,则不能仅使用LF或CR来获取正确的输出。 MS DOS使用原始打印机数据作为文本文件格式,Mac OS使用CR并从中转换为打印机的原始格式,UNIX使用LF并从中转换为打印机的原始格式。 - Mikko Rantalainen
显示剩余15条评论

237

这里是技巧:

首先,设置您的Vi(m)会话以允许使用特殊字符(例如:换行符)进行模式匹配。最好将此行放在您的 .vimrc 或 .exrc 文件中:

:set magic

接下来,执行:

:s/,/,^M/g

要得到字符^M,请键入Ctrl + V并按Enter。在Windows下,请执行Ctrl + QEnter。我唯一能记住这些操作的方法就是记住它们似乎没有什么意义:

A:用哪个控制字符来表示换行会最糟糕?

B:要么是q(因为它通常表示“退出”),要么是v,因为打错键很容易导致意外终止编辑器。

A:就这样吧。


10
我正在Windows上使用GVim,我不需要:set magic(在我的~/_vimrc中也没有)或者Ctrl-Q。只需要简单的按下ctrl-v,然后回车键就可以为我创建^M字符。 - Chris Phillips
6
C-v并不代表换行符,它是“转义下一个字面字符”的命令。我也不知道C-v是什么缩写,但它不会像换行符一样在大脑中映射。 - Jim Stewart
30
Ctrl-v是“verbatim”的助记符,即将下一个按键转义为其“verbatim”键码/字符。在Windows中,它是粘贴:为了保持熟悉感。Ctrl-Q可能是用于“(反)引用”。无论如何都很愚蠢,但你可以在二进制文件中使用它,例如搜索Ctrl-A到Ctrl-Z(Ascii 1-26,我猜)。 - Tomasz Gandor
1
Ctrl-C并不能真正地杀死编辑器,尽管它可以将您取消回到正常模式。 Ctrl-V表示逐字,而Ctrl-Q表示有人错误地加载了$VIMRUNTIME/mswin.vim配置文件。 您不需要mswin。 只需使用自己的vimrc即可。 - 00dani
哇——这太棒了。我之前一直用 sed -n l,现在知道在 vim 中可以用 Ctrl-v 实现相同的功能。 - arpit
这对我不起作用。当我按Ctrl+V时,它会粘贴剪贴板的内容。当使用“:s/ /\r/g”时,我会插入一个“r”字符。对于\n而不是\r,我得到一个“n”字符。 - Marlin Pierce

123
在语法 / foo / bar 中, \r \n 根据上下文具有不同的含义。

简短版:

对于foo

\r ==“回车符”(CR / ^M
\n == 在Linux / Mac上匹配“换行符”(LF),在Windows上匹配CRLF

对于bar

\r == 在Linux / Mac上产生LF,在Windows上产生CRLF
\n ==“空字节”(NUL / ^@

在Linux(即Web服务器)中编辑最初在Windows环境中创建并上传(即FTP / SFTP)的文件时 - vim中看到的所有^M都是CR,Linux不进行转换,因为它仅使用LF表示换行。

更详细版(带ASCII数字):

NUL == 0x00 == 0 == Ctrl + @ == vim中显示的^@
LF == 0x0A == 10 == Ctrl + J
CR == 0x0D == 13 == Ctrl + M == vim中显示的^M

这是 ASCII控制字符列表。通过 Ctrl + V Ctrl + --- key --- 在Vim中插入它们。

在Bash或其他Unix / Linux shell中,只需键入 Ctrl + --- key ---

在Bash中尝试 Ctrl + M 。与Linux系统使用换行符进行行定界不同,它与按 Enter 相同,因为shell意识到其意义。

要在Bash中插入文字,可以在其前面添加 Ctrl + V

在Bash中尝试:

echo ^[[33;1mcolored.^[[0mnot colored.

这里使用了ANSI转义序列。通过Ctrl + V, Esc键插入两个^[

您也可以尝试使用Ctrl + VCtrl + MEnter,它会给您呈现这样的结果:

bash: $'\r': command not found

记得前面提到的 \r 吗? :>

这个ASCII控制字符列表不同于一个完整的ASCII符号表,因为控制字符是通过Ctrl键(哈哈)插入到控制台/伪终端/Vim中的,所以它们可以在那里找到。

而在C和大多数其他语言中,通常使用八进制代码来表示这些'字符'。

如果你真的想知道所有这些的来历: TTY解密。这是你会遇到的关于这个话题最好的链接,但要小心:龙在其中。


太长;没看

通常foo=\nbar=\r


1
所以我很好奇,您如何用回车替换字符。 - codeshot
2
@codeshot :s/x/^M/g 应该可以解决问题。通过 ctrl-v 然后跟着 ctrl-m 插入 ^M - sjas
3
谢谢您,sjas。您知道这个问题是有史以来最奇怪的问题之一。1008个人投票支持的答案基本上只说了一些废话,就是“vim做了你发现的事情。这是因为vim做了你发现的事情。永远不要忘记vim做了你发现的事情。”我原本希望能够找到一个有趣字符的简短列表,包括模式、替换和奇怪现象的原因,这样很容易记住并预测其他类似的奇怪现象。那样的回答将赢得我的投票。 - codeshot
@codeshot 一个ASCII控制字符列表可能会对你有所帮助。请参考http://www.cs.tut.fi/~jkorpela/chars/c0.html获取更多信息。我会更新我的答案,包括两个链接。 - sjas

63

您需要使用:

:%s/,/^M/g

想要得到^M这个字符,需按下Ctrl + v再按下Enter键。


6
我需要执行<C-v><C-m>来获取^M字符。 - Jezen Thomas
这对我不起作用。当我按Ctrl+V时,它会粘贴剪贴板的内容。 - Marlin Pierce

46

\r 可以在这里为您完成工作。


25
在Windows上使用Vim时,将Ctrl + V 替换为 Ctrl + Q

20

这是我认为最好的答案,但如果用表格会更好:

为什么在Vim中\r代表换行符?

重新表述一下:

在正则表达式替换中需要使用\r来表示换行(ASCII 0x0A,即Unix换行符),但这只适用于替换操作 - 你通常应该继续使用\n来表示换行和\r表示回车。

这是因为Vim在替换操作中使用\n表示NIL字符(ASCII 0x00)。你可能认为NIL应该是\0,以便将\n用于其通常的作用——表示换行符,但\0在正则表达式替换中已经有了意义,所以它被移位到了\n。因此,进一步将换行符从\n移位到\r(在正则表达式模式中,\r是回车符,ASCII代码为0x0D)。

字符                      | ASCII码   | C表示方式 | 正则表达式匹配 | 正则表达式替换
-------------------------+----------+-----------+---------------+------------------------
NIL                      | 0x00     | \0       | \0            | \n
换行符(Unix换行符)    | 0x0a     | \n       | \n            | \r
回车符                  | 0x0d     | \r       | \r            | <未知>

注意: ^M(在Linux中使用Ctrl + VCtrl + M)在正则表达式替换中插入的是一个换行符而不是回车符,这与其他人的建议不同(我刚试过)。

需要注意的是,当Vim根据其文件格式设置保存文件时,会翻译换行符号,这可能会使事情变得混乱。


11

Eclipse 中,^M 字符可以嵌入到一行中,如果你想将它们转换为换行符。

:s/\r/\r/g

9
但如果必须替代,那么以下方法可以使用:
:%s/\n/\r\|\-\r/g

在上面的例子中,每一行都会被替换成下一行,然后是|-和一个新行。这在维基表格中经常被使用。
如果文本如下:
line1
line2
line3

它已发生改变

line1
|-
line2
|-
line3

5

如果你需要对整个文件进行操作,有人建议你可以尝试从命令行开始:

sed 's/\\n/\n/g' file > newfile

1
请注意,这需要GNU sed。尝试 printf 'foo\\nbar\n' | sed 's/\\n/\n/g' 查看它是否适用于您的系统。(感谢freenode上#bash的好心人提供此建议。) - Evan Donovan
是的,但问题是关于 Vim 的。有一个 Stack Overflow 的问题 *如何使用 sed 替换换行符 (\n)?*。 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接