Как читать из файлов в кодировке UTF-16LE в PHP?

у меня есть csv файлы в utf-16le кодирование с BOM, Они могут быть довольно большими, поэтому мне не очень нравится идея чтения целых файлов в памяти. И вопрос, очевидно, как мне их читать?

1

Решение

Читайте это построчно и используйте mb_convert_encoding ():

$decoded_line = mb_convert_encoding ($line, "UTF-8", "UTF-16LE");

Вы можете выбрать любую кодировку назначения, но я предполагаю, что вы хотите работать со строками utf-8, которые являются наиболее распространенными в настоящее время.

Эта функция нуждается в MBstring расширение должно быть включено.

Затем вы можете передать декодированную строку str_getcsv функция, которая возвращает массив, представляющий текущую строку.

2

Другие решения

Вот что я придумал:

class readutf16le_filter extends php_user_filter {
function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
# printf("filter: %s\n", to_hex($bucket->data));
$bucket->data = iconv('UTF-16LE', 'UTF-8',
strlen($bucket->data) && substr($bucket->data, 0, 2) == "\xff\xfe"? substr($bucket->data, 2)
: $bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}

stream_filter_register('readutf16le', 'readutf16le_filter');

$fh = fopen('1.txt', 'r');
stream_filter_append($fh, 'readutf16le');

$s = fgets($fh);
printf("%s\n", to_hex($s));

$s = fgets($fh);
printf("%s\n", to_hex($s));

$s = fgets($fh);
var_dump($s);

1.txt:

a
b

Выход:

filter: ff fe 61 00 0d 00 0a 00 62 00 0d 00 0a 00
61 0d 0a
62 0d 0a
bool(false)

Что мне до сих пор не нравится, так это то, что я не вижу способа обнаружить начало файла в фильтре. Однако вряд ли это вызовет проблемы. Википедия говорит:

Использование спецификации является необязательным, и, если оно используется, должно отображаться в начале текстового потока.

Если символ спецификации появляется в середине потока данных, Unicode говорит, что его следует интерпретировать как «неразрывный пробел нулевой ширины» (запрещает разрыв строки между глифами слов). В Unicode 3.2 это использование не рекомендуется в пользу символа «Word Joiner», U + 2060. [1] Это позволяет использовать U + FEFF только в качестве спецификации.

Для зарегистрированных кодировок IANA UTF-16BE и UTF-16LE знак порядка байтов не должен использоваться, поскольку имена этих наборов символов уже определяют порядок байтов. Если встречается где-либо в таком текстовом потоке, U + FEFF следует интерпретировать как «пробел нулевой ширины без перерывов».

Вероятно, это можно сделать с помощью обертки потока. UPD Можно, наверное, сделать fread($fh, 2); перед добавлением фильтра в поток.

И другая возможная проблема заключается в том, что strlen($bucket->data) теоретически может быть нечетным числом. Из того, что я могу сказать, php использует буферизацию, и вряд ли он попадет в буфер с нечетным числом (обычно они имеют степень 2). Но для размещения таких случаев:

...
while ($bucket = stream_bucket_make_writeable($in)) {
$data = strlen($bucket->data) ?
substr($bucket->data, 0, floor(strlen($bucket->data) / 2) * 2) : '';
$bucket->data = iconv('UTF-16LE', 'UTF-8',
strlen($data) && substr($data, 0, 2) == "\xff\xfe"? substr($data, 2)
: $data);
$consumed += strlen($data);
stream_bucket_append($out, $bucket);
...

Я не знаю, как воспроизвести это все же.

1

По вопросам рекламы [email protected]