crlf - convert windows text file to linux
¿Por qué deberían los archivos de texto terminar con una nueva línea? (17)
Supongo que todos aquí están familiarizados con el dicho 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é?
¿Por qué deberían los archivos (de texto) terminar con una nueva línea?
Como bien expresado por muchos, porque:
Muchos programas no se comportan bien, o fallan sin él.
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.Los programas rara vez rechazan el
''/n''
final (no conozco ninguno).
Sin embargo, esto plantea la siguiente pregunta:
¿Qué debería hacer el código con respecto a los archivos de texto sin una nueva línea?
Lo más importante: no escriba código que asuma que un archivo de texto termina con una nueva línea . Suponiendo que un archivo se ajuste a un formato, se pueden producir daños en los datos, ataques de piratas informáticos 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 ... }
Si es necesario el final
''/n''
, avise al usuario de su ausencia y de la acción tomada. 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.Defina claramente, documente, el manejo del código de un final faltante
''/n''
.No genere , como sea posible, un archivo que carezca de la terminación
''/n''
.
Además de las razones prácticas anteriores, no me sorprendería si los creadores de Unix (Thompson, Ritchie, et al.) O sus predecesores de Multics se dieran cuenta de que existe una razón teórica para usar terminadores de línea en lugar de separadores de línea: Con línea Terminadores, puede codificar todos los archivos de líneas posibles. Con los separadores de línea, no hay diferencia entre un archivo de cero líneas y un archivo que contiene una sola línea vacía; ambos están codificados como un archivo que contiene cero caracteres.
Entonces, las razones son:
- Porque así lo define POSIX.
- 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. - Porque es simple y conveniente. En Unix, el
cat
solo funciona y funciona sin complicaciones. Simplemente copia los bytes de cada archivo, sin necesidad de interpretación. No creo que haya un equivalente de DOS alcat
. Usandocopy a+bc
terminará fusionando la última línea del archivoa
con la primera línea del archivob
. - Debido a que un archivo (o flujo) de cero líneas se puede distinguir de un archivo de una línea vacía.
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
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 como parte del estándar C. (sección 5.1.1.2 aparentemente)
"No hay nueva línea al final del archivo" advertencia del compilador
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 nueva línea.
GCC advierte sobre esto no porque no pueda procesar el archivo, sino porque tiene que hacerlo como parte del estándar.
El estándar de 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 un carácter de barra diagonal inversa.
Dado que esta es una cláusula de "deber", debemos emitir un mensaje de diagnóstico por 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 .
En mi humilde opinión, es una cuestión de estilo personal y de 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.4K.
Más tarde, coloco esa nueva línea para que sea más fácil seleccionar la línea final usando shift + downarrow.
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 no <newline> más un carácter <newline> 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 <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 los {LINE_MAX} bytes de longitud, incluido el carácter <newline>. 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 opera 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 este momento, podemos deducir que la única vez que potencialmente nos encontraremos con cualquier tipo de problemas es si tratamos el concepto de una línea de un archivo o un archivo como un archivo de texto (ya que un archivo de texto es una organización de cero). o más líneas, y una línea que sepamos debe terminar con una <newline>).
Caso en punto: wc -l filename
.
Del manual de wc
leemos:
Una línea se define como una cadena de caracteres delimitados por un carácter <newline>.
¿Cuáles son las implicaciones para los archivos JavaScript, HTML y CSS que los archivos de texto ?
En los navegadores, los IDE modernos y otras aplicaciones front-end, no hay problemas con omitir EOL en EOF. Las aplicaciones analizarán los archivos correctamente. Tiene que hacerlo ya que no todos los sistemas operativos cumplen con el estándar POSIX, por lo que no sería práctico que las herramientas que no son del sistema operativo (por ejemplo, los navegadores) manejen los archivos de acuerdo con el estándar POSIX (o cualquier estándar a 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 está ejecutando en un sistema operativo UNIX.
En este punto, podemos decir con confianza que saltarse EOL en EOF es seguro cuando se trata de JS, HTML, CSS en el lado del cliente. En realidad, podemos afirmar que la reducción de cualquiera de estos archivos, que no contenga <newline> es seguro.
Podemos ir un paso más allá y decir que, en lo que respecta a NodeJS, tampoco puede cumplir con el estándar POSIX, ya que puede ejecutarse en entornos que no cumplen con POSIX.
¿Qué nos queda entonces? Herramientas de nivel de sistema.
Esto significa que los únicos problemas que pueden surgir son con herramientas que hacen un esfuerzo por 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 todos los shells se adherirán automáticamente a POSIX. Bash, por ejemplo, no establece por defecto el comportamiento de POSIX. Hay un interruptor para habilitarlo: POSIXLY_CORRECT
.
Reflexione sobre el valor de EOL en <newline>: http://www.rfc-editor.org/EOLstory.txt
Manteniéndonos en la pista de herramientas, para todos los propósitos y propósitos prácticos, consideremos esto:
Trabajemos con un archivo que no tenga EOL. Al momento de escribir esto, 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 comenzar cada archivo JavaScript con un punto y coma.
Como alguien más mencionó en este hilo: ¿qué sucede si desea cat
dos archivos cuya salida se convierte en una sola línea en lugar de dos? En otras palabras, el cat
hace lo que se supone que debe hacer.
El man
de cat
solo menciona la entrada de lectura hasta EOF, no <newline>. Tenga en cuenta que el interruptor -n
de cat
también imprimirá una línea terminada no <newline> (o línea incompleta ) como una línea , ya que el recuento comienza en 1 (según el man
).
-n Numera las líneas de salida, comenzando en 1.
Ahora que entendemos cómo POSIX define una línea , este comportamiento se vuelve ambiguo o, en realidad, no cumple.
Comprender el propósito y el cumplimiento de una herramienta dada ayudará a determinar qué tan crítico 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
podría hacer awk ''{x++}END{ print x}'' filename
, y tenga la seguridad de que el éxito de la tarea no se verá afectado por un archivo que podríamos querer procesar y que no escribimos ( por ejemplo, una biblioteca de terceros, como la JS minificada que curvamos d), a menos que nuestra intención fuera realmente contar las líneas en el sentido compatible con POSIX.
Conclusión
Habrá muy pocos casos de uso en la vida real en los que omitir EOL en EOF para ciertos archivos de texto como JS, HTML y CSS tendrá un impacto negativo, en todo caso. Si confiamos en que <newline> esté presente, estamos restringiendo la confiabilidad de nuestras herramientas solo a los archivos que creamos 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.
Esto se origina desde los primeros días en que se utilizaron terminales simples. La nueva línea de caracteres se utilizó para desencadenar un "vaciado" de los datos transferidos.
Hoy en día, la nueva línea de caracteres ya no es necesaria. Claro, muchas aplicaciones todavía tienen problemas si la nueva línea no está allí, pero consideraría que hay un error en esas aplicaciones.
Sin embargo, si tiene un formato de archivo de texto donde necesita la nueva línea, obtendrá una verificación de datos simple muy barata: si el archivo finaliza con una línea que no tiene nueva línea al final, sabe 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.
Imagine que el archivo se está procesando mientras el archivo aún está siendo generado por otro proceso.
Podría tener que ver con eso? Una bandera que indica que el archivo está listo para ser procesado.
Me he preguntado esto por años. Pero me encontré con una buena razón hoy.
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, 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 estar seguros.
Personalmente, me gustan las nuevas líneas al final de los archivos de código fuente.
Puede tener su origen con Linux o con todos los sistemas UNIX. Recuerdo que hay errores de compilación (gcc si no me equivoco) porque los archivos de código fuente no terminaron con una nueva línea vacía. ¿Por qué se hizo de esta manera uno se deja preguntarse.
Porque así es como el estándar POSIX define una línea :
- 3.206 linea
- Una secuencia de cero o más caracteres no <newline> más un carácter <newline> 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.
Esta guía tiene al menos una gran ventaja al trabajar en un emulador de terminal: todas las herramientas de 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 al de uno sin:
$ more a.txt
foo$ more b.txt
bar
$ more c.txt
baz
$ cat *.txt
foobar
baz
Y, como lo demuestra el ejemplo anterior, cuando se muestra el archivo en la línea de comandos (por ejemplo, a través de more
), un archivo terminado en nueva línea da como resultado una visualización correcta. Un archivo terminado incorrectamente podría estar confuso (segunda línea).
Para mantener la coherencia, es muy útil seguir esta regla, ya que de lo contrario se incurrirá en un trabajo adicional al tratar con las herramientas Unix predeterminadas.
Ahora, en sistemas que no cumplen con POSIX (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 está separado por nuevas líneas" (note el énfasis). Esto es completamente válido. Sin embargo, para los datos estructurados (por ejemplo, el código de programación) hace que el análisis sea un poco más complicado: generalmente significa que los analizadores deben ser reescritos. Si originalmente se escribió un analizador con la definición POSIX en mente, entonces podría ser más fácil modificar la secuencia del token en lugar del analizador; en otras palabras, agregue un token de "nueva línea artificial" al final de la entrada.
Presumiblemente simplemente que algún código de análisis esperaba que estuviera allí.
No estoy seguro de que lo consideraría una "regla", y ciertamente no es algo a lo que me adhiera religiosamente. El código más sensible sabrá cómo analizar el texto (incluidas las codificaciones) línea por línea (cualquier elección de finales de línea), con o sin una nueva línea en la última línea.
De hecho, si terminas con una nueva línea: ¿hay (en teoría) una línea final vacía entre la EOL y la EOF? Uno para reflexionar ...
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 concatenar dos archivos de texto haría que la última línea de la primera se ejecute en la primera línea de la segunda.
Además, un editor puede verificar durante la carga 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, ...) "olvidaron" esa EOL final, que no fue muy apreciada .
No solo eso, sino que interpretaron esa EOL final de manera incorrecta, 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 ''correcto'' con un editor de texto de buen comportamiento como vim, en comparación con abrirlo en uno de los editores anteriores. Se muestra 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
Siempre tuve la impresión de que la regla provenía de los días en que el análisis de un archivo sin una nueva línea final era difícil. Es decir, terminaría escribiendo código donde el carácter EOL o EOF definió el final de la línea. Era más simple suponer que una línea terminaba con EOL.
Sin embargo, creo que la regla se deriva de los compiladores de C que requieren la nueva línea. Y como se señaló en la advertencia del compilador "Sin nueva línea al final del archivo" , #include no agregará una nueva línea.
También hay un problema práctico de programación con los archivos que carecen de nuevas líneas al final: la función Bash integrada (no sé acerca de otras implementaciones de read
) no funciona como se esperaba:
printf $''foo/nbar'' | while read line
do
echo $line
done
Esto imprime sólo 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 de la echo $line
. Si quieres manejar esta situación, debes hacer lo siguiente:
while read line || [ -n "${line-}" ]
do
echo $line
done < <(printf $''foo/nbar'')
Es decir, haga el echo
si la 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.
Un caso de uso separado: cuando su archivo de texto está controlado por versión (en este caso específicamente bajo git, aunque también se aplica a otros). Si el contenido se agrega al final del archivo, entonces la línea que fue anteriormente la última línea se habrá editado para incluir un carácter de nueva línea. Esto significa que blame
al archivo para averiguar cuándo se editó por última vez esa línea mostrará el texto agregado, no la confirmación antes de lo que realmente quería ver.