Преобразование набора символов: PHP iconv не будет работать - PullRequest
1 голос
/ 19 апреля 2020

У нас есть несколько внутренних панелей с PHP бэкэндом, которые используются для загрузки файлов CSV. Недавно мы обнаружили, что некоторые CSV не могут быть проанализированы: функция fgetcsv возвращает false, что является супер мерзким , поскольку мы не смогли определить фактическую проблему в CSV (например, в какой строке нет проблем опыта, какие символы он не может переварить et c.)


Мы сузили задачу до кодировки с набором символов : сгенерированы CSV с Windows машины выходили из строя. Linux iconv команда смогла исправить CSV для нас

iconv -c --from-code=UTF-8 --to-code=ASCII path/to/uncleaned.csv > path/to/cleaned.csv

, в то время как PHP эквивалент не работал (пробовал используя обе опции //IGNORE//TRANSLIT).

$uncleaned_csv_text = file_get_contents($source_data_csv_filename);
$cleaned_csv_text = iconv('UTF-8', 'ASCII/IGNORE//TRANSLIT', $uncleaned_csv_text);
file_put_contents($source_data_csv_filename, $cleaned_csv_text);
..
$headers = fgetcsv($source_data_csv_filename)

Хотя мы можем использовать функцию PHP exec для запуска команды оболочки

  • это меньше, чем идеал
  • практика запрещена в нашей организации с точки зрения безопасности (Travis не позволяет ей пройти)

Есть ли альтернативный способ чтобы выполнить эту «очистку» CSV? *


UPDATE-1

Мы исследовали несколько других вариантов, ни один из которых не работал для нас


ОБНОВЛЕНИЕ-2

  • После echo дайджеста sha1 текста CSV до и после обработки PHP iconv функцией, мы обнаружили, что iconv не вносит никаких изменений
  • Также в моем случае mb_check_encoding на исходных текстовых выходах CSV true независимо от входного запроса: windows-1252, ascii, utf-8

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Я работаю над плагином (в Wordpress), который некоторое время обрабатывает csv-файлы (с оценкой 5/5 звезд), и я использую mb_convert_encoding() без проблем. Я знаю, что у меня есть пользователи, которые используют windows и Linux.

В основном: ( TO UTF-8, FROM : Windows -1252)

$cleaned_csv_text = mb_convert_encoding($uncleaned_csv_text, 'UTF-8', 'Windows-1252');

Если вы не знаете кодировку оригинала (возможно, лучше в вашем случае):

( TO UTF-8)

$cleaned_csv_text = mb_convert_encoding($uncleaned_csv_text, 'UTF-8');

ОБНОВЛЕНИЕ: Вот более полный ответ, который, я надеюсь, вы найдете полезным: я использовал file() вместе с str_getcsv() et c:

<?php
$file = "csvfiles/pricelist.csv"; //This is Windows-1252 encoded

//Load a csv file into an array $content_arr
$content_arr = array_map(function($v) {
    $delimiter = ';';
    return str_getcsv($v, $delimiter);},
    file( $file )); 

//Do encoding row by row
//and include end of line based on the item in the array $content_arr
$csv = array_map(function($v) {
    return mb_convert_encoding($v[0], 'UTF8','Windows-1252') . detect_eol($v[0]);},
    $content_arr);

//Save modified file in UTF8
file_put_contents('csvfiles/pricelist_modified.csv', $csv);

//Detects the end-of-line character of a string.
//
//function from 
//https://stackoverflow.com/questions/11066857/detect-eol-type-using-php/11066858#11066858
function detect_eol( $str )
{
    static $eols = array(
        "\0x000D000A", // [UNICODE] CR+LF: CR (U+000D) followed by LF (U+000A)
        "\0x000A",     // [UNICODE] LF: Line Feed, U+000A
        "\0x000B",     // [UNICODE] VT: Vertical Tab, U+000B
        "\0x000C",     // [UNICODE] FF: Form Feed, U+000C
        "\0x000D",     // [UNICODE] CR: Carriage Return, U+000D
        "\0x0085",     // [UNICODE] NEL: Next Line, U+0085
        "\0x2028",     // [UNICODE] LS: Line Separator, U+2028
        "\0x2029",     // [UNICODE] PS: Paragraph Separator, U+2029
        "\0x0D0A",     // [ASCII] CR+LF: Windows, TOPS-10, RT-11, CP/M, MP/M, DOS, Atari TOS, OS/2, Symbian OS, Palm OS
        "\0x0A0D",     // [ASCII] LF+CR: BBC Acorn, RISC OS spooled text output.
        "\0x0A",       // [ASCII] LF: Multics, Unix, Unix-like, BeOS, Amiga, RISC OS
        "\0x0D",       // [ASCII] CR: Commodore 8-bit, BBC Acorn, TRS-80, Apple II, Mac OS <=v9, OS-9
        "\0x1E",       // [ASCII] RS: QNX (pre-POSIX)
        "\0x15",       // [EBCDEIC] NEL: OS/390, OS/400
        "\r\n",
        "\r",
        "\n"
    );
    $cur_cnt = 0;
    $cur_eol = "\r\n"; //default

    //Check if eols in array above exists in string
    foreach($eols as $eol){     
        $char_cnt = mb_substr_count($str, $eol);

        if($char_cnt > $cur_cnt)
        {
            $cur_cnt = $char_cnt;
            $cur_eol = $eol;
        }
    }
    return $cur_eol;
}
0 голосов
/ 19 апреля 2020

Это не может рассматриваться как решение (поскольку мы даже не определили root причину проблем), а скорее взлом.


Мы попросили людей, использующих Windows машины, сделать это для загрузки CSV

  • Загрузить CSV в Google Sheet (File > Import) enter image description here
  • Загрузить созданный лист обратно в виде CSV (File > Download > Comma-separated values) enter image description here
  • Затем используйте загруженный CSV для загрузки на панель управления

Кредиты на @ RohitChandana за предложение этого обходного пути

...