Por que um feed de linha é convertido em um caractere nulo no registro de pesquisa e em um retorno de carro na linha de comando?

12

Se eu tiver o seguinte texto:

foo
bar

Eu visualmente seleciono e copio.
O texto está agora armazenado no registro sem nome "e aqui está o seu conteúdo (saída de :reg "):

""   foo^Jbar^J

De acordo com este gráfico , parece ^Jser a notação de sinal de intercalação para um avanço de linha.

Se eu quiser duplicar o registro sem nome no aregistro, digitando: :let @a = @"
Aqui está o conteúdo (saída de :reg a):

"a   foo^Jbar^J

Isso não mudou.

Se agora eu o duplicar no registro de pesquisa, digitando :let @/ = @", eis o conteúdo (saída de :reg /):

"/   foo^@bar^@

De acordo com o gráfico anterior, parece ^@ser a notação de sinal de intercalação para um caractere nulo.
Por que um feed de linha é automaticamente convertido em um caractere nulo no registro de pesquisa (mas não no aregistro)?

Se eu inserir o registro sem nome na linha de comando (ou dentro de uma pesquisa depois /), digitando :<C-R>", aqui está o que é inserido:

:foo^Mbar^M

Mais uma vez, de acordo com o último gráfico, ^Mparece ser a notação de intercalação para um retorno de carro.
Por que um avanço de linha é automaticamente convertido em retorno de carro na linha de comando?

Editar :

Geralmente, você pode inserir um caractere de controle literal digitando:
<C-V><C-{character in caret notation}>

Por exemplo, você pode inserir um literal <C-R>digitando <C-V><C-R>.
Você pode fazer isso para aparentemente qualquer personagem de controle.
No entanto, notei que não consigo inserir um LF literal dentro de um buffer ou na linha de comando, porque se eu digitar: <C-V><C-J>insere ^@, um caractere nulo, em vez de ^J.
É pelo mesmo motivo que um LF é convertido em NUL dentro do registro de pesquisa?

Edição 2 :

Em :h key-notation, podemos ler o seguinte:

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

A stored as 10parte na primeira linha e used for <Nul>na segunda linha pode indicar que há algum tipo de sobreposição entre um LF e um NUL, e que eles podem ser interpretados como a mesma coisa. Mas eles não podem ser a mesma coisa, porque depois de executar o comando anterior :let @/ = @", se eu digitar nno modo normal para chegar à próxima ocorrência das 2 linhas fooe bar, em vez de obter uma correspondência positiva, tenho a seguinte mensagem de erro:

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

Além desse link, parece explicar que um NUL denota o final de uma string, enquanto um LF denota o final de uma linha em um arquivo de texto.

E se um NUL é stored as 10como a ajuda diz, que é o mesmo código que para um LF, como o Vim é capaz de fazer a diferença entre os 2?

Edição 3 :

Talvez um LF e um NUL sejam codificados com o mesmo código decimal 10, como diz a ajuda. E o Vim faz a diferença entre os 2, graças ao contexto. Se encontrar um caractere cujo código decimal esteja 10em um buffer ou em qualquer registro, exceto os registros de pesquisa e comando, ele o interpretará como um LF.
Porém, no registro de pesquisa ( :reg /), ele é interpretado como um NUL porque, no contexto de uma pesquisa, o Vim procura apenas uma sequência em que o conceito de end of line in a filenão faz sentido porque uma sequência não é um arquivo (o que é estranho, pois você pode ainda usa o átomo \nem um padrão pesquisado, mas talvez isso seja apenas um recurso do mecanismo de expressão regular?). Portanto, ele interpreta automaticamente 10como um NUL porque é o conceito mais próximo ( end of stringend of line).

E da mesma maneira, na linha de comando / command register ( :reg :) ele interpreta o código 10como um CR, porque o conceito de end of line in a filenão faz sentido aqui. O conceito mais próximo é end of commandque o Vim interpreta 10como um CR, porque bater Enteré a maneira de terminar / executar um comando e um CR é o mesmo que bater Enter, pois quando você insere um literal com <C-V><Enter>, ^Mé exibido.

Talvez a interpretação do personagem cujo código seja 10alterado de acordo com o contexto:

  • fim de linha em um buffer ( ^J)
  • fim da string em uma pesquisa ( ^@)
  • fim do comando na linha de comando ( ^M)
saginaw
fonte
2
Às vezes, a ocorrência de NULL caracteres inesperados é causada pela função C subjacente que está manipulando as seqüências de caracteres. Esta explicação de como C processa seqüências de caracteres às quais você vinculou explica que internamente C delimita seqüências de caracteres com a NULL. NULLs ocorrem raramente no texto, o que o torna um bom caractere para esse fim. Uma consequência disto é que, se o programa de C (VIM) tentou transmitir uma "esvaziar" cadeia numa função interna C
the_velour_fog
2
por exemplo, someFunction(arg1, "")onde arg 2 estava, "" ou seja, "o item entre aspas, que literalmente não é nada - um" vazio ". um NULL pode aparecer, porque foi" adicionado "pela implementação C subjacente, pois delimitou a string. Não sei como você iria verificar para isso - mas ele vem à mente como uma possível causa.
the_velour_fog
1
Veja também a discussão \re a \ndiferença em:substitute .
jamessan

Respostas:

4

Primeiro, obrigado por este post muito abrangente e atencioso.

Após alguns testes, cheguei a esta conclusão:

  1. Os caracteres de controle são exibidos usando a notação de sinal de intercalação: ^Mpara <CR>(retorno de carro) e ^Jpara <LF>(avanço de linha). Nos buffers, <EOL>(fim de linha) são exibidos como novas linhas da tela e são inseridos com a tecla Enter. <EOL>dependem do formato do buffer de arquivo: <EOL> = <CR>|<LF>|<CR><LF>para mac|unix|dos, respectivamente.

  2. Ao editar um buffer, o formato do arquivo é sempre definido. Para alterar o formato do arquivo de um buffer aberto, você pode usar o seguinte comando que converte <EOL>:

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

    Além de converter <EOL>, esse comando é convertido <LF>em <CR>quando o formato do arquivo é alterado de macpara unix|dose, inversamente, <CR>em <LF>quando o formato do arquivo é alterado de unix|dospara mac. Para ver os bytes reais do buffer, você pode usar o seguinte comando que transforma a representação textual do buffer em sua representação hexadecimal usando o conveniente editor hexadecimal xxd:

    :%!xxd
    
  3. Nos registradores (mostrados com o comando :reg[isters]ou :di[splay]), <EOL>sempre são exibidos como ^J(mas nem todos ^Jsão <EOL>), independentemente do formato do arquivo do buffer. No entanto, <EOL>são armazenados como deveriam. Para poder distinguir visualmente real ^J(ou seja <LF>) dos outros ^J(ou seja <EOL>) nos registros, você pode usar o seguinte comando que exibe os valores hexadecimais em vez da notação de sinal de intercalação dos caracteres de controle diferentes de <EOL>:

    :set d[ispla]y=uhex
    
  4. Nos padrões de pesquisa e cadeias de substituição:

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

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

    Isso mostra que, quando o formato do arquivo é dos, é impossível inserir <LF>, pois <EOL> = <CR><LF>e <C-V><C-M>|<C-V><EOL> = <CR>.

  6. Nas cadeias de substituição:

    • nova linha diferente de <EOL>são interpretadas como <EOL>;

    • <EOL>são interpretados como <NUL>.

    Portanto, de acordo com 4., :%s[ubstitute]/\r/\r/gsubstitui todas as novas linhas diferentes do <EOL>buffer por <EOL>, enquanto :%s[ubstitute]/\n/\n/gsubstitui todas <EOL>as novas linhas do buffer por <NUL>.

  7. No registro de pesquisa /e registro de comando :, <EOL>são convertidos em

    • nova linha diferente de <EOL>quando inserida de um registro com /<C-R>{register}ou :<C-R>{register}respectivamente;

    • <NUL>quando inserido a partir de um registro com :let @/=@{register}ou :let @:=@{register}respectivamente.

  8. Nos buffers, uma nova linha diferente de <EOL>é convertida para <EOL>quando inserida de um registro usando i<C-R>{register}.

Por que um feed de linha é convertido em um caractere nulo no registro de pesquisa e em um retorno de carro na linha de comando?

Antes de copiar <LF>do registro não nomeado "para outros registros, é necessário inserir <LF>e colocá-lo no registro ". Se o formato do arquivo for unix, você poderá fazer isso usando yyuma linha vazia; se o formato do arquivo for mac, você poderá fazer isso usando i<C-V><C-M><Esc>yl; se o formato do arquivo for dos, você não poderá inserir <LF>(cf. 5.).

Agora sua declaração está parcialmente errada, pois

  • você não usa o mesmo método para copiar <LF>do registro "para o registro de pesquisa /e comando :. Você usa :let @/=@"para copiar para o registro /e :<C-R>"para copiar para o registro :. Usar /<C-R>"e :<C-R>"respectivamente fornecerá o mesmo resultado ( <CR>) nos dois casos;

  • as conversões <LF>que ocorrem com seus dois métodos diferentes de cópia ocorrem apenas quando o formato do arquivo é unix. Se for mac, não<LF> é convertido quando copiado para o registro ou o registro , e se for, você não pode nem inserir ./:dos<LF>

A afirmação correta é dada por 7. Mas eu realmente não sei as razões por trás disso.

Maggyero
fonte
Por que isso é tão difícil de entender ... Eu pesquisei em várias postagens sobre SO e vim-SE e vim help, mas não completamente consistente e ainda confuso.
Violapterin