为什么换行符在搜索寄存器中转换为Null字符,并在命令行转换为回车符?


12

如果我有以下文字:

foo
bar

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

""   foo^Jbar^J

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

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

"a   foo^Jbar^J

它没有改变。

如果现在我输入:let @/ = @",将其复制到搜索寄存器中,则其内容为的输出:reg /

"/   foo^@bar^@

根据上一张图表,它似乎^@是Null字符的插入符号。
为什么换行符会自动在搜索寄存器(而不是a寄存器)内转换为Null字符?

如果我在命令行上(或之后的搜索中/)插入未命名的寄存器,则通过输入:<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以普通模式键入以到达下一行出现的2行foobar,而不是得到正匹配,则出现以下错误消息:

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

此外,此链接似乎可以解释为NUL表示字符串的结尾,而LF表示文本文件中行的结尾。

而且,如stored as 10帮助人员所言,NUL 与LF的代码相同,那么Vim如何使两者之间有区别?

编辑3

10如帮助所说,也许LF和NUL用相同的十进制代码编码。由于上下文,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会显示CR 。

可能是其字符10随上下文而变化的字符的解释:

  • 缓冲区中的行尾(^J
  • 搜索(^@)中字符串的结尾
  • 命令行上的命令结尾(^M

2
有时,意外NULL 字符的出现是由处理字符串的基础C函数引起的。这章C如何处理字符串的解释,你链接到解释说,内部ç界定串用NULLNULLs在文本中很少出现,因此使其成为很好的字符。这样的结果是,如果C程序(vim)试图将“空”字符串传递到内部C函数中
the_velour_fog 16/02/29

2
例如someFunction(arg1, ""),arg 2是"" “引号之间的项目,实际上什么都不是-“空”。可能会出现NULL,因为它由分隔字符串的基础C实现“添加”了。我不知道您将如何检查这一点-但它可能是造成这种情况的原因
。– the_velour_fog

1
另请参阅中的讨论\r\n区别:substitute
jamessan '16

Answers:


4

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

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

  1. 控制字符使用脱字符号显示:^Mfor <CR>(回车)和^Jfor <LF>(换行)。在缓冲区中,<EOL>(行尾)显示为新的屏幕行,并使用回车键输入。<EOL>依赖于缓存的文件格式:<EOL> = <CR>|<LF>|<CR><LF>对于mac|unix|dos分别。

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

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

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

    :%!xxd
    
  3. 在寄存器(与命令显示:reg[isters]:di[splay]),<EOL>总是显示^J(但不是全部^J<EOL>),不管缓冲器的文件格式。但是<EOL>存储,因为他们应该。为了能够在视觉上区分真实^J(即<LF>从其他)^J(即<EOL>)在寄存器中,则可以使用下面的命令显示的十六进制值的,而不是从不同的控制字符插入符号表示法<EOL>

    :set d[ispla]y=uhex
    
  4. 在搜索模式和替换字符串中:

    \r = newline different from <EOL> (<CR> if <EOL> = <CR><LF>|<LF>, <LF> if <EOL> = <CR>)
    \n = <EOL>
    
  5. 到处:

    <C-V><C-M>|<C-V><EOL> = newline different from <EOL>
    <C-V><C-J> = <NUL>
    

    这表明,当文件格式dos为时<LF>,由于<EOL> = <CR><LF>和,因此无法输入<C-V><C-M>|<C-V><EOL> = <CR>

  6. 在替换字符串中:

    • 不同于的换行符<EOL>解释<EOL>;

    • <EOL>解释<NUL>

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

  7. 在搜索寄存器/和命令寄存器中:<EOL>转换

    • 换行符不同于分别<EOL>通过/<C-R>{register}或从寄存器中插入时的换行符:<C-R>{register}

    • <NUL>当分别从:let @/=@{register}:let @:=@{register}分别从寄存器插入时。

  8. 在缓冲器,从换行不同<EOL>转换<EOL>从使用寄存器插入时i<C-R>{register}

为什么换行符在搜索寄存器中转换为Null字符,并在命令行转换为回车符?

<LF>从未命名寄存器复制"到其他寄存器之前,需要输入<LF>并将其放入寄存器中"。如果文件格式为unix,则可以通过yy在空行上使用来实现;如果文件格式是mac,您可以通过使用i<C-V><C-M><Esc>yl; 如果文件格式为dos,则无法输入<LF>(请参阅5.)。

现在您的陈述部分错误,因为

  • <LF>从寄存器复制"到搜索寄存器/和命令寄存器的方法不同:。您:let @/=@"用于复制到寄存器/:<C-R>"复制到寄存器:。在两种情况下/<C-R>":<C-R>"分别使用和会给您相同的结果(<CR>);

  • <LF>仅当文件格式为时,使用两种不同的复制方法进行的转换才会发生unix。如果是mac<LF>不是当复制到寄存器转换/或寄存器:,如果它是dos你甚至无法输入<LF>

7给出了正确的声明。但是我真的不知道其背后的原因。


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