如果我有以下文本:

foo
bar


我视觉上将其选中并复制。
文本现在存储在未命名的寄存器"中,这是其文本内容(:reg "的输出):

""   foo^Jbar^J


根据此图表,看来^J是换行符的插入符号。

如果要通过键入以下内容来复制a寄存器中的未命名寄存器::let @a = @"
这里是其内容(:reg a的输出):

"a   foo^Jbar^J


它没有变化。

如果现在我通过输入:let @/ = @"在搜索寄存器中复制它,则其内容(:reg /的输出):
"/   foo^@bar^@


根据上一张图表,^@似乎是空字符的插入符号。 a寄存器)?

如果我在命令行上(或在/之后的搜索内)插入未命名的寄存器,则键入:<C-R>",将插入以下内容:

:foo^Mbar^M


同样,根据最后一张图表,^M似乎是回车符的插入符号。
为什么换行符在命令行上自动转换为回车符?
/>
编辑:

通常,您可以通过键入以下内容来插入文字控制字符:<C-V><C-{character in caret notation}>
例如,您可以通过键入<C-R>来插入文字<C-V><C-R>。似乎是任何控制字符。
但是我注意到我无法在缓冲区内或命令行中插入文字LF,因为如果我键入:<C-V><C-J>它将插入^@(一个空字符)而不是^J
是否出于同样的原因将LF转换为搜索寄存器中的NUL?

编辑2:

:h key-notation中,我们可以阅读以下内容:

<Nul>       zero            CTRL-@    0 (stored as 10) <Nul>
<NL>        linefeed        CTRL-J   10 (used for <Nul>)


第一行的stored as 10部分和第二行的used for <Nul>部分可能表明LF和NUL之间存在某种重叠,并且可以将它们解释为同一件事。但是它们不可能是同一回事,因为在执行上一条命令:let @/ = @"之后,如果我以正常模式键入n来到达下一行出现的两行foobar,而不是得到一个正匹配项,则得到以下内容错误消息:

E486: Pattern not found: foo^@bar^@


除了此链接外,它还解释了NUL表示字符串的结尾,而LF表示文本文件中行的结尾。

如果NUL是stored as 10(如帮助信息所示),与LF的代码相同,那么Vim如何使两者之间有区别?

编辑3:

就像帮助说的那样,也许LF和NUL用相同的十进制代码10编码。由于上下文,Vim使这两者之间有所不同。如果它在缓冲区或任何寄存器中(除了搜索和命令寄存器之外)遇到十进制代码为10的字符,则将其解释为LF。
但在搜索寄存器(:reg /)中,它将其解释为NUL,因为在搜索的上下文中,Vim只搜索end of line in a file的概念没有意义的字符串,因为字符串不是文件(这很奇怪,因为您仍然可以在搜索模式中使用原子\n,但这也许是仅是正则表达式引擎的功能?)。因此它自动将10解释为NUL,因为它是最接近的概念(end of stringend of line)。

同样,在命令行/命令寄存器(:reg :)上,它将代码10解释为CR,因为end of line in a file的概念在这里没有意义。最接近的概念是end of command,因此Vim将10解释为CR,因为按Enter是结束/执行命令的方式,而CR与按Enter相同,因为在用<C-V><Enter>插入文字时,将显示^M。 br />
也许代码为10的字符的解释会根据上下文而改变:搜索字符串(^J
命令行末尾(^@


评论

有时,意外的NULL字符的出现是由处理字符串的基础C函数引起的。有关C如何处理链接到的字符串的解释说明了C在内部用NULL分隔字符串。 NULL在文本中很少出现,因此使其成为良好的字符。这样的结果是,如果C程序(vim)试图将“空”字符串传递到内部C函数中,则为

例如someFunction(arg1,“”),其中arg 2是“”,即“引号之间的项目,实际上什么都不是-”空“。可能会出现NULL,因为它由定界的底层C实现“添加”字符串。我不知道您将如何检查该字符串-但我想到它是可能的原因。
另请参见:substitute中有关\ r和\ n差异的讨论。

#1 楼

首先,感谢您的这篇非常全面和周到的文章。

经过一些测试,我得出了以下结论:


使用插入符号显示控制字符表示法:^M(回车)为<CR>^J(换行)为<LF>。在缓冲区中,<EOL>(行尾)显示为新的屏幕行,并使用Enter键输入。 <EOL>取决于缓冲区的文件格式:<EOL> = <CR>|<LF>|<CR><LF>分别对应mac|unix|dos

编辑缓冲区时,始终设置文件格式。要更改打开的缓冲区的文件格式,可以使用以下命令将<EOL>转换:

:set f[ile]f[ormat]=mac|unix|dos


除了转换<EOL>之外,该命令还将<LF>转换为<CR>将文件格式从mac更改为unix|dos时,将文件格式从<CR>更改为<LF>,相反,将文件格式从unix|dos更改为mac。要查看缓冲区的实际字节,可以使用以下命令,使用方便的十六进制编辑器xxd将缓冲区的文本表示形式转换为十六进制表示形式:

:%!xxd



在寄存器(用命令:reg[isters]:di[splay]显示)中,无论缓冲区的文件格式如何,<EOL>始终显示为^J(但并非所有^J都是<EOL>)。但是,<EOL>会按原样存储。为了能够将视觉上真实的^J(即<LF>)与寄存器中的其他^J(即<EOL>)区分开来,可以使用以下命令显示十六进制值,而不是不同于<EOL>的控制字符的插入记号: />
:set d[ispla]y=uhex



在搜索模式和替换字符串中: br />
\r = newline different from <EOL> (<CR> if <EOL> = <CR><LF>|<LF>, <LF> if <EOL> = <CR>)
\n = <EOL>


这表明当文件格式为dos时,由于<LF><EOL> = <CR><LF>不能输入<C-V><C-M>|<C-V><EOL> = <CR>


替换字符串中:与<EOL>不同的换行被解释为<EOL>;
<EOL>被解释为<NUL>

因此,根据4,:%s[ubstitute]/\r/\r/g<EOL>替换了缓冲区中与<EOL>不同的所有换行符,而:%s[ubstitute]/\n/\n/g替换缓冲区中每个带有<EOL><NUL>


在搜索寄存器/和命令寄存器:中,<EOL>转换为与<EOL>不同的换行符当分别从带有/<C-R>{register}:<C-R>{register}的寄存器插入时;
<NUL>;分别从带有:let @/=@{register}:let @:=@{register}的寄存器插入时。

当使用<EOL>从寄存器插入时。


为什么将换行符转换为搜索
寄存器内的Null字符并在命令行上转换为回车符?寄存器,您需要输入<EOL>并将其放入寄存器i<C-R>{register}中。如果文件格式为<LF>,则可以通过在空行上使用"来实现;如果文件格式为<LF>,则可以使用"来实现;如果文件格式为unix,则无法输入yy(请参阅第5页)。

现在您的语句部分错误,因为


您不会使用相同的方法将mac从寄存器i<C-V><C-M><Esc>yl复制到搜索寄存器dos和命令寄存器<LF>中。您可以使用<LF>复制到寄存器"/复制到寄存器:。在这两种情况下,分别使用:let @/=@"/会得到相同的结果(:<C-R>");
用两种不同的复制方法进行的:转换仅在文件格式为/<C-R>"时发生。如果是:<C-R>",则将<CR>复制到寄存器<LF>或寄存器unix时不会转换,如果是mac,则您甚至无法输入<LF>。不知道其背后的原因。

评论


为什么这是如此难以理解...我通过SO,vim-SE和vim帮助方面的几篇文章进行了研究,但并不完全一致,仍然感到困惑。

–硫蛋白
17年1月1日在7:57