Pregunta ¿Por qué los archivos de texto deben terminar con una nueva línea?


Supongo que todos aquí están familiarizados con el adagio de que todos los archivos de texto deben terminar con una nueva línea. Conozco esta "regla" desde hace años, pero siempre me he preguntado: ¿por qué?


1090
2018-04-08 12:16


origen


Respuestas:


Porque eso es cómo el estándar POSIX define una línea:

3.206 Línea
Una secuencia de cero o más caracteres no <newline> más un carácter de terminación <newline>.

Por lo tanto, las líneas que no terminan en un carácter de nueva línea no se consideran líneas reales. Es por eso que algunos programas tienen problemas para procesar la última línea de un archivo si no se termina la línea nueva.

Al trabajar con un emulador de terminal, hay al menos una ventaja importante en esta directriz: todas las herramientas de Unix esperan esta convención y funcionan con ella. Por ejemplo, al concatenar archivos con cat, un archivo terminado por nueva línea tendrá un efecto diferente que uno sin:

$ more a.txt
foo$ more b.txt
bar
$ more c.txt
baz
$ cat *.txt
foobar
baz

Y, como también lo demuestra el ejemplo anterior, cuando se visualiza el archivo en la línea de comando (por ejemplo, a través de more), un archivo terminado en nueva línea da como resultado una visualización correcta. Un archivo incorrectamente terminado puede ser confuso (segunda línea).

Para mayor coherencia, es muy útil seguir esta regla; de lo contrario, se incurrirá en trabajo adicional al tratar con las herramientas Unix predeterminadas.

Ahora, en no compatible con POSIX sistemas (hoy en día es principalmente Windows), el punto es discutible: los archivos generalmente no terminan con una nueva línea, y la definición (informal) de una línea podría ser, por ejemplo, "texto que es apartado por nuevas líneas "(observe el énfasis). Esto es completamente válido. Sin embargo, para datos estructurados (por ejemplo, código de programación) hace que el análisis sea mínimamente más complicado: generalmente significa que los analizadores deben ser reescritos. Si un analizador se escribió originalmente con la definición POSIX en mente, entonces podría ser más fácil modificar la secuencia de token en lugar del analizador; en otras palabras, agregar un token de "nueva línea artificial" al final de la entrada.


1021
2018-04-08 12:46



Cada línea debe terminar en un carácter de nueva línea, incluido el último. Algunos programas tienen problemas para procesar la última línea de un archivo si no se termina la línea nueva.

GCC lo advierte no porque hipocresía procesar el archivo, pero porque tiene que como parte del estándar.

El estándar de lenguaje C dice   Un archivo de origen que no esté vacío finalizará en un carácter de nueva línea, que no estará precedido inmediatamente por un carácter de barra inclinada invertida.

Como se trata de una cláusula "debe", debemos emitir un mensaje de diagnóstico para una violación de esta regla.

Esto se encuentra en la sección 2.1.1.2 de la norma ANSI C 1989. Sección 5.1.1.2 de la norma ISO C 1999 (y probablemente también la norma ISO C 1990).

Referencia: El archivo de correo GCC / GNU.


245
2018-04-08 12:26



Esta respuesta es un intento de respuesta técnica más que de opinión.

Si queremos ser puristas POSIX, definimos una línea como:

Una secuencia de cero o más caracteres no <newline> más un carácter de terminación <newline>.

Fuente: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

Una línea incompleta como:

Una secuencia de uno o más caracteres no <newline> al final del archivo.

Fuente: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

Un archivo de texto como:

Un archivo que contiene caracteres organizados en cero o más líneas. Las líneas no contienen caracteres NUL y ninguna puede exceder {LINE_MAX} bytes de longitud, incluido el carácter <nueva línea>. Aunque POSIX.1-2008 no distingue entre archivos de texto y archivos binarios (consulte el estándar ISO C), muchas utilidades solo producen resultados predecibles o significativos cuando se trabaja en archivos de texto. Las utilidades estándar que tienen tales restricciones siempre especifican "archivos de texto" en sus secciones STDIN o INPUT FILES.

Fuente: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

Una cadena como:

Una secuencia contigua de bytes terminada por e incluyendo el primer byte nulo.

Fuente: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

A partir de esto, podemos deducir que el único momento en que lo haremos potencialmente encontrar cualquier tipo de problema si tratamos con el concepto de una línea de un archivo o un archivo como un Archivo de texto (siendo eso un Archivo de texto es una organización de cero o más líneas, y una línea que sabemos debe terminar con una <nueva línea>).

Caso en punto: wc -l filename.

Desde el wcEl manual que leemos:

Una línea se define como una cadena de caracteres delimitados por un carácter <nueva línea>.

¿Cuáles son las implicaciones para los archivos JavaScript, HTML y CSS que son texto  archivos?

En navegadores, IDEs modernos y otras aplicaciones de front-end, no hay problemas al omitir EOL en EOF. Las aplicaciones analizarán los archivos correctamente. Tiene que ser porque no todos los sistemas operativos se ajustan al estándar POSIX, por lo que no sería práctico para las herramientas que no son del sistema operativo (por ejemplo, los navegadores) manejar los archivos de acuerdo con el estándar POSIX (o cualquier estándar de nivel del sistema operativo).

Como resultado, podemos estar relativamente seguros de que EOL en EOF no tendrá prácticamente ningún impacto negativo en el nivel de la aplicación, independientemente de si se ejecuta en un sistema operativo UNIX.

En este punto, podemos decir con confianza que omitir EOL en EOF es seguro cuando se trata con JS, HTML, CSS en el lado del cliente. En realidad, podemos afirmar que minimizar cualquiera de estos archivos, que no contiene <nueva línea>, es seguro.

Podemos dar un paso más allá y decir que, en lo que respecta a NodeJS, tampoco puede adherirse al estándar POSIX, ya que puede ejecutarse en entornos no compatibles con POSIX.

¿Qué nos queda entonces? Herramientas a nivel del sistema.

Esto significa que los únicos problemas que pueden surgir son las herramientas que hacen un esfuerzo para adherir su funcionalidad a la semántica de POSIX (por ejemplo, la definición de una línea como se muestra en wc)

Aun así, no todas las shells se adherirán automáticamente a POSIX. Bash, por ejemplo, no adopta el comportamiento POSIX por defecto. Hay un interruptor para habilitarlo: POSIXLY_CORRECT.

Un elemento de reflexión sobre el valor de EOL siendo <newline>: http://www.rfc-editor.org/EOLstory.txt

Permaneciendo en la pista de herramientas, para todos los propósitos prácticos, consideremos esto:

Trabajemos con un archivo que no tiene EOL. Al escribir esto, el archivo en este ejemplo es un JavaScript miniaturizado sin EOL.

curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

Observe la cat el tamaño del archivo es exactamente la suma de sus partes individuales. Si la concatenación de archivos JavaScript es una preocupación para los archivos JS, la preocupación más adecuada sería iniciar cada archivo JavaScript con un punto y coma.

Como alguien más mencionó en este hilo: ¿y si quieres cat dos archivos cuya salida se convierte en una sola línea en lugar de dos? En otras palabras, cat hace lo que se supone que debe hacer.

los man de cat solo menciona la lectura de entrada hasta EOF, no <newline>. Tenga en cuenta que -n interruptor de cat también imprimirá una línea que no sea <newline> terminada (o línea incompleta) como un línea - siendo que el recuento comienza en 1 (de acuerdo con la man.)

-n Número las líneas de salida, comenzando en 1.

Ahora que entendemos cómo POSIX define un línea , este comportamiento se vuelve ambiguo, o realmente, no conforme.

Comprender el propósito y el cumplimiento de una herramienta determinada ayudará a determinar qué tan importante es terminar los archivos con un EOL. En C, C ++, Java (JAR), etc. ... algunos estándares dictarán una nueva línea para la validez; no existe tal estándar para JS, HTML, CSS.

Por ejemplo, en lugar de usar wc -l filename uno podría hacer awk '{x++}END{ print x}' filename , y puede estar seguro de que el éxito de la tarea no se ve comprometido por un archivo que tal vez deseemos procesar y que no hayamos escrito (por ejemplo, una biblioteca de terceros como JS minificado). curld) - a menos que nuestra intención fuera realmente contar líneas en el sentido compatible con POSIX.

Conclusión

Habrá muy pocos casos de uso de la vida real donde omitir EOL en EOF para ciertos archivos de texto como JS, HTML y CSS tendrá un impacto negativo, si es que lo hace. Si confiamos en que <newline> está presente, estamos restringiendo la confiabilidad de nuestras herramientas solo a los archivos que generamos y nos abrimos a posibles errores introducidos por archivos de terceros.

Moraleja de la historia: herramientas de ingeniería que no tienen la debilidad de confiar en EOL en EOF.

Siéntase libre de publicar casos de uso, ya que se aplican a JS, HTML y CSS, donde podemos examinar cómo la omisión de EOL tiene un efecto adverso.


87
2017-08-15 06:31



Puede estar relacionado con el diferencia entre:

  • archivo de texto (se supone que cada línea termina en un final de línea)
  • archivo binario (no hay verdaderas "líneas" para hablar, y la longitud del archivo debe conservarse)

Si cada línea finaliza en un final de línea, esto evita, por ejemplo, que concatenar dos archivos de texto haga que la última línea de la primera carrera entre en la primera línea del segundo.

Además, un editor puede verificar si el archivo finaliza en un final de línea, lo guarda en su opción local 'eol' y lo usa al escribir el archivo.

Hace unos años (2005), muchos editores (ZDE, Eclipse, Scite, ...) se "olvidaron" de la EOL final, que no fue muy apreciado.
No solo eso, sino que interpretaron ese EOL final incorrectamente, como 'comenzar una nueva línea', y en realidad comienzan a mostrar otra línea como si ya existiera.
Esto fue muy visible con un archivo de texto "adecuado" con un editor de texto de buen comportamiento como vim, en comparación con abrirlo en uno de los editores anteriores. Mostraba una línea adicional debajo de la última línea real del archivo. Ves algo como esto:

1 first line
2 middle line
3 last line
4

59
2018-04-08 12:29



Algunas herramientas esperan esto. Por ejemplo, wc espera esto:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

37
2017-10-12 14:16



Básicamente hay muchos programas que no procesarán los archivos correctamente si no obtienen el EOL EOL final.

GCC te advierte sobre esto porque se espera como parte del estándar C. (sección 5.1.1.2 aparentemente)

Advertencia de compilación "No hay nueva línea al final del archivo"


18
2018-04-08 12:21



Esto se origina desde los primeros días cuando se usaban terminales simples. El carácter de nueva línea se utilizó para desencadenar un "lavado" de los datos transferidos.

Hoy, ya no se requiere la charla nueva. Claro, muchas aplicaciones todavía tienen problemas si la nueva línea no está allí, pero consideraría un error en esas aplicaciones.

Sin embargo, si tiene un formato de archivo de texto donde exigir En la línea nueva, obtendrá una verificación de datos simple muy económica: si el archivo finaliza con una línea que no tiene línea nueva al final, sabrá que el archivo está roto. Con solo un byte extra para cada línea, puede detectar archivos rotos con gran precisión y casi sin tiempo de CPU.


12
2018-04-08 12:41



También hay un problema de programación práctica con archivos que carecen de líneas nuevas al final: read Bash incorporado (no sé sobre otro read implementaciones) no funciona como se esperaba:

printf $'foo\nbar' | while read line
do
    echo $line
done

Esto imprime solamente foo! La razón es que cuando read encuentra la última línea, escribe el contenido en $line pero devuelve el código de salida 1 porque alcanzó EOF. Esto rompe el while loop, por lo que nunca llegamos a la echo $line parte. Si desea manejar esta situación, debe hacer lo siguiente:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

Es decir, haz lo echo Si el read falló debido a una línea no vacía al final del archivo. Naturalmente, en este caso habrá una nueva línea adicional en la salida que no estaba en la entrada.


10
2017-11-04 10:12