El mojibake de Windows no se produce porque el japonés sea difícil. En la mayoría de los casos, la causa es haber leído la misma secuencia de bytes con un código de caracteres distinto o haber guardado el resultado de una lectura errónea con otro código de caracteres.
Esto se agrava especialmente al cruzar entre Windows y Linux: en el lado de Windows conviven varios contextos —CP932, UTF-8, UTF-16, la página de códigos de la consola, las diferencias entre versiones de PowerShell—, mientras que en el lado de Linux casi todo se procesa bajo el supuesto de UTF-8. Esa combinación hace que desajustes de suposiciones normalmente invisibles salgan a la luz de golpe.
Más que una cuestión de lo complicado que es procesar japonés, esto es una cuestión de si se logra alinear bajo qué suposición se están tratando los bytes. En este artículo ordenamos la codificación de caracteres de Windows desde el punto de vista de «por qué se produce el mojibake» y resumimos, con un enfoque práctico, los puntos donde aumentan los incidentes al combinarlo con Linux.
Este artículo está dirigido a quienes generan archivos CSV, registros o archivos de configuración en Windows y los pasan al lado de Linux (o hacen lo contrario), y quieren aprender a diagnosticar y recuperarse del mojibake por sí mismos. No se presupone conocimiento de ningún lenguaje o framework en particular. Los ejemplos de comandos usan iconv y PowerShell.
1. Lo primero que hay que entender
Antes de entrar en detalle, estos son los seis puntos clave:
- El mojibake no es un problema de «caracteres», sino de «cómo se interpretó la secuencia de bytes».
- En Windows coexisten el mundo Unicode y el de las páginas de código heredadas (legacy), de modo que incluso dentro de un mismo equipo las suposiciones cambian según el contexto.
- En el lado de Linux predomina fuertemente el supuesto de UTF-8, por lo que mezclar CP932 o UTF-16 procedentes de Windows tiende a provocar incidentes.
- Conviene distinguir entre la etapa en la que solo se ha deteriorado la visualización y la etapa en la que ya se ha guardado un contenido dañado.
- Lo más seguro es elegir UTF-8 como primera opción para los textos nuevos y mantener los archivos legacy existentes tal cual hasta que exista una tarea explícita de migración.
- La codificación del archivo, la codificación del editor, la página de códigos de la consola y el formato interno de cadenas de la aplicación son cosas distintas. Confundirlas hace que la investigación se pierda.
Decir simplemente «se produjo mojibake en Windows» no permite identificar la causa. Como mínimo hay que determinar cuál de los siguientes elementos está desalineado:
- El código de caracteres del propio archivo
- El código de caracteres usado al guardar
- La interpretación del editor
- La página de códigos de entrada/salida de la consola
- El formato interno de cadenas de la aplicación
- El locale del lado de Linux y la codificación asumida
1.1 Términos que aparecen a continuación
A continuación resumimos brevemente las abreviaturas que aparecerán en el texto sin más explicación.
| Término | Nombre completo | Significado en este artículo |
|---|---|---|
| BOM | Byte Order Mark | Marca de unos pocos bytes colocada al inicio del archivo. Indica al lector qué codificación Unicode se está usando y, en UTF-16, también el orden de los bytes. En UTF-8 es opcional |
| code page (página de códigos) | - | Mecanismo mediante el cual Windows identifica con un número «con qué código de caracteres legacy interpretar el texto». CP932, usado en el Windows japonés, es uno de esos números |
| ANSI | - | Forma en que Windows se refiere a la «página de códigos activa en ese momento». Su identidad real varía según el entorno; en un entorno japonés es CP932 |
| locale | - | Configuración que agrupa los valores predeterminados de idioma, región y código de caracteres. En Linux se especifica mediante LANG o LC_ALL e incluye la codificación, como en ja_JP.UTF-8 |
| WSL | Windows Subsystem for Linux | Mecanismo para ejecutar Linux sobre Windows. Como las suposiciones de Windows y de Linux conviven dentro de un mismo equipo, es un lugar propenso a los incidentes descritos en este artículo |
| ETL | Extract / Transform / Load | Proceso que extrae datos, los transforma y los vuelve a escribir. Como en el camino se vuelve a leer y a guardar el archivo, es un punto donde la codificación puede cambiar |
2. Qué es realmente el mojibake
En el fondo, el mojibake es bastante simple.
- Una cadena de texto se codifica (encode) con algún código de caracteres para convertirla en una secuencia de bytes
- Esa secuencia de bytes se decodifica (decode) con algún código de caracteres para volver a convertirla en una cadena de texto
- Si las suposiciones usadas al codificar y al decodificar no coinciden, el resultado se lee como una cadena de texto distinta
Por ejemplo, si guardamos あ en UTF-8, la secuencia de bytes resultante es la siguiente.
E3 81 82
Si esta secuencia de bytes se lee como UTF-8, se obtiene あ, pero si se lee en el contexto de CP932 aparece como una cadena distinta, algo como 縺�. Esto es el mojibake.
Lo importante es que lo que ocurre aquí no es que «el japonés se haya roto», sino simplemente que la interpretación de los mismos bytes se ha desalineado.
Veamos también el caso inverso. Si guardamos あ en CP932, la secuencia de bytes queda así.
82 A0
Si intentamos leer esta secuencia de bytes como UTF-8, ni 0x82 ni 0xA0 son válidos como primer byte de una secuencia UTF-8, por lo que ambos se convierten en caracteres de sustitución y el resultado se ve como ��. Desde el punto de vista de UTF-8, en realidad ni siquiera llega a constituir un carácter.
Un ejemplo un poco más largo muestra mejor la particularidad. Si guardamos 日本語 en CP932, la secuencia de bytes es la siguiente.
93 FA 96 7B 8C EA
Al leer esto como UTF-8 se obtiene algo como ���{��. Lo que conviene observar aquí es que solo el cuarto byte, 0x7B, pasa sin cambios como el carácter ASCII {. Como el segundo byte de un carácter CP932 a veces cae dentro del rango ASCII, en el resultado del mojibake se cuelan símbolos como { o \.
En otras palabras, los síntomas difieren según la dirección.
| Secuencia de bytes real | Suposición del lector | Aspecto resultante |
|---|---|---|
| UTF-8 | CP932 | Aparecen kanji o katakana que parecen plausibles, como 縺 |
| CP932 | UTF-8 | El texto queda plagado del carácter de sustitución �, con algún símbolo ASCII como { mezclado ocasionalmente |
Esta asimetría es útil de recordar porque permite hacer una primera hipótesis: «si aparecen kanji ilegibles, probablemente se está leyendo UTF-8 como CP932»; «si el texto está lleno de caracteres de sustitución, probablemente se está leyendo CP932 como UTF-8». Conocerla acelera el diagnóstico.
2.1 Si solo se ha deteriorado la visualización, a veces todavía se puede recuperar
El mojibake tiene una etapa en la que aún es reversible. Por ejemplo, si los bytes originales no han cambiado, en algunos casos basta con volver a abrir el archivo con la codificación correcta para recuperarlo.
Por el contrario, lo peligroso es un flujo como el siguiente.
- Se lee erróneamente un archivo UTF-8 como si fuera CP932
- En pantalla aparece algo como
縺� - Se guarda tal cual «la cadena que se ve en pantalla»
- Se pierden los bytes UTF-8 originales
Una vez alcanzada esta etapa, ya no se trata de un simple deterioro visual, sino de corrupción de datos.
2.2 Es aún más peligroso reducir «caracteres irrepresentables» a una página de códigos estrecha
Otro incidente típico ocurre al convertir una cadena Unicode a una página de códigos legacy como CP932.
Por ejemplo, si el texto contiene un carácter que no existe en la página de códigos de destino, puede ocurrir lo siguiente:
- se sustituye por
? - se inserta el carácter de sustitución
� - se convierte a otro carácter parecido
- la conversión falla directamente
Este tipo de incidente no debe evaluarse solo por si el texto se puede leer o no, sino por si, al convertirlo de ida y vuelta, se recupera el original. Un carácter que ya se ha perdido no se puede restaurar aunque después se conozca la codificación correcta.
3. Por qué Windows tiende a complicarse tanto
Windows no es complicado simplemente porque sea antiguo. Lo es porque el mundo de Unicode y el mundo de las páginas de código legacy siguen coexistiendo hoy en día.
3.1 En la API de Windows coexisten la familia Unicode y la familia de páginas de código
La API de Windows se divide, a grandes rasgos, en dos familias:
- Familia
W: wide character. La familia que trata Unicode mediante UTF-16 - Familia
A: la familia de páginas de código, llamada ANSI
Es decir, dentro de Windows existen desde el principio dos caminos: «tratar el texto como Unicode» y «tratarlo según la página de códigos activa en ese momento». Por eso, incluso dentro de un mismo Windows, las suposiciones cambian según qué API o qué herramienta se haya usado.
3.2 «El japonés de Windows» no es uno solo
En el entorno del japonés en Windows, en la práctica suelen mezclarse estos cuatro elementos:
- CP932: aparece con frecuencia en textos legacy del Windows japonés
- UTF-8: cada vez más presente en activos de texto nuevos, en la web y en entornos multiplataforma
- UTF-16LE: sigue apareciendo con normalidad en el contexto de herramientas y API de Windows
- La página de códigos de la consola: una capa aparte que afecta a la entrada/salida de
cmd.exey de algunas herramientas de consola
Lo importante aquí es que ejecutar chcp 65001 no convierte automáticamente el archivo en UTF-8. Cambiar la página de códigos de la consola y saber qué bytes tiene realmente un archivo existente son cuestiones distintas.
Por cierto, es habitual llamar de forma imprecisa «Shift_JIS» al texto legacy del Windows japonés, pero en la práctica es mejor tener presente el nombre CP932, porque así la conversación se desvía menos. Como mínimo, deja claro que se está hablando de una codificación legacy japonesa originada en Windows.
3.3 El nombre del archivo y su contenido son cuestiones distintas
Cuando en Windows el nombre de archivo en japonés se ve con normalidad, es fácil pensar que «entonces el contenido también estará bien». Ahí está el peligro.
- La capa que trata las rutas y los nombres de archivo
- La capa que lee el contenido del archivo
- La capa que lo muestra en la consola
Estas tres capas son distintas.
Por ejemplo, aunque una ruta en japonés se maneje sin problemas, si el contenido del archivo se guardó en CP932 y se lee en el lado de Linux como UTF-8, se dañará. A la inversa, aunque el contenido del archivo esté en UTF-8, si la página de códigos de la consola no coincide, solo se deteriorará la visualización.
Si representamos la relación entre estas capas en un diagrama, queda así.
flowchart LR
accTitle: Capas de interpretación entre quien escribe el archivo y quien lo lee
accDescr: Diagrama que muestra cómo la secuencia de bytes del archivo es el único hecho objetivo, y cómo cuatro lectores independientes (editor, consola, aplicación interna y lado Linux) pueden interpretarla de forma distinta, produciendo cada uno un síntoma diferente
W["Quien escribe<br/>aplicación / editor / script"] --> FB["Secuencia de bytes del archivo<br/>esto es lo único objetivo"]
FB --> R1["Lector A: editor<br/>detección automática o encoding indicado"]
FB --> R2["Lector B: consola<br/>página de códigos de entrada/salida"]
FB --> R3["Lector C: interior de la aplicación<br/>encoding predeterminado de la librería"]
FB --> R4["Lector D: lado Linux<br/>supone UTF-8 según el locale"]
R1 --> S1["Solo se deteriora la visualización<br/>se convierte en corrupción al volver a guardar"]
R2 --> S2["Solo se deteriora la visualización<br/>el archivo está intacto"]
R3 --> S3["El resultado del procesamiento se daña<br/>se propaga aguas abajo"]
R4 --> S4["Error de decodificación o carácter de sustitución"]
Hay dos puntos que conviene observar. El primero es distinguir si lo que está dañado es la secuencia de bytes central o el lector de la derecha. El segundo es que los cuatro lectores de la derecha son independientes entre sí, de modo que confirmar uno no garantiza los otros tres. Por esta estructura no se sostiene la idea de que «si se leyó bien en la consola, también estará bien en el editor».
3.4 Tampoco están alineados los valores predeterminados de PowerShell y herramientas relacionadas
Algo que aumenta los incidentes en Windows de forma poco visible es que aunque se crea estar «escribiendo el mismo texto», los bytes de salida difieren según la ruta usada.
Conviene prestar especial atención a lo siguiente:
- Windows PowerShell 5.1 no tiene una codificación predeterminada coherente
- Algunos cmdlets y redirecciones generan UTF-16LE
- Otras rutas usan la página de códigos ANSI activa
- A partir de PowerShell 7, UTF-8 no BOM es el valor predeterminado
En otras palabras, decir solo «texto generado con PowerShell» no determina la codificación. Hay que fijarse hasta en qué versión, qué cmdlet y qué ruta de escritura se usó.
Qué ruta produce qué bytes está documentado en about_Character_Encoding de Microsoft Learn. Si extraemos solo las más usadas, queda así:
| Ruta de escritura | Predeterminado en Windows PowerShell 5.1 | Predeterminado en PowerShell 7 |
|---|---|---|
Out-File, >, >> |
UTF-16LE (con BOM) | UTF-8 no BOM |
Set-Content, Add-Content (archivo nuevo o vacío) |
ANSI = página de códigos activa. En un entorno japonés, CP932 | UTF-8 no BOM |
Export-Csv |
ASCII. Los caracteres no ASCII se pierden | UTF-8 no BOM |
Export-Clixml, New-ModuleManifest |
UTF-16LE | UTF-8 no BOM |
New-Item -Type File -Value |
UTF-8 no BOM | UTF-8 no BOM |
Start-Transcript |
UTF-8 con BOM | UTF-8 no BOM |
También hay diferencias en el lado de la lectura. Al leer un archivo sin BOM, Get-Content en 5.1 lo trata como ANSI, mientras que Import-Csv y Select-String lo tratan como UTF-8. Es decir, las suposiciones se dividen incluso dentro de una misma sesión.
En la práctica, lo que más suele doler es que aunque se «escriba el mismo texto», Out-File produce UTF-16LE mientras que Set-Content produce CP932. El «texto de aspecto binario con muchos bytes NUL mezclados» que se menciona en 4.3 casi siempre proviene del comportamiento predeterminado de > o de Out-File.
Otro detalle: en 5.1, incluso especificando -Encoding UTF8, el resultado lleva BOM. Si en 5.1 se quiere escribir UTF-8 sin BOM, hay que hacerlo desde el lado de .NET.
# Escribir UTF-8 no BOM en Windows PowerShell 5.1
$text = "日本語を含む本文"
[System.IO.File]::WriteAllText(
"C:\work\output.txt", $text,
(New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false))
Poner el argumento de UTF8Encoding en $false es lo que indica no añadir BOM. En PowerShell 7, se obtiene el mismo resultado con -Encoding utf8NoBOM.
4. Incidentes típicos al combinar con Linux
No es raro que algo que funcionaba más o menos bien en Windows por sí solo se rompa en cuanto se introduce Linux en el proceso. La razón es sencilla: en el lado de Linux predomina fuertemente el supuesto de UTF-8.
4.1 Linux lee como UTF-8 un texto que Windows guardó en CP932
Es el incidente más común.
- Una aplicación legacy de Windows o un flujo de operación antiguo escribe CSV, TXT o logs en CP932
- El script o la herramienta del lado de Linux los lee bajo el supuesto de UTF-8, siguiendo el locale
- El resultado es un error de decodificación,
�o cadenas sin sentido
En este caso, la herramienta del lado de Linux no tiene la culpa: la causa raíz es que los bytes recibidos no llevaban adjunto un acuerdo sobre la codificación.
4.2 Windows interpreta como ANSI un UTF-8 no BOM creado en Linux o VS Code
También existe el incidente en la dirección opuesta.
- Se crea un script, un archivo de configuración o un texto en UTF-8 no BOM desde Linux o VS Code
- Windows PowerShell 5.1 o una herramienta legacy interpreta el archivo sin BOM como si estuviera en la página de códigos ANSI
- Solo se dañan las líneas que contienen japonés o caracteres no ASCII
Aquí suele culparse a UTF-8, pero la causa real es que hay un lector mezclado en el proceso que no logra inferir correctamente un UTF-8 sin BOM.
4.3 Windows escribe UTF-16LE y en Linux «no parece texto»
Esto también sucede con bastante frecuencia.
- Algunas salidas de Windows PowerShell 5.1 o herramientas legacy escriben en UTF-16LE
- Las herramientas de texto del lado de Linux esperan un flujo de 1 byte por carácter en UTF-8
- El resultado es un «texto con aspecto binario», lleno de bytes NUL
UTF-16LE en sí mismo no es malo. Sin embargo, en muchos casos no encaja con la suposición de pasarlo directamente a una herramienta de procesamiento de texto de Linux.
4.4 La presencia o ausencia de BOM también genera fricción
El BOM no es la codificación en sí misma, pero en la práctica tiene un impacto considerable.
- Algunas herramientas del lado de Windows se benefician de que exista un BOM
- Algunas herramientas del lado de Linux tratan el BOM como bytes sobrantes al inicio del archivo
- El resultado es que solo se daña el inicio de la primera columna o la primera línea, se añade basura invisible o los resultados de una comparación no coinciden
Esto es especialmente cierto en UTF-8: aunque se hable del mismo UTF-8, los bytes son distintos según haya o no BOM. Decir simplemente «lo pasamos a UTF-8» deja la regla operativa solo a medio definir.
4.5 Confiar en el aspecto de la consola lleva a confusión
Al cruzar entre Windows y Linux, otro punto peligroso es la consola.
- La consola de Windows tiene una página de códigos de entrada/salida
- El terminal de Linux suele funcionar bajo el supuesto de un locale UTF-8
- Al pasar por WSL, SSH, contenedores o CI, aumentan las rutas de visualización
En esta situación, juzgar que «se leyó bien en la consola, así que el archivo también está bien» o que «se vio mal en la consola, así que el archivo está dañado» es fácil que lleve a error. Es más seguro comprobar por separado si lo que está dañado es lo que se ve o los bytes que están guardados.
4.6 Los incidentes típicos resumidos en una tabla
| Situación | Bytes reales | Suposición del lector | Síntoma típico |
|---|---|---|---|
| CSV guardado por una aplicación legacy de Windows | CP932 | El lado de Linux supone UTF-8 | �, error de decodificación, japonés sin sentido |
| Archivo creado en Linux / VS Code | UTF-8 no BOM | Windows PowerShell 5.1 lo trata como ANSI | Solo se dañan las líneas en japonés |
| Parte de la salida de Windows PowerShell 5.1 | UTF-16LE o ANSI | El lado de Linux espera texto UTF-8 | Bytes NUL mezclados, comportamiento de aspecto binario |
| Archivo UTF-8 with BOM | UTF-8 + BOM | Las herramientas de tipo Unix suponen UTF-8 plano | Solo se daña la primera columna, se añaden caracteres sobrantes |
| Confiar solo en la visualización de la consola | El archivo y la consola tienen suposiciones distintas | Quien investiga juzga solo por lo que ve | Se falla en el diagnóstico de la causa |
5. El diagnóstico del mojibake se resuelve con estas cuatro preguntas
Cuando la investigación del mojibake se estanca, lo más rápido es volver a estas cuatro preguntas.
5.1 ¿Cuáles son los bytes originales?
Lo primero que hay que mirar es «qué bytes tiene ahora mismo este archivo». Hace falta el hábito de fijarse en los bytes, no en la apariencia.
- ¿Es UTF-8?
- ¿Es UTF-8 with BOM?
- ¿Es CP932?
- ¿Es UTF-16LE?
- ¿Se ha vuelto a guardar en algún punto intermedio y ha pasado a ser otra cosa?
5.2 ¿Quién lo escribió primero y bajo qué suposición?
A continuación, hay que identificar al «primer escritor».
- ¿Una aplicación legacy de Windows?
- ¿PowerShell 5.1 o 7?
- ¿Un script de Linux?
- ¿VS Code?
- ¿Una exportación proveniente de Excel?
- ¿Algún middleware, proceso por lotes o CI?
Si esto queda ambiguo, deducir la codificación se convierte en cuestión de suerte.
5.3 ¿Quién lo está leyendo ahora y bajo qué suposición?
No basta con el escritor: también hace falta conocer la suposición del lector.
- ¿El editor está usando detección automática?
- ¿PowerShell está mirando el BOM?
- ¿El lado de Linux lo trata como UTF-8 siguiendo el locale?
- ¿La librería está usando su codificación predeterminada?
- ¿Se ha especificado explícitamente
Encoding.UTF8ocp932?
El mojibake se origina casi siempre aquí.
5.4 ¿Ya se ha guardado el contenido leído erróneamente?
Por último, hay que confirmar si el daño se ha quedado solo en la visualización.
- ¿Los bytes siguen siendo los originales?
- ¿Alguien ha guardado el contenido que se veía dañado?
- ¿No hay
?ni�en el diff? - ¿No se ha reescrito todo el texto con otra codificación?
Si se responden estas cuatro preguntas, casi siempre queda claro el origen del problema.
6. Cómo reparar un archivo dañado
Una vez identificada la causa, sigue la recuperación. Lo primero que hay que determinar aquí es si los bytes originales todavía existen.
- Los bytes originales existen: se puede recuperar leyéndolos con la codificación correcta y volviendo a escribirlos con la codificación deseada. Este capítulo describe ese procedimiento
- Ya se guardó el resultado de una lectura errónea: solo queda recuperarlo desde una copia de seguridad o desde el historial de Git. Los caracteres que se convirtieron en
�o en?no se pueden restaurar aunque después se conozca la codificación correcta
Por eso, lo primero que hay que hacer es sacar una copia del archivo con el que se va a trabajar. Realice la conversión sobre la copia y no toque el archivo original.
6.1 Si está en el lado de Linux, use iconv
Para convertir un archivo CP932 a UTF-8, lo más directo es iconv.
# CP932 -> UTF-8
iconv -f CP932 -t UTF-8 input.csv > output.csv
Si se indica mal la codificación de origen, el proceso se detiene a mitad de camino, así:
iconv: illegal input sequence at position 0
El propio hecho de que se detenga ya es información de que «no es esa codificación», así que lo más rápido es ir probando otros candidatos. A la inversa, si no se detiene sea cual sea la codificación que se pruebe, es posible que el archivo esté compuesto únicamente de ASCII.
Para convertir de UTF-16LE a UTF-8 se usa -f UTF-16LE. Sin embargo, si se convierte explícitamente con -f UTF-16LE un archivo que lleva BOM, el BOM puede quedar en la salida como el carácter U+FEFF. Si prefiere dejar que iconv se encargue del BOM, use -f UTF-16 y compruebe siempre el primer carácter después de la conversión.
6.2 Si está en el lado de Windows, use PowerShell
A partir de PowerShell 6.2, se puede pasar directamente un número de página de códigos a -Encoding. CP932 es el 932.
# PowerShell 6.2 en adelante. CP932 -> UTF-8 no BOM
Get-Content -Path .\input.csv -Encoding 932 |
Set-Content -Path .\output.csv -Encoding utf8NoBOM
A la inversa, si se trata de pasar un archivo UTF-8 creado en el lado de Linux a un destinatario que solo puede leer CP932, se hace así:
Get-Content -Path .\input.csv -Encoding utf8 |
Set-Content -Path .\output.csv -Encoding 932
Sin embargo, esta forma de escribirlo tiene dos efectos secundarios.
Get-Contentdivide el contenido línea por línea ySet-Contentvuelve a añadir un salto de línea después de cada una. Es decir, el carácter de salto de línea termina siendo unificado- Aunque el archivo original no termine en un salto de línea, la salida sí lo llevará
Si se quiere conservar los bytes incluyendo los saltos de línea, hay que tratar el archivo completo sin dividirlo en líneas.
# CP932 -> UTF-8 no BOM conservando los saltos de línea tal cual
$text = [System.IO.File]::ReadAllText(
"C:\work\input.csv", [System.Text.Encoding]::GetEncoding(932))
[System.IO.File]::WriteAllText(
"C:\work\output.csv", $text,
(New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false))
GetEncoding(932) se puede usar directamente en Windows PowerShell 5.1. En entornos de PowerShell 7 donde esto genera una excepción, ejecute primero lo siguiente una vez antes de usarlo.
[System.Text.Encoding]::RegisterProvider(
[System.Text.CodePagesEncodingProvider]::Instance)
Si se añade o no BOM se decide según las necesidades del destinatario, como se explica en 7.1. En el ejemplo anterior, poniendo el argumento de UTF8Encoding en $true se obtiene una salida con BOM.
6.3 Qué comprobar siempre después de convertir
Una conversión no termina solo porque «no haya dado error». Como mínimo, hay que revisar lo siguiente:
- Si 2 o 3 líneas representativas en japonés se leen correctamente
- Si no han aumentado los
?o�. Si han aumentado, ese carácter ya se ha perdido - Si el número de líneas no ha cambiado
- Si el BOM y el salto de línea coinciden con lo que espera el destinatario
- Si el tamaño del archivo no ha cambiado de forma extrema. Al pasar de CP932 a UTF-8, la parte en japonés pasa de 2 a 3 bytes, así que es normal que el tamaño aumente un poco
El segundo punto es especialmente importante. Al convertir a CP932 un texto UTF-8 que contiene caracteres que no existen en CP932, siempre se pierde información. Como se explicó en 2.2, compruebe también si, al convertir de ida y vuelta, se recupera el original.
6.4 Separe la conversión masiva como una «tarea de migración»
Por último, un comentario sobre la operación diaria. Reparar un único archivo dañado y migrar todo un repositorio de CP932 a UTF-8 son tareas distintas. Como se indica en 7.2 del siguiente capítulo, esta última no debe hacerse de paso al corregir alguna funcionalidad cotidiana: planifíquela como una tarea de migración independiente.
7. Reglas operativas para reducir incidentes
De aquí en adelante, el enfoque es más práctico. En proyectos que cruzan entre Windows y Linux, definir de antemano las siguientes reglas reduce considerablemente los incidentes.
7.1 Elija UTF-8 como primera opción para los archivos nuevos
Para los archivos de texto nuevos, lo más prudente es elegir UTF-8 como primera opción. Sin embargo, no basta con quedarse ahí: hay que decidir también qué hacer con el BOM.
Esta es la forma de decidirlo que recomendamos:
- Textos que se leen sobre todo en el lado de Linux: usar UTF-8 no BOM como base
- Scripts que lee una herramienta legacy de Windows o Windows PowerShell 5.1: indicar explícitamente si llevan BOM según las necesidades del destinatario
- Si existe un destinatario concreto que requiere UTF-16LE, documentar ese requisito como especificación
Si solo se escribe «unificar en UTF-8», más adelante surgirán conflictos por el BOM.
7.2 Mantenga los archivos legacy existentes hasta que exista una tarea explícita de migración
Si un archivo existente está en CP932, lo más seguro es no convertirlo a UTF-8 por cuenta propia de paso al corregir alguna funcionalidad cotidiana.
Como práctica operativa, este es el enfoque seguro:
- Mantener en los archivos existentes la codificación, el BOM y el salto de línea originales
- Separar el cambio de codificación como una tarea de migración
- Realizar la conversión masiva solo después de confirmar el alcance, el impacto y los consumidores aguas abajo
Muchos incidentes de mojibake comienzan con una «conversión a UTF-8 de paso» hecha con buena intención.
7.3 Trate la codificación como parte de la interfaz
En archivos CSV, TXT, logs, archivos de configuración y protocolos simples, no solo el contenido importa: la propia codificación es parte de la interfaz.
Por ejemplo, como mínimo conviene documentar en la especificación lo siguiente:
- Si este archivo es UTF-8, CP932 o UTF-16LE
- En caso de UTF-8, si lleva BOM
- Si el salto de línea es LF o CRLF
- Si Linux o Windows actúa como productor o como consumidor
- Si algún proceso por lotes o ETL intermedio vuelve a guardar el archivo
Decir simplemente «se entrega como texto» no constituye una especificación.
7.4 No confíe en los valores predeterminados: especifique la codificación al escribir
Tanto en el código como en los scripts, es más seguro especificar la codificación de forma explícita.
Las siguientes formas de pensar son peligrosas:
- Guardar con la configuración predeterminada, sin más
- Suponer que, ajustándose al sistema operativo, probablemente saldrá bien
- Pensar que, como se leyó bien en la consola, el archivo también estará bien
- Confiar en que la detección automática se encargará de todo
Los valores predeterminados cambian normalmente entre Windows y Linux, entre PowerShell 5.1 y 7, y según el editor o el runtime. A menos que se especifique explícitamente, es fácil que solo esté funcionando por casualidad.
7.5 Compruebe por separado la consola y el archivo
Esta regla, aunque poco vistosa, resulta muy eficaz.
- Verificación de la visualización en la consola
- Verificación reabriendo el archivo
Estas dos comprobaciones deben mantenerse separadas.
Aunque chcp o la visualización del terminal parezcan correctas, no sirve de nada si el archivo guardado está en otra codificación. A la inversa, aunque el archivo esté correcto, si la página de códigos de visualización de la consola no coincide, solo se deteriorará la apariencia.
7.6 Git no corrige la codificación por usted
Es un detalle poco vistoso, pero importante.
Git, básicamente, solo hace seguimiento de bytes. Es decir, incluso los bytes dañados se incorporan al historial tal cual, con toda seriedad.
Por eso, cuando ocurra algo como lo siguiente,
- Aparece un diff enorme sin haber cambiado nada
- Solo las líneas en japonés muestran un diff extraño
- Solo cambia la primera línea
- El salto de línea y la codificación cambian a la vez
conviene sospechar antes de un incidente de recodificación que de un cambio real de contenido.
8. Lista de verificación mínima
A continuación se presenta la lista de verificación que conviene fijar desde el principio en proyectos donde se mezclan Windows y Linux.
8.1 Antes de editar
- ¿Cuál es la codificación actual de este archivo?
- ¿Tiene BOM?
- ¿El salto de línea es LF o CRLF?
- ¿Se han anotado 2 o 3 líneas representativas en japonés?
- ¿Se sabe si el consumidor final es el lado de Linux o el de Windows?
8.2 Durante la edición
- ¿No se está escribiendo dependiendo de la codificación predeterminada?
- ¿No se está guardando confiando en la detección automática?
- ¿No se están usando de forma descuidada las rutas de PowerShell o de redirección de shell?
- ¿No se está confiando solo en que «se puede leer en pantalla»?
8.3 Después de editar
- ¿Se ha reabierto el archivo tras guardarlo para verificarlo?
- ¿Las líneas representativas no se ven dañadas ni en el lado de Linux ni en el de Windows?
- ¿No han aumentado los
?o�en el diff? - ¿No se ha dañado solo la primera línea o la primera columna?
- ¿El diff no consiste únicamente en un gran cambio de BOM o de salto de línea?
8.4 Tareas que deben tratarse como migración
- Conversión masiva de CP932 a UTF-8
- Unificación de la política de BOM en UTF-8
- Inventario de scripts que asumen PowerShell 5.1
- Documentación explícita del paso de texto a través de CI, contenedores, WSL y SSH
- Unificación de la configuración de guardado del editor, del formateador y de los procesos por lotes
9. Resumen
Si hay que resumir en una frase el problema de la codificación de caracteres en Windows, la esencia es que el mundo de Unicode y el mundo de las páginas de código legacy siguen coexistiendo hoy en día.
Y los incidentes aumentan al combinarlo con Linux porque el lado de Linux suele operar bajo el supuesto de UTF-8, lo que hace salir a la superficie de golpe el CP932 y el UTF-16 de Windows, la página de códigos de la consola y las diferencias entre versiones de PowerShell.
Estos son los cinco puntos que conviene recordar:
- El mojibake es un desajuste en la interpretación de los bytes
- El deterioro de la visualización y la corrupción de datos son cosas distintas
- En Windows hay que pensar por separado en las capas de archivo, editor, consola y API
- Para el texto que se intercambia con Linux, elegir UTF-8 como primera opción
- Separar la conversión de archivos legacy existentes de las correcciones habituales
Tratar tal cual el problema de «se produjo mojibake en Windows» resulta demasiado amplio. Pero si se reduce a estas cuatro preguntas,
- ¿Cuáles son los bytes originales?
- ¿Quién los escribió y cómo?
- ¿Quién los leyó y cómo?
- ¿Ya se han guardado?
se puede ordenar bastante bien el problema.
La codificación de caracteres es un tema poco vistoso, pero entre Windows y Linux constituye el propio contrato de E/S. No dejar esto en la ambigüedad es la medida más eficaz.
10. Referencias
Windows / Microsoft
- Code Pages - Win32 apps | Microsoft Learn
- Code Page Identifiers - Win32 apps | Microsoft Learn
- Unicode in the Windows API - Win32 apps | Microsoft Learn
- Console Code Pages - Windows Console | Microsoft Learn
- chcp | Microsoft Learn
- Use UTF-8 code pages in Windows apps | Microsoft Learn
PowerShell / VS Code
- about_Character_Encoding | Microsoft Learn
- Understanding file encoding in VS Code and PowerShell | Microsoft Learn
GNU / Linux locale
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Codificación de caracteres y saltos de línea en Windows - Fundamentos de los caracteres corruptos y CRLF/LF
Organizamos de forma práctica las diferencias entre Shift_JIS, UTF-8, UTF-16, los caracteres corruptos y CRLF/LF, que suelen confundirse ...
Reglas de instrucción para reducir los errores de mojibake de Codex en Windows
Reglas prácticas para que Codex maneje archivos japoneses en Windows: evitar guardar por suposición, mantener el encoding existente y ver...
Fuentes japonesas y las trampas de los caracteres — JIS2004, selectores de variantes de ideogramas y caracteres externos en aplicaciones empresariales
«El carácter 葛 cambia de forma entre pantalla e informe», «no aparece el carácter del nombre»: los problemas de caracteres se aclaran sep...
Usar WMI/CIM desde C# y PowerShell ── Guía práctica de obtención de información de hardware, monitorización de procesos y consultas remotas
WMI/CIM es la solución estándar para leer el número de serie, monitorizar el disco y detectar procesos. Cmdlets CIM, migración desde Get-...
Guía práctica de la directiva de grupo (GPO) — funcionamiento, verificación de la aplicación y cuándo usar Intune
¿Usa GPO para distribuir configuraciones sin saber exactamente qué significa? Explicamos el funcionamiento de la directiva de grupo, el o...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Consultoría técnica y revisión de diseño
En proyectos donde las suposiciones sobre la codificación de caracteres de archivos CSV, registros y archivos de configuración difieren entre Windows y Linux, ordenar primero el contrato de E/S y las reglas operativas ayuda a reducir incidentes.
Desarrollo de aplicaciones para Windows
En las herramientas de negocio para Windows es habitual que coexistan CP932 y UTF-8, por lo que incorporar el tratamiento de la codificación de caracteres en el diseño incide directamente en la mantenibilidad.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Cuál es la causa del mojibake en Windows?
- En la mayoría de los casos, la causa es haber leído la misma secuencia de bytes con un código de caracteres distinto, o haber guardado con otro código de caracteres el resultado de una lectura errónea. Por ejemplo, si se lee en el contexto de CP932 la secuencia de bytes (E3 81 82) resultante de guardar «あ» en UTF-8, aparece como una cadena distinta, algo como «縺». No ocurre porque el japonés sea difícil: la verdadera causa es que las suposiciones de encode y decode no coinciden.
- ¿Se puede recuperar un archivo que ha sufrido mojibake?
- Si la secuencia de bytes original no ha cambiado, a veces se puede recuperar volviendo a abrirla con la codificación correcta. Lo peligroso es el flujo en el que se guarda tal cual un contenido que se ve dañado por una lectura errónea; al llegar a ese punto, ya no es un simple deterioro visual, sino corrupción de datos. Además, si una cadena Unicode se reduce a una página de códigos estrecha como CP932 y se convierte en «?» o en un carácter de sustitución, el carácter perdido no se puede restaurar aunque después se conozca la codificación correcta.
- ¿Si ejecuto chcp 65001, los archivos también pasarán a ser UTF-8?
- No. Cambiar la página de códigos de la consola y saber qué es realmente la secuencia de bytes de un archivo existente son cuestiones distintas. En Windows, la codificación del propio archivo, la interpretación del editor, la página de códigos de entrada/salida de la consola y el formato interno de cadenas de la aplicación son cosas separadas, y hay que pensar en cada capa por separado. Juzgar que «se pudo leer en la consola, así que el archivo también estará bien» es fácil que lleve a error, por lo que conviene separar la verificación de la visualización en consola de la verificación reabriendo el archivo.
- ¿Cuáles son las reglas seguras para intercambiar texto entre Windows y Linux?
- Elegir UTF-8 como primera opción para los archivos nuevos y decidir también qué hacer con el BOM. Para el texto que se lee sobre todo en el lado de Linux, use UTF-8 no BOM como base; cuando lo lea una herramienta legacy como Windows PowerShell 5.1, indique explícitamente si lleva BOM según las necesidades del destinatario. No convierta los archivos CP932 existentes de paso al hacer correcciones cotidianas: sepárelo como una tarea de migración explícita. Además, como en CSV y logs la propia codificación es parte de la interfaz, documentar como especificación la codificación, el BOM y el salto de línea reduce los incidentes.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.