A veces abres los dos archivos y no hay ni un carácter distinto, pero diff dice que todas las líneas han cambiado, o que solo difieren la primera o la última. La causa suele ser algún carácter que no se ve en pantalla. Este artículo repasa los tipos de «diferencias invisibles» y cómo resolverlas.
Caracteres de salto de línea: CRLF, LF, CR
Cada sistema operativo tiene su propia convención para terminar las líneas.
| Notación | Bytes | Dónde se usa sobre todo |
|---|---|---|
| LF | 0A (\n) | Linux, macOS y la mayoría de herramientas de desarrollo |
| CRLF | 0D 0A (\r\n) | Muchos programas de Windows, como el Bloc de notas, y protocolos de Internet como el correo electrónico o HTTP |
| CR | 0D (\r) | El Mac OS muy antiguo (anterior a la versión 9) |
Si un archivo se guardó con CRLF y el otro con LF, al comparar por líneas cada línea tiene al final un \r invisible que cuenta como diferencia, y parece que ha cambiado todo. GNU diff puede ignorar esta diferencia con --strip-trailing-cr, y git diff con --ignore-cr-at-eol.
Salto de línea al final del archivo
Según la convención POSIX, las líneas de un archivo de texto terminan en un carácter de salto de línea. Por eso muchas herramientas añaden un salto también tras la última línea, pero algunos editores no lo hacen. En un diff unificado, esta diferencia se muestra así:
@@ -1,2 +1,2 @@ host=example.com-port=8080\ No newline at end of file+port=8080
\ No newline at end of file significa que la línea inmediatamente anterior no termina en salto de línea. En el ejemplo, el contenido es el mismo y solo el archivo nuevo tiene el salto final. Muchas guías de estilo de código exigen un salto de línea al final del archivo, y se puede unificar con el ajuste del editor «insertar salto de línea al final del archivo».
BOM: cuando solo difiere la primera línea
A veces un archivo UTF-8 lleva al principio los tres bytes EF BB BF. Es la marca de orden de bytes (BOM). En UTF-8 el orden de bytes no hace falta, pero algunos programas la añaden como señal de que «este archivo es UTF-8». La BOM no se ve en pantalla, así que si el resultado dice que solo difiere la primera línea, sospecha de ella. Este sitio elimina la BOM inicial al cargar un archivo y, si activas una opción de ignorar espacios, también trata como espacio la BOM (U+FEFF) que haya en el texto pegado.
Tabuladores y espacios, espacios especiales
- Tabuladores y espacios: un tabulador y cuatro espacios pueden verse igual en pantalla, pero son caracteres distintos.
- Espacio de no separación (NBSP, U+00A0): se cuela a menudo en el texto copiado de páginas web o de procesadores de textos.
- Espacio de ancho completo (U+3000): puede aparecer con los métodos de entrada de coreano, japonés o chino.
- Espacio de ancho cero (U+200B): tiene ancho 0 y es completamente invisible, pero cuenta como carácter.
La mayoría de estos caracteres se filtran al activar las opciones de ignorar espacios (aunque los que no se clasifican como espacio, como U+200B, pueden quedar incluso con la opción activada). Si cambias la comparación dentro de la línea a por caracteres, el resaltado te mostrará en qué posición hay un carácter invisible.
Normalización Unicode: el mismo carácter, bytes distintos
Un mismo carácter puede escribirse de más de una forma. La «é» puede ser un único carácter precompuesto (U+00E9) o una «e» (U+0065) seguida del acento agudo combinable (U+0301). Lo mismo ocurre con la sílaba coreana «가», que puede ser un único carácter (U+AC00) o la combinación de la consonante ㄱ (U+1100) y la vocal ㅏ (U+1161). Unicode llama a la primera forma NFC y a la segunda NFD (UAX #15). Los dos textos se ven idénticos en pantalla, pero al compararlos son caracteres distintos. Un ejemplo típico es que los nombres de archivo creados en macOS aparezcan en otros sistemas con las letras y los acentos separados. La solución es convertir ambos lados a NFC antes de comparar.
Unificar los saltos de línea en git
En un repositorio en el que se trabaja desde varios sistemas operativos, puedes unificar los saltos de línea con la configuración de git.
core.autocrlf: contrue, convierte LF en CRLF al hacer checkout y CRLF en LF al hacer commit (sobre todo en Windows). Coninput, solo convierte CRLF en LF al hacer commit..gitattributes: define las reglas por repositorio, así que el resultado es el mismo aunque cada persona tenga una configuración distinta.
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
text=auto normaliza a LF, dentro del repositorio, los saltos de línea de los archivos que git considera de texto, y eol=lf o eol=crlf fijan el salto de línea que se usa al extraerlos a la carpeta de trabajo.
Pruébalo en esta herramienta
La opción Normalizar fin de línea del comparador de textos está activada por defecto: trata CRLF, CR y LF como el mismo salto de línea e ignora si el archivo termina o no con salto de línea. Si la desactivas, el CR al final de línea se muestra como ␍ y la línea sin salto final como ∅, de modo que puedes ver las diferencias que antes eran invisibles. Las opciones relacionadas con los espacios se explican en Opciones para ignorar espacios, y el significado de los símbolos del resultado, en Cómo leer un diff unificado.