Многобайтовый безопасный хлеб в переполнении стека

У меня есть файл, который слишком велик, чтобы поместиться в память, из которого мне нужно удалить определенные символы (точнее управляющие символы). Моя текущая функция выглядит так:

$old = fopen($file, 'r');
$new = fopen($tmpFile, 'w');

while (!feof($old)) {
fwrite($new, preg_replace('/[^\P{Cc}\t\r\n]/u', '', fgets($old)));
}

rename($tmpFile, $file);

Это прекрасно работает в большинстве случаев. Возможная проблема заключается в том, что fgets читает всю строку. Некоторые файлы, которые я обрабатываю, являются буквально огромными однострочниками, которые по-прежнему вызывают проблемы с памятью.

Это можно исправить с помощью freadс размером порции скажем 8192. Однако теперь текст я кормлю preg_replace Могут быть обрезанные многобайтовые символы.

Я думал, как я могу fread сохраняя многобайтовые символы, но пока не нашел хорошего решения. Любая помощь будет потрясающей.

Возможное решение

Хотя я решил проблему по-другому, мне все еще любопытен мой первоначальный вопрос: как сделать mb-сейф fread? Я думаю, что такая функция может работать:

  1. Прочитать кусок байтов с fread
  2. Осмотрите последний байт, проверьте, является ли он частью многобайтовой последовательности. Если нет, остановитесь здесь.
  3. Продолжайте чтение байтов до тех пор, пока последний байт не станет частью многобайтовой последовательности или не завершит текущую последовательность.

Шаг 2, вероятно, может использовать некоторую логику как это, но я не настолько опытен с юникодом, как знаю.

4

Решение

Я пока не могу оставлять комментарии. Но можно было бы прочитать данные порциями, как вы сказали, и использовать unpack (‘C *’, $ chunk), оттуда вы можете перебирать массив байтов и находить совпадения для вашего символа в зависимости от последовательности байтов в байтовый массив. Если вы найдете совпадение в этом массиве, замените или удалите эти байты и упакуйте () строку обратно.

Постскриптум : не забудьте перечитать последние несколько байтов в следующем фрагменте (таким образом, у вас не будет никаких проблем согласованности с последней замененной строкой).
Я не знаю, подходит ли мой пример распаковки к вашим предпочтениям, но вы можете прочитать больше здесь: распаковать документ

Вот еще один указатель на то, как работает кодировка utf-8, если вы используете utf-8: кодировка utf-8

1

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

Мое решение было довольно простым, в конце концов. Проблема была в использовании preg_replace с возможными отрезанными многобайтовыми символами, что привело к ошибочным фрагментам.

Поскольку мне нужно было только убрать управляющие символы, которые находятся в диапазоне ASCII и, таким образом, однобайтовые, я так же легко могу сделать str_replace, который оставляет другие байты в покое.

Мое рабочее решение теперь выглядит так:

$old = fopen($file, 'r');
$new = fopen($tmpFile, 'w');

// list control characters, but leave out \t\r\n
$chars = array_map('chr', range(0, 31));
$chars[] = chr(127);
unset($chars[9], $chars[10], $chars[13]);

while (!feof($old)) {
fwrite($new, str_replace($chars, '', fread($old, 8192)));
}

Хотя он не отвечает на мой первоначальный вопрос (как сделать mb-безопасный фред), он действительно решает мою проблему.

1

Непроверенные. Слишком много, чтобы вписаться в комментарий, но это суть того, что я получил.

$old = fopen($file, 'r');
$new = fopen($tmpFile, 'w');

while (!feof($old)) {
// Your search subject
$subject = '';

// Get $numChars
for($x = 0, $numChars = 100; $x < $numChars; $x++){
$subject .= fgetc($old);
}

// Replace and write to $new
fwrite($new, preg_replace('/[^\P{Cc}\t\r\n]/u', '', $subject));

// Clean out the characters
$subject = '';
}

rename($tmpFile, $file);
0

За последние несколько дней я потратил немало часов на поиск многобайтовой безопасной версии PHP. fread(), fgetc(), file_get_contents(), так далее.

К сожалению, я не думаю, что он существует, особенно для очень больших файлов. Итак, я написал свой (к лучшему или к худшему):

Jstewmc \ Chunker \ File :: GetChunk ()

Надеюсь, это не ужасно; это помогает кому-то кроме меня; и я не выгляжу как самоуничижительный придурок на ХА.

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