У меня есть файл, который слишком велик, чтобы поместиться в память, из которого мне нужно удалить определенные символы (точнее управляющие символы). Моя текущая функция выглядит так:
$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
? Я думаю, что такая функция может работать:
fread
Шаг 2, вероятно, может использовать некоторую логику как это, но я не настолько опытен с юникодом, как знаю.
Я пока не могу оставлять комментарии. Но можно было бы прочитать данные порциями, как вы сказали, и использовать unpack (‘C *’, $ chunk), оттуда вы можете перебирать массив байтов и находить совпадения для вашего символа в зависимости от последовательности байтов в байтовый массив. Если вы найдете совпадение в этом массиве, замените или удалите эти байты и упакуйте () строку обратно.
Постскриптум : не забудьте перечитать последние несколько байтов в следующем фрагменте (таким образом, у вас не будет никаких проблем согласованности с последней замененной строкой).
Я не знаю, подходит ли мой пример распаковки к вашим предпочтениям, но вы можете прочитать больше здесь: распаковать документ
Вот еще один указатель на то, как работает кодировка utf-8, если вы используете utf-8: кодировка utf-8
Мое решение было довольно простым, в конце концов. Проблема была в использовании 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-безопасный фред), он действительно решает мою проблему.
Непроверенные. Слишком много, чтобы вписаться в комментарий, но это суть того, что я получил.
$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);
За последние несколько дней я потратил немало часов на поиск многобайтовой безопасной версии PHP. fread()
, fgetc()
, file_get_contents()
, так далее.
К сожалению, я не думаю, что он существует, особенно для очень больших файлов. Итак, я написал свой (к лучшему или к худшему):
Jstewmc \ Chunker \ File :: GetChunk ()
Надеюсь, это не ужасно; это помогает кому-то кроме меня; и я не выгляжу как самоуничижительный придурок на ХА.