Infierno de encoding de caracteres PHP leer archivo csv con fgets

Tengo un sitio web que recibe un archivo CSV por FTP una vez al mes. Durante años fue un archivo ASCII. Ahora recibo UTF-8 un mes, luego UTF-16BE el siguiente y UTF-16LE el mes siguiente. Quizás obtenga UTF-32 el próximo mes. Fgets devuelve la marca de orden de bytes al comienzo de los archivos UTF. ¿Cómo puedo obtener PHP para que reconozca automáticamente la encoding de caracteres? Probé mb_detect_encoding y devolvió ASCII independientemente del tipo de archivo. Cambié mi código para leer la lista de materiales y explícitamente poner la encoding de caracteres en mb_convert_encoding. Esto funcionó hasta el último archivo, que es UTF-16LE. En este archivo, lee la primera línea correctamente y todas las líneas siguientes se muestran como signos de interrogación (“?”). ¿Qué estoy haciendo mal?

$fhandle = fopen( $file_in, "r" ); if ( fhandle === false ) { echo "

Error opening file $file_in.

"; die(); } $i = 0; while( ( $line = fgets( $fhandle ) ) !== false ) { $i++; // Detect encoding on first line. Actual text always begins with string "Document" if ( $i == 1 ) { $line_start = substr( $line, 0, 4 ); $line_start_hex = bin2hex( $line_start ); $utf16_start = 'fffe4400'; $utf8_start = 'efbbbf44'; if ( strcmp( $line_start, 'Docu' ) == 0 ) { $char_encoding = 'ASCII'; } elseif ( strcmp( $line_start_hex, 'efbbbf44' ) == 0 ) { $char_encoding = 'UTF-8'; $line = substr( $line, 3 ); } elseif ( strcmp( $line_start_hex, 'fffe4400' ) == 0 ) { $char_encoding = 'UTF-16LE'; $line = substr( $line, 2 ); } elseif ( strcmp( $line_start_hex, 'feff4400' ) == 0 ) { $char_encoding = 'UTF-16BE'; $line = substr( $line, 2 ); } else { echo "

Error, unknown character encoding. Line =
", $line_start_hex, '

'; require( '../footer.php' ); die(); } echo "

char_encoding = $char_encoding

"; } // Convert UTF if ( $char_encoding != 'ASCII' ) { $line = mb_convert_encoding( $line, 'ASCII', $char_encoding); } echo '

'; var_dump( $line ); echo '

'; }

Salida:

  char_encoding = UTF-16LE string(101) "DocumentNumber,RecordedTS,Title,PageCount,City,TransTaxAccountCode,TotalTransferTax,Description,Name " string(83) "???????????????????????????????????????????????????????????????????????????????????" string(88) "????????????????????????????????????????????????????????????????????????????????????????" string(84) "????????????????????????????????????????????????????????????????????????????????????" string(80) "????????????????????????????????????????????????????????????????????????????????" 

Pase explícitamente el orden y las posibles codificaciones para detectar, y use el parámetro estricto. También use file_get_contents , si el archivo está en UTF-16LE, fgets lo arruinará.

 $encoding

"; foreach( explode( PHP_EOL, $input ) as $line ) { var_dump( $line ); }

El orden es importante porque UTF-8 y UTF-32 son más restrictivos y UTF-16 es extremadamente permisivo; casi cualquier longitud par aleatoria de bytes es UTF-16 válida.

La única forma de retener toda la información es convertirla a una encoding Unicode, no ASCII.

Mi sugerencia sería simplemente convertir todo a UTF-8 o ASCII (no estoy seguro del código que publicaste si estás tratando de convertir todo a UTF-8 o ASCII)

 $utf8Line = iconv( mb_detect_encoding( $line ), 'UTF-8', $line ); 

o…

 $asciiLine = iconv( mb_detect_encoding( $line ), 'ASCII', $line ); 

Puede aprovechar mb_detect_encoding para hacer el trabajo pesado por usted