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


Asumo que todos aquí están familiarizados con el adagio de que todos los archivos de texto deben terminar con una nueva línea. He sabido de esta "regla" durante años, pero siempre me he preguntado - ¿por qué?

Author: codeforester, 2009-04-08

17 answers

Porque así es como el estándar POSIX define una línea :

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

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 nueva línea.

Hay al menos una ventaja difícil de esta guía cuando se trabaja en un emulador de terminal: Todas las herramientas Unix esperan esta convención y trabajan con ella. Por ejemplo, al concatenar archivos con cat, un archivo terminado por newline 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 el ejemplo anterior también demuestra, cuando se muestra el archivo en la línea de comandos (por ejemplo, a través de more), un archivo terminado en una nueva línea resulta en una visualización correcta. Un archivo terminado incorrectamente puede ser ilegible (segunda línea).

Para la consistencia, es muy útil siga esta regla – de lo contrario incurrirá en trabajo extra cuando se trate de las herramientas Unix predeterminadas.

Ahora, en los sistemas que no cumplen con POSIX (hoy en día, la mayoría son 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 está separado por nuevas líneas" (note el énfasis). Esto es totalmente válido. Sin embargo, para datos estructurados (por ejemplo, código de programación) hace que el análisis sea mínimo más complicado: generalmente significa que los analizadores tienen que ser reescritos. Si un analizador se escribió originalmente con la definición POSIX en mente, entonces podría ser más fácil modificar el flujo de token en lugar del analizador, en otras palabras, agregar un token de "nueva línea artificial" al final de la entrada.

 1065
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-22 10:11:57

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

GCC advierte sobre ello no porque no puedaprocesar el archivo, sino porque tiene que como parte del estándar.

El estándar del lenguaje C dice Un archivo fuente que no esté vacío terminará en un carácter de nueva línea, que no irá precedido inmediatamente por una barra invertida caracter.

Dado que esta es una cláusula "shall", 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.

 249
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:26:46

Esta respuesta es un intento de una respuesta técnica en lugar de una opinión.

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

Una secuencia de cero o más caracteres que no sean más un carácter de terminación .

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- en el fin 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 ninguno puede exceder {LINE_MAX} bytes en longitud, incluyendo el carácter . Aunque POSIX.1-2008 no distingue entre archivos de texto y archivos binarios (ver el estándar ISO C), muchos las utilidades solo producen resultados predecibles o significativos cuando operan en archivos de texto. Las utilidades estándar que tienen tales restricciones siempre especifican "archivos de texto" en sus secciones STDIN o ARCHIVOS DE ENTRADA.

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

Una cadena como:

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

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

De esto entonces, podemos deducir que la única vez que potencialmente se encontrará con cualquier tipo de problemas es si tratamos con el concepto de una línea de un archivo o un archivo como un archivo de texto (siendo que 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 ).

Caso en cuestión: wc -l filename.

Del manual de wc leemos: {[17]]}

Una línea se define como una cadena de caracteres delimitada por un carácter .

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

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

Como resultado, podemos estar relativamente seguros de que EOL en EOF no tendrá prácticamente ningún impacto negativo a nivel de 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 de JS, HTML, CSS en el lado del cliente. En realidad, puede indicar que minificar cualquiera de estos archivos, que no contiene es seguro.

Podemos ir 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 de sistema.

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

Aún así, no todos los shells se adherirán automáticamente a POSIX. Bash, por ejemplo, no tiene por defecto el comportamiento POSIX. Hay un interruptor para activarlo: POSIXLY_CORRECT.

Alimento para la reflexión sobre el valor de EOL siendo : http://www.rfc-editor.org/EOLstory.txt

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

Vamos a trabajar con un archivo que no tiene EOL. A partir de este escribir el archivo en este ejemplo es un JavaScript minificado 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 que el tamaño del archivo cat 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 apropiada sería iniciar cada archivo JavaScript con un punto y coma.

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

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

-n Numere las líneas de salida, comenzando en 1.

Ahora que entendemos cómo POSIX define una línea , este comportamiento se convierte en ambiguo, o realmente, no obediente.

Comprender el propósito y el cumplimiento de una herramienta determinada ayudará a determinar qué tan crítico es terminar los archivos con un EOL. En C, C++, Java (JARs), 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 tenga la seguridad de que el éxito de la tarea no se ve comprometido por un archivo que queramos procesar que no escribimos (por ejemplo, un biblioteca de terceros como la minificada JS we curl d) - 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 en la vida real en los que saltarse EOL en EOF para ciertos archivos de texto como JS, HTML y CSS tendrá un impacto negativo, si es que lo tiene. Si confiamos en que esté presente, estamos restringiendo la confiabilidad de nuestras herramientas solo a los archivos que creamos y abrimos nosotros mismos a posibles errores introducidos por archivos de terceros.

Moraleja de la historia: Ingeniero de herramientas que no tiene 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 saltar EOL tiene un efecto adverso.

 91
Author: Milan Adamovsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-15 06:47:08

Puede estar relacionado con la diferencia entre :

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

Si cada línea termina en un final de línea, esto evita, por ejemplo, que la concatenación de dos archivos de texto haría que la última línea de la primera corriera en la primera línea de la segunda.

Además, un editor puede comprobar al cargar si el archivo termina en un final de línea, lo guarda en su opción local 'eol', y lo usa cuando escribe el archivo.

Hace unos años (2005), muchos editores (ZDE, Eclipse, Scite,...) se "olvidaron" de esa EFV final, que no fue muy apreciada.
No solo eso, sino que interpretaron esa final EOL incorrectamente, como 'iniciar una nueva línea', y en realidad comienzan a mostrar otra línea como si ya existiera.
Esto era muy visible con un archivo de texto 'adecuado' con un texto bien comportado editor como vim, en comparación con la apertura en uno de los editores anteriores. Se muestra una línea extra debajo de la última línea real del archivo. Ves algo como esto:

1 first line
2 middle line
3 last line
4
 59
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-04 11:30:07

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
 39
Author: Flimm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-12 14:16:58

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

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

"No hay nueva línea al final del archivo" advertencia del compilador

 18
Author: cgp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:49

Esto se origina desde los primeros días cuando se usaban terminales simples. El char de nueva línea se utilizó para activar un 'flush' de los datos transferidos.

Hoy en día, el carácter de nueva línea ya no es necesario. Claro, muchas aplicaciones todavía tienen problemas si la nueva línea no está allí, pero yo consideraría que un error en esas aplicaciones.

Sin embargo, si tiene un formato de archivo de texto donde requiere la nueva línea, obtiene una verificación de datos simple muy barata: si el archivo termina con una línea que no tiene nueva línea al final, sabes que el archivo está roto. Con solo un byte adicional para cada línea, puede detectar archivos rotos con alta precisión y casi sin tiempo de CPU.

 12
Author: Stefan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:41:21

Un caso de uso separado: cuando su archivo de texto está controlado por la versión (en este caso específicamente bajo git, aunque también se aplica a otros). Si se agrega contenido al final del archivo, entonces la línea que anteriormente era la última línea se habrá editado para incluir un carácter de nueva línea. Esto significa que blameing el archivo para averiguar cuándo se editó esa línea por última vez mostrará la adición de texto, no la confirmación antes de que realmente quería ver.

 11
Author: Robin Whittleton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-29 18:55:59

También hay un problema práctico de programación con archivos que carecen de nuevas líneas al final: El read Bash incorporado (no se acerca a otras read implementaciones) no funciona como se esperaba:

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

Esto imprime solo 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 bucle while, por lo que nunca alcanzamos la parte echo $line. Si quieres manejar esta situación, tienes que hacer lo siguiente:

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

Es decir, haga el 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 extra en la salida que no estaba en la entrada.

 10
Author: l0b0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-01 09:06:49

Presumiblemente simplemente que algún código de análisis esperaba que estuviera allí.

No estoy seguro de considerarlo una "regla", y ciertamente no es algo a lo que me adhiera religiosamente. El código más sensible sabrá cómo analizar texto (incluyendo codificaciones) línea por línea (cualquier elección de terminaciones de línea), con o sin una nueva línea en la última línea.

De hecho - si termina con una nueva línea: ¿hay (en teoría) una línea final vacía entre el EOL y el EOF? Uno para reflexionar...

 9
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:19:54

Además de las razones prácticas anteriores, no me sorprendería si los creadores de Unix (Thompson, Ritchie, et al.) o sus predecesores Multics se dieron cuenta de que hay una razón teórica para usar terminadores de línea en lugar de separadores de línea: Con terminadores de línea, puede codificar todos los archivos posibles de líneas. Con los separadores de líneas, no hay diferencia entre un archivo de líneas cero y un archivo que contiene una sola línea vacía; ambos están codificados como un archivo que contiene cero caracter.

Entonces, las razones son:

  1. Porque esa es la forma en que POSIX lo define.
  2. Porque algunas herramientas lo esperan o "se portan mal" sin él. Por ejemplo, wc -l no contará una "línea" final si no termina con una nueva línea.
  3. Porque es simple y conveniente. En Unix, cat simplemente funciona y funciona sin complicaciones. Simplemente copia los bytes de cada archivo, sin necesidad de interpretación. No creo que haya un DOS equivalente a cat. Usar copy a+b c terminará fusionando la última línea de archivo a con la primera línea de archivo b.
  4. Porque un archivo (o flujo) de líneas cero se puede distinguir de un archivo de una línea vacía.
 9
Author: John Wiersba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-25 12:23:09

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

Así expresado por muchos, porque:

  1. Muchos programas no se comportan bien, o fallan sin él.

  2. Incluso los programas que manejan bien un archivo carecen de un final '\n', la funcionalidad de la herramienta puede no cumplir con las expectativas del usuario, lo que puede no estar claro en este caso de esquina.

  3. Programas raramente no permiten final '\n' (No sé de cualquier).


Sin embargo, esto plantea la siguiente pregunta:

¿Qué debe hacer el código con los archivos de texto sin una nueva línea?

  1. Lo más importante - No escriba código que suponga que un archivo de texto termina con una nueva línea. Suponiendo que un archivo se ajusta a un formato conduce a la corrupción de datos, ataques de hackers y bloqueos. Ejemplo:

    // Bad code
    while (fgets(buf, sizeof buf, instream)) {
      // What happens if there is no \n, buf[] is truncated leading to who knows what
      buf[strlen(buf) - 1] = '\0';  // attempt to rid trailing \n
      ...
    }
    
  2. Si se necesita el final final '\n', avise al usuario de su ausencia y de la acción tomar. IOWs, validar el formato del archivo. Nota: Esto puede incluir un límite a la longitud máxima de línea, codificación de caracteres, etc.

  3. Definir claramente, documento, el manejo del código de un final que falta '\n'.

  4. No genere, en la medida de lo posible, un archivo que carezca de la terminación '\n'.

 7
Author: chux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-30 13:59:13

Me he preguntado esto durante años. Pero hoy encontré una buena razón.

Imagine un archivo con un registro en cada línea (por ejemplo: un archivo CSV). Y que la computadora estaba escribiendo registros al final del archivo. Pero de repente se estrelló. Gee fue la última línea completa? (no es una buena situación)

Pero si siempre terminamos la última línea, entonces lo sabremos (simplemente verifique si la última línea está terminada). De lo contrario, probablemente tendríamos que descartar la última línea cada vez, solo para ser seguro.

 6
Author: symbiont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-08 14:22:52

Siempre tuve la impresión de que la regla venía de los días en que analizar un archivo sin una nueva línea final era difícil. Es decir, terminarías escribiendo código donde un final de línea fue definido por el carácter EOL o EOF. Era más simple asumir que una línea terminaba con EOL.

Sin embargo, creo que la regla se deriva de compiladores de C que requieren la nueva línea. Y como se señaló en "No hay nueva línea al final del archivo" advertencia del compilador, #include no agregará una nueva línea.

 3
Author: he_the_great,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:00

Imagine que el archivo está siendo procesado mientras el archivo todavía está siendo generado por otro proceso.

Podría tener que ver con eso? Un indicador que indica que el archivo está listo para ser procesado.

 0
Author: Pippen_001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:36:30

Personalmente me gustan las nuevas líneas al final de los archivos de código fuente.

Puede tener su origen en Linux o en todos los sistemas UNIX. Recuerdo que había errores de compilación (gcc si no me equivoco) porque los archivos de código fuente no terminaban con una nueva línea vacía. ¿Por qué se hizo de esta manera uno se queda a preguntarse.

 -4
Author: User,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:23:08

En mi humilde opinión, es una cuestión de estilo personal y opinión.

En los viejos tiempos, no puse esa nueva línea. Un personaje guardado significa más velocidad a través de ese módem de 14.4 K.

Más tarde, puse esa nueva línea para que sea más fácil seleccionar la línea final usando shift+downarrow.

 -7
Author: Torben Gundtofte-Bruun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-08 12:38:48