PHP中的多字节安全保护

时间:2014-10-16 15:34:17

标签: php fread multibyte

我有一个太大而无法放入内存的文件,我需要从中删除某些字符(控制字符要精确)。我目前的功能如下:

$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-safe fread?我认为像这样的函数可以工作:

  1. 使用fread
  2. 读取一大块字节
  3. 检查最后一个字节,检查它是否是多字节序列的一部分。如果没有,请停在这里。
  4. 继续读取字节,直到最后一个字节不是多字节序列的一部分,或者结束当前序列。
  5. 第2步可能会使用某些逻辑like this,但我不熟悉我知道如何使用的unicode。

4 个答案:

答案 0 :(得分:1)

我还不能发表评论。但是一个选项就是像你说的那样以块的形式读取数据并使用unpack(' C *',$ chunk),从那里你可以迭代字节数组并根据你的字符找到匹配在字节数组中的字节序列。如果在该数组中找到匹配项,请替换或删除这些字节并将字符串pack()返回。

P.S。 :记得重新读取下一个块中的最后几个字节(这样你就不会在最终替换的字符串中有任何一个副词)。
我不知道我的解压缩示例是否符合您的偏好,但您可以在此处阅读更多内容:unpack doc

这是另一个指针,如果您使用utf-8,utf-8编码如何工作:utf-8 encoding

答案 1 :(得分: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-safe fread),但它确实解决了我的问题。

答案 2 :(得分:1)

我在过去几天里花了相当多的时间来搜索PHP的fread()fgetc()file_get_contents()等多字节安全版本。

不幸的是,我认为不存在,特别是对于非常大的文件。所以,我写了自己的(无论好坏):

Jstewmc\Chunker\File::getChunk()

希望,这并不可怕;它可以帮助除了我之外的人而且,我看起来并不像SO哈哈那样自我夸张的混蛋。

答案 3 :(得分:0)

未测试。太多不适合评论,但这是我所得到的要点。

$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);