¿Por qué se convierte un avance de línea en un carácter nulo dentro del registro de búsqueda y en un retorno de carro en la línea de comando?

12

Si tengo el siguiente texto:

foo
bar

Lo selecciono visualmente y lo copio.
El texto ahora se almacena en el registro sin nombre "y aquí está su contenido (salida de :reg "):

""   foo^Jbar^J

Según este gráfico , parece ^Jser la notación de intercalación para un avance de línea.

Si quiero duplicar el registro sin nombre en el aregistro escribiendo: :let @a = @"
Aquí está su contenido (salida de :reg a):

"a   foo^Jbar^J

No ha cambiado.

Si ahora lo duplico en el registro de búsqueda escribiendo :let @/ = @", aquí está su contenido (salida de :reg /):

"/   foo^@bar^@

Según el gráfico anterior, parece ^@ser la notación de intercalación para un carácter nulo.
¿Por qué un salto de línea se convierte automáticamente en un carácter nulo dentro del registro de búsqueda (pero no en el aregistro)?

Si inserto el registro sin nombre en la línea de comando (o dentro de una búsqueda después /), al escribir :<C-R>", esto es lo que se inserta:

:foo^Mbar^M

Una vez más, según el último cuadro, ^Mparece ser la notación de intercalación para un retorno de carro.
¿Por qué un salto de línea se convierte automáticamente en un retorno de carro en la línea de comando?

Editar :

Por lo general, puede insertar un carácter de control literal escribiendo:
<C-V><C-{character in caret notation}>

Por ejemplo, puede insertar un literal <C-R>escribiendo <C-V><C-R>.
Puedes hacerlo por aparentemente cualquier personaje de control.
Sin embargo, me di cuenta de que no puedo insertar un LF literal dentro de un búfer o en la línea de comando, porque si escribo: <C-V><C-J>inserta ^@un carácter nulo en lugar de ^J.
¿Es por la misma razón que un LF se convierte en NUL dentro del registro de búsqueda?

Edición 2 :

En :h key-notation, podemos leer esto:

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

La stored as 10parte en la primera línea y used for <Nul>en la segunda línea podría indicar que hay algún tipo de superposición entre un LF y un NUL, y que podrían interpretarse como la misma cosa. Pero no pueden ser lo mismo, porque después de ejecutar el comando anterior :let @/ = @", si escribo nen modo normal para llegar a la siguiente aparición de las 2 líneas fooy bar, en lugar de obtener una coincidencia positiva, tengo el siguiente mensaje de error:

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

Además, este enlace parece explicar que un NUL denota el final de una cadena, mientras que un LF denota el final de una línea en un archivo de texto.

Y si un NUL es stored as 10como dice la ayuda, que es el mismo código que para un LF, ¿cómo puede Vim hacer la diferencia entre los 2?

Edición 3 :

Tal vez un LF y un NUL están codificados con el mismo código decimal 10, como dice la ayuda. Y Vim marca la diferencia entre los 2 gracias al contexto. Si encuentra un carácter cuyo código decimal está 10en un búfer o en cualquier registro, excepto los registros de búsqueda y comando, lo interpreta como un LF.
Pero en el registro de búsqueda ( :reg /) lo interpreta como un NUL porque en el contexto de una búsqueda, Vim solo busca una cadena donde el concepto de end of line in a fileno tiene sentido porque una cadena no es un archivo (lo cual es extraño ya que puede todavía usa el átomo \nen un patrón buscado, pero ¿tal vez eso sea solo una característica del motor de expresiones regulares?). Por lo tanto, se interpreta automáticamente 10como un NUL porque es el concepto más cercano ( end of stringend of line).

Y de la misma manera, en la línea de comando / registro de comando ( :reg :) interpreta el código 10como un CR, porque el concepto de end of line in a fileno tiene sentido aquí. El concepto más cercano es end of commandque Vim interpreta 10como un CR, porque golpear Enteres la forma de finalizar / ejecutar un comando y un CR es lo mismo que golpear Enter, ya que cuando inserta uno literal con <C-V><Enter>, ^Mse muestra.

Tal vez la interpretación del personaje cuyo código es 10cambia según el contexto:

  • fin de línea en un buffer ( ^J)
  • fin de cadena en una búsqueda ( ^@)
  • fin del comando en la línea de comando ( ^M)
Saginaw
fuente
2
A veces, la aparición de NULL caracteres inesperados es causada por la función C subyacente que maneja las cadenas. Esta explicación de cómo C procesa las cadenas a las que se vinculó explica que internamente C delimita las cadenas con a NULL. NULLs ocurren raramente en el texto lo suficiente como para que sea un buen personaje para este propósito. Una consecuencia de esto es que si el programa C (vim) intentó pasar una cadena "vacía" a una función interna C
the_velour_fog
2
por ejemplo, someFunction(arg1, "")donde arg 2 era, "" es decir, "el elemento entre las comillas, que literalmente no es nada: un" vacío ". Puede aparecer un NULL, porque fue" agregado "por la implementación subyacente de C ya que delimitó la cadena. No sé cómo le gustaría comprobar esto - pero viene a la mente como una posible causa.
the_velour_fog
1
Vea también la discusión \ry la \ndiferencia en:substitute .
jamessan

Respuestas:

4

Primero, gracias por esta publicación tan completa y reflexiva.

Después de algunas pruebas, he llegado a esta conclusión:

  1. Los caracteres de control se muestran utilizando la notación de intercalación: ^Mpara <CR>(retorno de carro) y ^Jpara <LF>(avance de línea). En los buffers, <EOL>(fin de línea) se muestran como nuevas líneas de pantalla y se ingresan con la tecla Intro. <EOL>dependerá del formato de archivo del búfer: <EOL> = <CR>|<LF>|<CR><LF>para mac|unix|dosrespectivamente.

  2. Al editar un búfer, el formato del archivo siempre se establece. Para cambiar el formato de archivo de un búfer abierto, puede usar el siguiente comando que convierte <EOL>:

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

    Además de la conversión <EOL>, este comando convierte <LF>a <CR>cuando se cambia el formato de archivo de maca unix|dosy, por el contrario, <CR>a <LF>cuando se cambia el formato de archivo de unix|dosa mac. Para ver los bytes reales del búfer, puede usar el siguiente comando que transforma la representación textual del búfer en su representación hexadecimal usando el conveniente editor hexadecimal xxd:

    :%!xxd
    
  3. En registros (mostrado con el comando :reg[isters]o :di[splay]), <EOL>siempre se visualiza como ^J(pero no todos ^Json <EOL>), independientemente del formato de archivo de la memoria intermedia. Sin embargo, <EOL>se almacenan como deberían. Para poder distinguir visualmente real ^J(es decir <LF>) de los otros ^J(es decir <EOL>) en los registros, puede usar el siguiente comando que muestra los valores hexadecimales en lugar de la notación de caracteres de control diferentes de <EOL>:

    :set d[ispla]y=uhex
    
  4. En patrones de búsqueda y cadenas de sustitución:

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

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

    Esto muestra que cuando el formato de archivo es dos, es imposible ingresar <LF>, ya que <EOL> = <CR><LF>y <C-V><C-M>|<C-V><EOL> = <CR>.

  6. En cadenas de sustitución:

    • nueva línea diferente de <EOL>se interpretan como <EOL>;

    • <EOL>son interpretados como <NUL>.

    Entonces, de acuerdo con 4., :%s[ubstitute]/\r/\r/greemplaza cada nueva línea diferente de <EOL>en el búfer con <EOL>, mientras que :%s[ubstitute]/\n/\n/greemplaza cada <EOL>en el búfer con <NUL>.

  7. En el registro de búsqueda /y el registro de comando :, <EOL>se convierten a

    • nueva línea diferente de <EOL>cuando se inserta desde un registro con /<C-R>{register}o :<C-R>{register}respectivamente;

    • <NUL>cuando se inserta desde un registro con :let @/=@{register}o :let @:=@{register}respectivamente.

  8. En tampones, salto de línea diferente de <EOL>se convierte a <EOL>cuando se inserta desde un registro utilizando i<C-R>{register}.

¿Por qué se convierte un avance de línea en un carácter nulo dentro del registro de búsqueda y en un retorno de carro en la línea de comando?

Antes de copiar <LF>desde el registro sin nombre "a otros registros, debe ingresarlo <LF>y colocarlo en el registro ". Si el formato del archivo es unix, puede hacerlo utilizando yyuna línea vacía; si el formato del archivo es mac, puede hacerlo usando i<C-V><C-M><Esc>yl; si el formato del archivo es dos, no puede ingresar <LF>(ver 5.).

Ahora su declaración es parcialmente incorrecta, ya que

  • no utiliza el mismo método para copiar <LF>desde el registro "al registro de búsqueda /y al registro de comandos :. Se utiliza :let @/=@"para copiar en el registro /y :<C-R>"para copiar en el registro :. Usar /<C-R>"y :<C-R>"respectivamente le dará el mismo resultado ( <CR>) en ambos casos;

  • las conversiones de <LF>que tienen lugar con sus dos diferentes métodos de copia ocurren solamente cuando el formato de archivo es unix. Si se trata mac, <LF>es no convertidos cuando se copian en el registro /o en el registro :, y si es dosque no se puede ni siquiera de entrada <LF>.

La declaración correcta viene dada por 7. Pero realmente no sé las razones detrás de esto.

Maggyero
fuente
¿Por qué es tan difícil de entender? He investigado a través de varias publicaciones sobre SO y vim-SE y vim help, pero no es completamente coherente y todavía estoy confundido.
Violapterin