1

tengo una BD Mysql en local con codificación utf8.

Luego tengo un fichero txt que el contenido es como este, donde hay acentos Ñ y otros caracteres.

9950543SÃO MATEUS MAD PT0830

Mi intención es leer registro a registro este fichero de 50.000 líneas e insertarlas en la tabla de MySQL.

Para ello he escrito un command en laravel que lee el fichero, llama al modelo PostalCode e intento insertar los datos.

    public function __construct()
    {
    parent::__construct();

    $content = fopen(storage_path("app\fichero_codigos_postales.TXT"),'r');

    while(!feof($content)){

        $line=utf8_decode(fgets($content));

        $cp = substr($line,0,7);
        $localidad = substr($line,8,24);
        $pais = substr($line,32,2);
        $param = substr($line,34,4);


        try {


            $item = PostaslCode::create([
                'postal_code' => $cp,
                'town' => $localidad,
                'country' => $pais,
                'extra_param' => $param
            ]);
        }
        catch (\Exception $exception)
        {
            var_dump($exception->getMessage());

            dd($exception);
        }

    }

}

El commmand me funciona, pero en MySQL veo los caracteres como acentos o Ñ como "?"

ilernet
  • 1,291
  • 1
  • 17
  • 38
  • 1
    ¿Responde esto a tu pregunta? [¿Por que el Carácter inspector (�) aparece en algunos datos obtenidos de la Base de Datos?](https://es.stackoverflow.com/questions/59489/por-que-el-car%c3%a1cter-inspector-aparece-en-algunos-datos-obtenidos-de-la-base) – BetaM Oct 29 '21 at 17:56

1 Answers1

2
  1. Empieza por determinar el encoding del archivo. En linux

    file -i fichero_codigos_postales.txt
    

te daría esa información. Para este caso puntual da igual la herramienta. Si en cambio tuvieses que procesar una colección incierta de archivos con encodings variables habría que automatizar ese diagnóstico.

  1. Si en el paso 1 el archivo original está en UTF-8, y la tabla destino también en UTF-8, entonces no transformes la data, porque actualmente tú mismo la estás convirtiendo al encoding incorrecto:

     // ahora $line es ISO-8859-1
     $line=utf8_decode(fgets($content));
    

Si en cambio el paso 1 indicase que es ISO-8859-1, entonces lo que buscas es la función inversa

 // ahora $line es UTF-8
 $line=utf8_encode(fgets($content));

Pero en mi experiencia, utf8_decode y encode son las más frágiles de entre las funciones de conversión de encoding de PHP y cualquier input con caracteres de más de 2 bytes (UTF-8 puede usar hasta 4) corromperá la salida. Es preferible mb_convert_encoding, pero incluso mejor que ésta es iconv.

 $line=iconv('ISO-8859-1','UTF-8',fgets($content));  

Iconv no es infalible pero es muy versátil con los juegos de caracteres, pudiendo además recibir modificadores como "si el carácter no tiene equivalencia, omítelo, no me pongas un �":

 $line=iconv('ISO-8859-1','UTF-8//IGNORE',fgets($content));  

Hay un último caso de borde que puede explicar los caracteres corruptos incluso trabajando con un archivo en UTF-8 y una tabla utf8, y se debe a que utf8_general_ci y todos los utf8_xxxxxx_yy de MySQL no son realmente UTF-8.

Como puse antes, UTF-8 puede usar hasta cuatro bytes. El utf-8 de MySQL solamente usa 3 bytes. Si llegas a encontrarte en ese nivel de profundidad en el agujero del conejo y ya cuestionas tu sanidad, el encoding utf8_mb4 (de multibyte 4) es UTF-8 real y es lo que se usa actualmente por ejemplo al instalar Laravel. (Creo que wordpress sigue instalando el encoding viejo).

ffflabs
  • 21,223
  • 25
  • 48