Многобайтовый безопасный фрейд в PHP
У меня есть файл, который слишком велик, чтобы поместиться в память, из которого мне нужно удалить определенные символы (точнее, управляющие символы). Моя текущая функция выглядит так:
$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, вероятно, мог бы использовать некоторую логику , подобную этой , но я не настолько опытен в юникоде, чтобы знать, как это сделать.
4 answers
Я пока не могу оставлять комментарии. Но можно было бы прочитать данные по частям, как вы сказали, и использовать unpack ('C*', $chunk), оттуда вы можете повторить массив байтов и найти соответствие для вашего символа в зависимости от последовательности байтов в массиве байтов. Если вы найдете совпадение в этом массиве, замените или удалите эти байты и упакуйте() строку обратно.
P.S.: не забудьте перечитать последние несколько байтов в следующем фрагменте (чтобы у вас не было никаких совпадений с окончательным замененная строка).
Я не знаю, соответствует ли мой пример распаковки вашим предпочтениям, но вы можете прочитать больше здесь: распаковать документ
Вот еще один указатель того, как работает кодировка 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\Фрагмент\Файл::gEtchunk()
Надеюсь, это не ужасно; это помогает кому-то, кроме меня; и я не выгляжу как самовозвеличивающий придурок, ха-ха.