PHP-кодировка символов для чтения csv-файла с fgets

У меня есть веб-сайт, который получает файл CSV по FTP один раз в месяц. В течение многих лет это был файл ASCII. Теперь я получаю UTF-8 один месяц, затем UTF-16BE следующий и UTF-16LE через месяц. Возможно, я получу UTF-32 в следующем месяце. Fgets возвращает отметку порядка байтов в начале файлов UTF. Как я могу заставить PHP автоматически распознавать кодировку символов? Я попытался mb_detect_encoding, и он вернул ASCII независимо от типа файла. Я изменил свой код, чтобы прочитать спецификацию, и явно помещал кодировку символов в mb_convert_encoding. Это работало до последнего файла, который является UTF-16LE. В этом файле он правильно считывает первую строку, и все последующие строки отображаются как вопросительные знаки («?»). Что я делаю не так?

$fhandle = fopen( $file_in, "r" ); if ( fhandle === false ) { echo "<p class=redbold>Error opening file $file_in.</p>"; 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 "<p class=redbold>Error, unknown character encoding. Line =<br>", $line_start_hex, '</p>'; require( '../footer.php' ); die(); } echo "<p>char_encoding = $char_encoding</p>"; } // Convert UTF if ( $char_encoding != 'ASCII' ) { $line = mb_convert_encoding( $line, 'ASCII', $char_encoding); } echo '<p>'; var_dump( $line ); echo '</p>'; } 

Вывод:

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

Явно передайте порядок и возможные кодировки для обнаружения и использования строгого параметра. Также, пожалуйста, используйте file_get_contents , если файл находится в UTF-16LE, fgets будет fgets от вас.

 <?php header( "Content-Type: text/html; charset=utf-8"); $input = file_get_contents( $file_in ); $encoding = mb_detect_encoding( $input, array( "UTF-8", "UTF-32", "UTF-32BE", "UTF-32LE", "UTF-16", "UTF-16BE", "UTF-16LE" ), TRUE ); if( $encoding !== "UTF-8" ) { $input = mb_convert_encoding( $input, "UTF-8", $encoding ); } echo "<p>$encoding</p>"; foreach( explode( PHP_EOL, $input ) as $line ) { var_dump( $line ); } 

Порядок важен, потому что UTF-8 и UTF-32 являются более ограничительными, а UTF-16 чрезвычайно разрешительным; почти любая случайная длина байтов действительна UTF-16.

Единственный способ сохранить всю информацию – преобразовать ее в кодировку Unicode, а не ASCII.

Мое предложение состояло в том, чтобы просто преобразовать все в UTF-8 или ASCII (не совсем уверен в коде, который вы опубликовали, если вы пытаетесь преобразовать все в UTF-8 или ASCII)

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

или…

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

Вы можете использовать mb_detect_encoding чтобы сделать тяжелую работу для вас