Convertir en masa archivos a UTF-8

  • Por
En este artículo vamos a ver varias herramientas y procedimientos para convertir en masa todos los archivos de un proyecto y pasar a usar la codificación de caracteres UTF-8.

La frase "archivo de texto plano", que utilizamos cuando nos referimos a un fichero en el que colocamos código de un lenguaje de programación, no tiene mucho sentido realmente. Cualquier fichero se almacena usando un juego de caracteres y aunque parezca ser texto plano realmente no lo es.

Existen muchos juegos de caracteres que podemos usar cuando creamos un archivo de texto. iso-8859-1, iso-8859-15, Latin1, UTF-8, UTF-16, Windows 1250… pero el que se recomienda en la mayoría de las ocasiones para los proyectos es UTF-8.

Los que usan Linux o Mac ya tienen programas que usan predeterminadamente la codificación recomendada a la hora de trabajar con archivos, pero los que usan Windows no lo tienen tan directo. Windows generalmente usa una codificación distinta a la recomendada y muchas veces tenemos que configurar las preferencias del editor de código para que use realmente UTF-8 y no otras codificaciones como iso-8859-1, que está por defecto (al menos en las versiones en español del Windows).

Si comienzas un proyecto nuevo, seguramente empezarás ya usando UTF-8, pero en este artículo queremos atender a las personas que heredan código de un proyecto antiguo, o bien que tienen una codificación diferente de la recomendada y quieren pasarlo a UTF-8.

Al final de este texto verás que lo acompañamos con un vídeo, en el que se muestran paso por paso todos los procedimientos que hemos enumerado en el artículo.

¿Por qué UTF-8?

Porque es un juego de caracteres extenso, que no solo tiene los caracteres del alfabeto inglés, sino también del español y otra serie de idiomas, incluso orientales. Por tanto, usar UTF-8 te puede ahorrar muchos problemas cuando tratas con internacionalización.

Pero además UTF-8 es una recomendación para trabajar desde que se usa HTML5. De hecho, en el código del documento básico HTML5 tenemos una etiqueta meta que nos marca que estamos usando UTF-8. Si la usas deberías tener todos los archivos de tu proyecto en esa codificación y no siempre es así.

Por último, Ajax siempre funciona en UTF-8. Es decir, cuando el navegador recibe un contenido de manera asíncrona, usando Ajax, espera que ese contenido tenga siempre codificación UTF-8. Así que es buena idea que comencemos el proyecto usando ese juego de caracteres.

Usar un editor para cambiar la codificación

Lo más rápido y sencillo es que uses tu editor de código para cambiar la codificación del archivo. Desde todos los editores puedes hacer ese cambio y algunos te ofrecen además herramientas más avanzadas para cambiar la codificación a todos los ficheros de una carpeta y sus subcarpetas.

Si usas Sublime Text en el menú "File / Save with encoding" puedes guardar un archivo que tengas en cualquier juego de caracteres, lo que te permite usar la codificación que desees. También desde las preferencias puedes verificar y si es necesario especificar que te grabe los archivos con UTF-8.

Ahora una mención especial para el editor Brackets, que tiene una extensión muy interesante para cambiar la codificación del juego de caracteres. La extensión la cargas desde el propio Brackets desde "Archivo / Gestionar extensiones". Si buscas por UTF-8 encontrarás una llamada "Brackets Extension - UTF8 converter" que te permite convertir una carpeta de una manera rápida a UTF-8. Para más información os dejamos enlace a la página de esa extensión en Github. Realmente no te machaca los archivos, sino que te genera un clon de la carpeta a convertir, donde ha cambiado la codificación de todos los archivos.

Hasta ahora usaba Brackets para esta tarea, porque era muy sencillo realizar la conversión a golpe de ratón, pero en el último proyecto que manejé me dió problemas y los archivos convertidos se quedaron corruptos, con los enlaces con caracteres muy extraños, así que tuve que buscar otras alternativas que ahora os comento.

Convertir con el comando iconv

Este comando está disponible en Linux y Mac y realiza la conversión de un archivo, indicando la codificación origen y destino.

Nota: Los que están en Windows no sé si les funcionará, pero eso no es excusa porque es muy sencillo dentro de un Windows virtualizarse una máquina Linux y poder acceder a ese comando y muchas otras herramientas de desarrolladores fundamentales. De hecho cada vez más la virtualización se está convirtiendo en la base de lo que sería el entorno de desarrollo ideal.

La sintaxis de este comando es como la que sigue:

iconv -f ISO-8859-1 -t UTF-8 index.php > index.php.utf8

Como ves, indicas la codificación origen, luego la codificación destino y por último el archivo origen y el archivo destino donde vas a volcar el contenido una vez cambiada la codificación. Osea, tal como se ha escrito este comando, no se machaca el archivo original, sino que se genera un nuevo archivo con la codificación UTF-8.

El problema de este comando es que se realiza la conversión de un único fichero. Si lo tienes que hacer para todo un proyecto, con centenas de archivos, es mejor buscar alguna alternativa automatizada.

Nota: Si quieres saber la codificación original de un archivo apunta también este otro comando:

Para Mac:

file -I index.php

Para Linux:

file -i index.php

O también:

file -bi [nombre_del_archivo]

Te devolverá una salida indicando la codificación de ese archivo, en este caso "index.php".

Script de conversión masiva a UTF-8

Ahora vamos a crear un script para convertir todo un proyecto a UTF-8, es decir, todos los archivos de varias carpetas una carpeta y sus subcarpetas, de manera recursiva.

Primero os dejo una referencia de donde empecé a investigar este tema, que nos ofrece varias posibilidades de comandos más complejos, que por debajo usan iconv para hacer la conversión. Incluso tienes un script bash para poder hacerlo todo por medio de un comando que puedes repetir las veces que haga falta.

http://stackoverflow.com/questions/4544669/batch-convert-latin-1-files-to-utf-8-using-iconv

Infelizmente, ninguno de los comandos expuestos en ese artículo a mi me sirvió. Pero al menos es una buena referencia para probar o ver por dónde van los tiros. Quizás a vosotros os funcionen u os sirva de punto de partida para modificar el comando y adaptarlo a vuestras condiciones, si es que sabéis lo que estáis haciendo.

Como yo no soy muy bueno con el bash, para hacer más o menos lo que se necesita, me creé un script PHP que hace eso mismo.

El script lo tienes listado a continuación:

<?php
$directorios = ['web', 'data'];
$extensiones = ['.php', '.js', '.css', '.html', '.htm', '.txt'];

function cambiar_codificacion_directorio($dir, $extensiones){
    if(is_dir($dir)){
        $folder = opendir($dir);
        while($file = readdir($folder)){
            if($file == '.' | $file == '..'){
                continue;
            }
            $ruta = $dir . "/" . $file;
            if(is_dir($ruta)) {
                cambiar_codificacion_directorio($ruta, $extensiones);
            }else{
                if(in_array(strrchr($file, '.'), $extensiones)){
                    $archivo_utf8 = $ruta . '.utf8';
                    echo "\n" . $archivo_utf8;
                    exec("iconv -f ISO-8859-1 -t UTF-8 $ruta > $archivo_utf8"); 
                    unlink($ruta);
                    rename($archivo_utf8, $ruta);
                }
            }
        }
    }    
}

foreach ($directorios as $directorio){
    cambiar_codificacion_directorio($directorio, $extensiones);
}

Comienza por dos declaraciones de arrays. En el primero podríamos colocar las carpetas principales de un proyecto, para que las convierta todas, si es que tenéis organizada la carpeta de vuestro proyecto en varias partes, "htdocs", "data" y cosas similares. En el segundo array se colocan las extensiones de archivos sobre las que se va a realizar la conversión.

Luego tenemos la función recursiva cambiar_codificacion_directorio() que es donde se hace de verdad todo el proceso de la conversión. Esa función es recursiva porque te cambia los archivos de un directorio y los que haya en cualquier subdirectorio interno.

Por último en el script verás un bucle para recorrer todas las rutas de carpetas que se desean convertir, que se encarga de llamar a la función anterior para cada ruta de carpetas principales de tu proyecto.

Nota: Ojo, este script te machaca los archivos del proyecto, modificándolos una vez realizada la conversión a UTF-8. Por ello es importante que te hagas una copia de tus archivos antes de convertirlos. Otra cosa que te recomiendo es que lo leas detenidamente para que cambies lo que consideres oportuno. Por ejemplo, en la llamada a iconv estamos teniendo como codificación de origen ISO-8859-1 que quizás no sea la tuya. Otra cosa que toma por hecha este script es que las carpetas a convertir están en la misma carpeta donde tienes el script .php.

Para invocar ese archivo podemos hacerlo desde la línea de comandos. Si el script PHP lo guardaste como "convertir.php" lo ejecutas desde la consola con:

php convertir.php

Te hará la conversión sobre las extensiones y carpetas indicadas, e insisto: machacando los archivos que tuvieras anteriormente.

Alternativa Gulp

Otra alternativa que he visto que también funciona bien es usar Gulp, creando una tarea para realizar la conversión. Esto se realiza fácilmente usando un plugin de Gulp llamado gulp-convert-encoding.

En la página de ese plugin te dan un código que he probado y me realiza muy bien la conversión. Lo que no he sabido es hacer el bucle a todas las carpetas de manera recursiva. En vez de investigarlo en NodeJS, que iba a tardar un poco más, decidí escribir el script en PHP, me resulta muy fácil. Pero los que domináis NodeJS seguramente os sentiréis más cómodos usando una tarea Gulp, basada en ese plugin, que va de maravilla.

Alternativa Vim

Nuestro amigo Manuel @rastreador nos ha compartido por Twitter una nueva manera de conseguir este objetivo, usando el editor de texto Vim, que nos parece muy sencilla. Merece la pena recogerla como una alternativa a tener en cuenta.

Vim, en modo comando, acepta la siguiente instrucción:

set fenc=utf-8

Para lanzar un proceso que se encargue de convertir todos los archivos de cierta extensión que haya en una ruta, se haría de la siguiente manera, desde la línea de comandos, fuera de Vim:

find -name '*.php' -exec vim {} '+set fenc=utf-8' '+x' \;

Yo la he probado y me ha funcionado perfectamente, facilitando el cambio rápido de todas los los archivos de manera recursiva, metiéndose en todas las sub-carpetas. Eso si, en este caso solo te convierte los .php, pero podrías convertir otros tipos de archivos simplemente cambiando *.php por *.html (o la extensión que necesites) y ejecutando de nuevo el comando. Más información en el post del Blog de Rastreador.

Vídeo: adaptar un proyecto a UTF-8

En el siguiente vídeo te comentamos todas estas posibilidades y te mostramos cómo se realiza cualquiera de las acciones relatadas en el artículo. Si algo no te ha quedado claro o si quieres aprender a hacer algunas de las cosas que hemos detallado en este texto, te recomendamos atenderlo.

Configuración de Apache

Independientemente de los archivos y su codificación, también tienes que revisar cómo está configurado Apache, en caso que uses ese servidor web. Nuestro amigo Oscar Reixa, de ApacheCtl nos comenta.

Aparte de tener los archivos en UTF-8 hay que revisar los archivos de configuración de Apache. En tu caso el servidor tenía comentada la opción utf-8 y puesta la ISO, posiblemente en su día se dejó así para que funcionara bien la codificación de las webs. El fichero que lo controla está en /etc/apache2/conf.d y se llama charset la línea es

AddDefaultCharset UTF-8

o bien, para la otra codificación típica:

AddDefaultCharset ISO-8859-15

Esta es una opción que prevalece sobre los tags meta http-equiv y similares, por lo que lo que el servidor usará la codificación que se diga aquí independientemente de cómo este codificado el fichero, por eso salían mal los caracteres cuando los cambiaste a UTF.

Para cambiarlo solo en un directorio, se podría usar .htaccess añadiendo:

IndexOptions +Charset=UTF-8

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Daniel Costas

01/7/2015
comvmv
Buena aportacion pero para una conversion en masa puedes usar comvmv que entiendo es mas sencillo que el script que indicas.

http://danicos.net/2015/06/fusionando-librerias-en-calibre-codificacion-de-caracteres/

midesweb

01/7/2015
ventaja del script PHP
Hola,

Gracias por la recomendación, nunca está de más tener otras alternativas de utilidades para hacer esta tarea más sencilla.

No obstante, quería apuntar que existe una ventaja esencial de ese script PHP, aparte de que queda todo en tu mano y no dependes de las herramientas de terceros, es que el script lo puedes correr en tu servidor en producción (siempre que lo permita, puesto que hay una llamada a una función exec() que a veces está bloqueada en servidores compartidos, por lo que para que funcione en producción lo más seguro es que necesites un VPS en adelante).
Saludos!

Pedro

01/7/2015
ojito con producción...
Bueno Miguel, eso que dices es muy peligroso. Correrlo en producción puede ser un dolor de muelas si algo no funciona bien, no lo recomendaría.

midesweb

01/7/2015
tomar medidas de seguridad antes de cambiar a utf-8
Eso que dices, Pedro, es totalmente cierto. Muy bien apuntado.
Hacer este tipo de tareas en producción requiere mucho cuidado. Si tenemos acceso a la línea de comandos del servidor sería muy sencillo tomar las debidas precauciones. Yo antes de correr el script haría una copia de los archivos originales

cp directorio_a_cambiar backup_directorio_a_cambiar

Así tienes una copia de los archivos antes de correr el script y si algo va mal, sería borrar el directorio_a_cambiar y poner de nuevo el directorio backup_directorio_a_cambiar, con el nombre de antes, que tiene los archivos originales antes de pasar a utf-8. Así todo volvería a estar como antes.

midesweb

01/7/2015
Funciones mb_string son mejores y mas seguras que la iconv
Por cierto, dejo aquí un comentario interesante que nos hicieron durante la emisión en vivo del vídeo que acompaña a este artículo:

"Tengo entendido que la funciones mb_string son mejores y mas seguras que la iconv porque obtiene el listado de caracteres distintos y segun lo disponible en el sistema"

Paco Garcia

24/8/2015
Proyecto con varios cientos de archivos php
Miguel Angel eres un crak!!!
Debido al traslado de servidor, de una aplicación web con varios cientos de archvios php codificados con iso-8859-1, he conseguido convertir la codificación con el script que has publicado...
Muchas gracias!!!.

daniel_rg

28/6/2016
Para que funcione en windows xampp
con estos cambios funciona en windows con el xampp

Esto en el for de la ruta
///////////cambio
$texto_recuperado = file_get_contents($ruta);
if(!mb_check_encoding( $texto_recuperado , "UTF-8" )){
file_put_contents($ruta, utf8_encode($texto_recuperado));
}
/////////////fin cambio

oscar hugo

10/11/2017
UTF-8
Gracias ya comprendí que es UTF-8, ESTOY RECIÉN APRENDIENDO LAS 5 VOCALES para desarrollar un proyecto .....NUEVAMENTE GRACIAS y espero aprender a desarrollar.

Salomón

24/9/2018
FileEncodingConverter (v1.5.0)
https://www.jasoft.org/Blog/post/Nueva-version-mejorada-de-FileEncodingConverter-(v150).aspx

Lo encontré y anda bien para mi.

sahu

24/11/2018
coolmath
Play and enjoy one of the best online game cool math game, To play the game go to the portal http://coolmathgamesjunction.net it provide you the each necessary details.