En Shift_JIS el carácter 本 son los dos bytes 96 7B, y 7B es el { de ASCII; el carácter 表 es 95 5C, y 5C es una barra invertida. Es decir: en cuanto la codificación de caracteres se equivoca, la prosa japonesa se convierte, byte a byte, en secuencias de control y llaves de LaTeX. Por eso el mojibake en TeX nunca es solo «glifos ilegibles», y por eso hoy un archivo .tex debe guardarse en UTF-8 con saltos LF. Esta página muestra, con los mensajes de error reales, qué ocurre al toparse con restos de Shift_JIS, EUC-JP o ISO-2022-JP, cómo convertirlos, y en qué se diferencia el repertorio de caracteres de platex y uplatex.
¿En qué guardar un archivo .tex? UTF-8 y LF
UTF-8 —con o sin BOM— y LF. En TeX Live 2024, uplatex, lualatex y xelatex leen UTF-8 por defecto, y Git presupone UTF-8 y LF al construir un diff. Lo incómodo en japonés es que antes de que Unicode se generalizara convivían tres codificaciones mutuamente incompatibles: Shift_JIS en Windows, EUC-JP en Unix e ISO-2022-JP —el «código JIS»— para el correo, que debía atravesar canales de siete bits. El mismo kanji tenía tres secuencias de bytes distintas y, por mucho que se mire el archivo, no se distingue con certeza cuál es. Por eso la codificación es lo primero que hay que sospechar al abrir un directorio de laboratorio antiguo.
| Codificación | Dónde se usaba | Dónde aparece todavía |
|---|---|---|
UTF-8 | estándar actual (Unicode) | única opción para lo nuevo y valor por defecto de TeX Live |
Shift_JIS | Windows antiguo, autoedición, consolas | plantillas antiguas distribuidas, anexos en CD-ROM; también CP932 |
EUC-JP | Unix antiguo, centros de cálculo universitarios | los .tex y .sty que siguen en directorios compartidos |
ISO-2022-JP | correo electrónico (el «código JIS») | esquema de siete bits que conmuta juegos con secuencias de escape; fragmentos pegados desde el correo |
Qué ocurre cuando un archivo Shift_JIS se topa con una cadena de herramientas UTF-8
La pantalla no se llena de glifos ilegibles. Aparece una cascada de errores y la compilación cae en ! Undefined control sequence. Entregue un archivo Shift_JIS tal cual a uplatex en TeX Live 2024: el primer mensaje es ! LaTeX Error: Invalid UTF-8 byte "93., seguido de ! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕). Hasta ahí todo es honesto: avisos de bytes ilegales. El problema viene después: la línea reproducida dice l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e, y se ve a simple vista — el segundo byte de 本 se ha vuelto { y el de 表 se ha vuelto \, y ambos son ya sintaxis TeX viva. El síntoma, por tanto, no es «el japonés se ve mal» sino «LaTeX dice que un comando que nunca escribí está indefinido», y por eso la codificación es lo último que se sospecha.
$ uplatex sjis.tex
! LaTeX Error: Invalid UTF-8 byte "93.
l.3 ^^93
! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕).
! Undefined control sequence.
l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e
$ uplatex -kanji=sjis sjis.tex # tell the engine what it is reading
Output written on sjis.dvi (1 page, 320 bytes).El accidente se sigue necesariamente del diseño de Shift_JIS. Como el segundo byte de un carácter de dos bytes puede caer dentro del rango ASCII 0x40–0x7E, hay 52 caracteres cuyo segundo byte es exactamente 0x5C (barra invertida) y 50 cuyo segundo byte es 0x7B (llave de apertura). Al primer grupo pertenecen 表, 十, ソ, 能, 貼, 暴, 申 y 構; al segundo, 本, 宮, 施, 旬, 養 y 鶏, todos ellos caracteres corrientes en japonés seguido. Los desarrolladores japoneses los llamaron los «caracteres malos» y pelearon con ellos durante años, porque el mismo accidente golpeaba a los guiones de shell y a los archivos de configuración, no solo a TeX. EUC-JP no tiene ese defecto —sus segundos bytes son siempre 0x80 o superiores—, y por eso durante un tiempo se prefirió EUC-JP en el mundo TeX.
Convertir Shift_JIS a UTF-8: iconv y nkf
La herramienta clásica del ámbito japonés es nkf (Network Kanji Filter), pero hay que instalarla: no viene ni con macOS ni con la mayoría de las distribuciones Linux. Pruebe antes iconv: es una utilidad POSIX estándar y está en /usr/bin/iconv tanto en macOS como en Linux. iconv -f CP932 -t UTF-8 old.tex > new.tex resuelve la conversión de Shift_JIS a UTF-8. Si dispone de nkf, nkf -w -Lu --overwrite *.tex reescribe muchos archivos en el sitio con una sola línea, lo que compensa instalarlo cuando se hereda un directorio entero. Use lo que use, haga una copia o registre en Git antes de ejecutarlo: --overwrite destruye el original tal como anuncia.
# iconv -- always present; safest one file at a time
iconv -f CP932 -t UTF-8 old.tex > new.tex # Shift_JIS -> UTF-8
iconv -f EUC-JP -t UTF-8 old.tex > new.tex # EUC-JP -> UTF-8
# whole tree, keeping a backup of every original
for f in *.tex; do cp "$f" "$f.bak"; iconv -f CP932 -t UTF-8 "$f.bak" > "$f"; done
# nkf, if installed: detect first, then convert in place to UTF-8 + LF
nkf -g old.tex
nkf -w -Lu --overwrite *.texAl usar iconv, escriba CP932 y no SHIFT_JIS. Suele darse por hecho que son lo mismo, y no lo son. Pida al iconv de macOS que convierta a SHIFT_JIS un texto con ~ (la raya ondulada) o ① (un dígito encerrado) y se detendrá con iconv: iconv(): Illegal byte sequence; con CP932 funciona. SHIFT_JIS es la definición estrecha, fiel a JIS X 0208, y excluye los caracteres de extensión de NEC e IBM: dígitos encerrados, números romanos y demás. Los archivos procedentes de Windows son en la práctica CP932, así que ese es el valor por defecto correcto. El mismo razonamiento vale en sentido inverso, cuando hay que volver a Shift_JIS para una herramienta antigua.
| Opción de nkf | Qué hace | Equivalente en iconv |
|---|---|---|
-g | detecta la codificación y el salto actuales; no convierte | sin equivalente: use file o un detector como chardetect |
-w | convierte a UTF-8 sin BOM | iconv -t UTF-8 |
-s / -e / -j | convierte a Shift_JIS / EUC-JP / ISO-2022-JP | iconv -t CP932 / -t EUC-JP / -t ISO-2022-JP |
-Lu / -Lw / -Lm | normaliza los saltos a LF / CRLF / CR | sin equivalente: use sed, dos2unix o el eol=lf de Git |
--overwrite | reescribe en el sitio los archivos indicados | sin equivalente: iconv escribe en la salida estándar; redirija a un archivo nuevo |
-kanji=: decirle al motor cómo leer, sin convertir
Cuando no se quiere —o no se puede— reescribir el archivo, hay que decírselo al motor. (u)platex acepta -kanji=, con -kanji=sjis, -kanji=euc, -kanji=jis y -kanji=utf8. El archivo Shift_JIS que producía un muro de errores leído como UTF-8 compila bajo uplatex -kanji=sjis sjis.tex con un escueto Output written on sjis.dvi (1 page, 320 bytes). Pero considérelo primeros auxilios. El editor, Git, grep y cualquier archivo traído con \input siguen suponiendo UTF-8, así que en cuanto se mezclan codificaciones aparece otro accidente. Úselo para compilar una vez un manuscrito recibido, lo justo para leerlo, y después convierta. Conviene saber que lualatex y xelatex no tienen opción -kanji=: siempre leen UTF-8.
platex frente a uplatex: el mismo binario, dos mundos de caracteres
La diferencia está en el repertorio de caracteres, no en el programa. Ejecute platex --version y uplatex --version en TeX Live 2024 y ambos se anuncian como e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6: el mismo binario. Solo cambia el paréntesis: (utf8.euc) en platex, (utf8.uptex) en uplatex. Cargan formatos distintos, y el formato decide cómo se guardan internamente los caracteres. La consecuencia es concreta: escriba 髙 (U+9AD9, variante de 高 frecuente en apellidos japoneses) y platex se detiene con ! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9), mientras que uplatex lo compone sin decir nada. El platex a secas está encerrado en el repertorio de JIS X 0208, y todo lo que quede fuera se rechaza en la puerta. Ya no hay motivo para elegir platex en un documento nuevo. Con uplatex por defecto, 髙, 𠮟 y las variantes de una lista de nombres pasan todas.
$ platex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.euc) (TeX Live 2024)
$ uplatex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.uptex) (TeX Live 2024)
$ platex takashima.tex # the document contains 髙 (U+9AD9)
! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9)
$ uplatex takashima.tex
Output written on takashima.dvi (1 page, 304 bytes).Saltos de línea y BOM: ¿rompen algo realmente LF, CRLF y CR?
LaTeX mismo los acepta todos sin rechistar. Entregue a uplatex un .tex escrito con \r\n (CRLF, Windows), o uno que empiece con un BOM UTF-8 (EF BB BF): en TeX Live 2024, tanto uplatex como lualatex compilan sin un solo aviso. La creencia de que un BOM deja un carácter invisible al principio del documento no se cumple hoy en estos dos motores. Quien sufre no es LaTeX, sino las herramientas de alrededor. Un archivo con CRLF y LF mezclados aparece en Git como si todas las líneas hubieran cambiado, lo que hace imposible revisarlo. Un .sty con \r residuales al final de línea puede desactivar un ancla de fin de línea en grep. Unificar en LF es, pues, una decisión de trabajo en equipo, no de composición. Con Git, una línea en .gitattributes es lo más fiable y absorbe al extraer las diferencias entre las máquinas de cada colaborador.
# .gitattributes -- normalise on checkin, hand out LF on checkout
*.tex text eol=lf
*.sty text eol=lf
*.bib text eol=lf
*.pdf binary
# one-off cleanup of a file that arrived with CRLF
sed -i.bak $'s/\r$//' old.tex- Unifique todo, viejo y nuevo, en UTF-8 + LF. Es el valor por defecto de los tres motores de TeX Live 2024 y encaja con lo que Git espera.
- Empiece las conversiones con
iconv -f CP932 -t UTF-8. EscribaCP932, noSHIFT_JIS, o fallarán los dígitos encerrados y la raya ondulada. - Si tiene
nkf,nkf -w -Lu --overwrite *.texes lo más rápido, pero no viene ni con macOS ni con la mayoría de distribuciones Linux. - Copie el archivo o haga commit en Git antes de convertir.
--overwriteno se puede deshacer. - Empiece los documentos nuevos con
uplatex(olualatex). Elplatexa secas falla con caracteres fuera de JIS X 0208, como 髙 (U+9AD9). -kanji=sjisson primeros auxilios. Cuando pueda leer el documento, convierta el archivo mismo a UTF-8.