entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

2votos

Leer correctamente en charset ASCII en php

Hola a todos, estoy teniendo un problema al migrar un sistema viejo a una plataforma web, el sistema anterior tenía bases en DBF (aparentemente estaba escrito en FOX que es un lenguaje que usas DBF como bases muy a menudo).

Estoy subiendo las bases a MySQL. Lo primero que intente hacer es subir los DBF (son muchisimos) directamente al mySQL pero desde el Migration Toolkit, ni del Migration Wizard (Workbench) no eran compatibles, sino que habìa que pasar antes los archivos de DBF a CSV por ejemplo. También hay muchos programas pagos que hacen exportación a otros formatos, pero no encontré ninguno que lo haga gratis.

Entonces decidí hacer yo mismo la exportación, para eso habilite la extensión de dbase en PHP y usé estas funciones http://php.net/manual/es/book.dbase.php . Pude abrir bien las bases y escribirlas en el server MySQL en sus respectivas tablas.
Pero tengo problemas con los caracteres especiales ÁÉÍÓÚ y Ñ etc... aparentemente los DBF están guardados en formato ascii extendido, la tabla ASCII que todos conocemos, allí por ejemplo el caracter Á es el numero 181 (sería lo mismo que pararse en cualquier editor de texto y escribir Alt+181).
http://es.wikipedia.org/wiki/ASCII_extendido

Sin embargo, la mayoría de los archivos de los editores guardan los datos en ANSI (latin 1 o ISO-8859-1) , donde el caracter "Á" es el 193
http://es.wikipedia.org/wiki/ISO_8859-1http://es.wikipedia.org/wiki/ISO_8859-1

Y no puedo leer correctamente el archivo original.
Estoy usando las

$cadena=mb_convert_encoding($cadena,"ISO-8859-1","ASCII");

o

$cadena=iconv("ASCII","ISO-8859-1",$cadena);

y variando para visualizarlo bien en pantalla con las funciones

ini_set( 'default_charset', 'ASCII' );
header('content-type:text/html; charset=ASCII');
iconv_set_encoding("internal_encoding", "ASCII");
iconv_set_encoding("output_encoding", "ASCII");

Pero no me dan el resultado esperado, no lo puedo ver bien en pantalla y mucho menos guardarlo correctamente en la base de datos.

Si alguien me puede ayudar se los agradecería (acá les paso un ejemplo de DBF. Si abren el DBF con excel (al menos con mi excel 2010) se puede observar bien los acentos)
https://dl.dropboxusercontent.com/u/75309684/test.dbf

Gracias

0voto

carlossevi comentado

¿Has probado a ir poco a poco con la función mb_detect_enconding() [doc] para tratar de encontrar dónde empiezan los problemas?

3 Respuestas

2votos

Leonardo-Tadei Puntos227320

Buenas,

encontré lo que te pasa: el DBF está usando el juego de caracteres IBM-850

Escribí este script para la consola (no puedo agregar módulos de PHP al servidor del trabajo) que muestra la cadena original y la versión convertida correctamente:

<?php
$dbase_name="./test.dbf";
$db=dbase_open($dbase_name, 0);
$regs=dbase_numrecords($db);
$nc=dbase_numfields($db);
$h = dbase_get_header_info($db);
print_r($h);
for ($i = 1; $i <= $regs; $i++) {
    $reg = dbase_get_record($db, $i);
    for ($j = 0; $j < $nc; $j++) {
        $cadena = $reg[$j];
        $tmp = mb_detect_encoding($cadena);
        print("$tmp | $cadena\n");
        $s = mb_convert_encoding($cadena,"UTF-8","IBM-850");
            print("convertido: $s\n");
    }
}

Mi consola en Debian GNU/Linux muestra en UTF-8 y los textos convertidos se ven perfectos.

enter image description here

Para hacer una versión web, fijate de emitir en el HTML la codificación que vayas a usar.

Saludos!

0voto

ibarragp comentado

Hola Leonardo, gracias por tu respuesta; ahora estoy en mi casa con un Windows 7, mañana en el trabajo puedo probarlo en un linux pero me va a pasar lo mismo, no tengo el control sobre las extensiones en el server del trabajo.

Esto que mencionas del "IBM-850" es correcto, yo había probado también con "CP850" (CodePage) que es lo que se conoce al ASCII extendido.
Sin embargo, bajo Windows, la función "mb_detect_encoding" me devuelve vacio (lo probé desde la consola y desde un navegador) y luego al hacer el "mb_convertido_encoding" me tira warnings de "Ilegal caracter encoding specified" y no logra cambiar el strings. Mañana lo voy a probar en linux... Será que la función en Windows está implementada diferente? Difiere el .so de la .dll??

En casa testeo con un win7x64 + apache2.2.25+PHP5.2.17+MySQL5.5.38x64

Gracias

0voto

Peter comentado

@ibarragp moví tu respuesta a comentario :)

0voto

Leonardo-Tadei comentado

Hola,

a mi en Debian la función mb_detect_encoding() tampoco me devuelve nada cuando la cadena tiene caracteres especiales. Cuando no tiene nada, me devuelve ASCII, pero convertir todo a UTF-8 no rompe al string, porque las letras "normales" coinciden, como se ve en el ejemplo.

Lo del warning por el juego de caracteres ilegal en MS Windows seguro que no es por diferencias de implementación de la función, sino por la imposibilidad del ese SO de trabajar con este jeugo de caracteres o substituirlo por uno compatible.

No se puede convertir de un juego de caracteres ni a un juego de caracterse si no está soportado.

En resumen, convertí todo de IBM-850 a UTF-8 y no tendrás más problemas.

Saludos!

0voto

ibarragp comentado

Hola Leonardo... efectivamente parece que MS Windows no se lleva bien con esto.. porque lo probé en Suse con PHP y anda bien tal cual mencionabas vos y mas o menos como había planteado originalmente... voy a ver como hago ya que el sistema debe correr en Windows, tal vez solo migre las bases en Linux y luego todo seguirá en Windows.

Gracias por tu ayuda.

3votos

Leonardo-Tadei Puntos227320

Hola ibarragp,

el problema creo que no está tanto en que los archivos estén en ASCII, que en querer enviar a un navegador este juego de caracetres.

Para ahorrarte problemas, tu DB (conexiones, tablas y campos) y tu HTML deben estar todas en el mismo juego de caracteres. Lo más normal hoy es elegir UTF-8, pero para este problema da lo mismo que pongas todo en ISO-8859-1.

Suponiendo que usás UTF-8 en todas partes, un ejemplo de la conversión sería:

<?php
...
$cadena = 'La cadena leída del CSV';
// Si detecta ASCII
if( mb_detect_encoding($cadena, "ASCII") ) {
   // Convierte a UTF-8
  $cadena = mb_convert_encoding ( $cadena , "ASCII", "UTF-8");
}
...

Podés ver acá http://ar2.php.net/manual/en/function.mb-detect-encoding.php y acá http://ar2.php.net/manual/en/function.mb-convert-encoding.php detalles sobre cómo se usan las funciones, la prioridad del juego de caracteres y la detección estricta.

Con los string convertidos, hacés los INSERT en la tabla. Luego si el HTML está codificado como UTF-8, bastará leerlo y mostrarlo para que se vea todo correctamente.

Saludos cordiales!

0voto

ibarragp comentado

Muchas gracias por sus respuetas:

Las funciones mb_convert_encoding ya las revisé del manual, ahi dice que desde el segundo parametro es el "TO" y el primer parametro es el "FROM", por eso, como dice el cuerpo de la pregunta yo probe con "ASCII" en tercer parametro (al reves de lo tuyo)

$cadena=mb_convert_encoding($cadena,"ISO-8859-1","ASCII");

Pero siguio sin funcionar...
Por las dudas probé lo que dijiste, te paso el codigo que estoy haicendo (el texto esta codificado en UTF-8)

<?php
header('content-type:text/html; charset=UTF8');

$dbase_name="./test.dbf";
$db=dbase_open($dbase_name, 0);

$regs=dbase_numrecords($db);
$nc=dbase_numfields($db);
for ($i = 1; $i <= $regs; $i++) {
    $reg = dbase_get_record($db, $i);
    for ($j = 0; $j < $nc; $j++) {
        $cadena = $reg[$j];
//      if( mb_detect_encoding($cadena, "ASCII") ) {
//          echo "[ASCII]:";
//      }
        $cadena=mb_convert_encoding($cadena,"UTF-8","ASCII");
        echo $cadena;
        echo "|";
        $cadena = $reg[$j];
        $cadena=mb_convert_encoding($cadena,"ASCII","UTF-8");
        echo $cadena;
        echo ",";
    }
    echo "<br/>";
}

?>

el archivo test.dbf esta en este link, es una mini base de datos que tiene 10 registro de un solo campo (pero se puede ver el problema bien:
https://dl.dropboxusercontent.com/u/75309684/test.dbf

Para que ande este codigo, las extensiones de

[PHP_DBASE]
extension=php_dbase.dll

y

[PHP_EXIF]
extension=php_exif.dll

tienen que estar habilitadas en el php.ini.

La idea como dije antes es no tener que pasar todo a csv, ya que son muchas tablas y se siguen actualizando.

Gracias

0voto

ibarragp Puntos360

Probé usar la funcion inconv en Windows y parece que funciona bien, (usando la mb_convert_encoding daba warnings de "Illegal character encoding specified "... asi quedó el código final...

<?php
$encode="ISO-8859-1";
//$encode="UTF-8";
header('content-type:text/html; charset="'.$encode.'"');

$db_name = 'test.dbf';

$db = dbase_open($db_name, 0) or die("¡Error! No se pudo abrir el archivo de base de datos dbase '$ruta_db'.");
$info_columna = dbase_get_header_info($db);
$ncol = dbase_numfields($db);
$nreg = dbase_numrecords($db);

for ($i=1;$i<=$nreg;$i++) {
    $reg = dbase_get_record ($db , $i );
    for ($j=0;$j<$ncol;$j++){
        $cadena = $reg[$j];
        $tmp = mb_detect_encoding($cadena);
        print("$tmp | $cadena\n");
//LINUX
//      $s1=$cadena;
//      $s1=mb_convert_encoding($cadena,$encode,"ASCII");
//      $s1=mb_convert_encoding($cadena,$encode,"IBM-850");
//      $s1=mb_convert_encoding($cadena,$encode,"CP850");
//      print("Convertido: $s1\n");
//LINUX-WINDOWS
//      $s2=$cadena;
        $s2=iconv("CP850",$encode,$cadena);
        print("Convertido: $s2\n");
    }
}

dbase_close($db);

Gracias a todos

0voto

Peter comentado

Si esta es la respuesta que soluciona, por favor marcala como correcta.

0voto

Leonardo-Tadei comentado

Peter tiene razón. Alternativamente, podés editar tu respuesta para convertirla en comentario de la respuesta que originó el thread que finalizó con este código.

Saludos!

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta