2014-01-14 35 views
3

有没有办法做一些像fread,但在一个变量? 也就是说,我想一次“读取”另一个内存变量1MB。大多数内存有效的方法来分割变量大小的块?

这样,我能有这样的事情:

$data = ... ; // 10MB of data 

$handle = fopen($data, "rb"); // Need something instead of fopen here 

while (!feof($handle)) 
{ 
    $chunk = fread($handle, 1048576); // Want to read 1MB at a time 

    doSomethingWithChunk($chunk); 
} 

fclose($handle); 

我已经加载到内存中一个大的二进制文件,10MB左右。我想将它分成1MB大小的数组。我一次不需要全部1MB大小的内存,所以我认为我可以比使用PHP内置的str_split函数更有效地执行上述操作。

回答

1

没有办法按顺序“读取”已加载到内存中的字符串;分裂它并不是真的更高效。多个变量的开销也会比单个变量更多地使用内存。理想情况下,您可以将字符串加载到流中,但PHP实际上并没有字符串流。

如果你只是想以处理大块的字符串,可以刚刚超过它的子循环:

$data; 
$pointer = 0, $size = strlen($data); 

$chunkSize = 1048576; 
while ($pointer < $size) 
{ 
    $chunk = substr($data, $pointer, $chunkSize); 
    doSomethingWithChunk($chunk); 
    $pointer += $chunkSize; 
} 

我不知道PHP如何处理内部的大字符串,但根据string documentation,一个字符串只能“最大2GB(最大2147483647个字节)”。如果你的文件大约是10MB,那么对PHP来说不应该是个问题。

另一个选项(可能是更好的选择)是将$data加载到memory or temporary stream。如果您想要避免内存过多,可以使用php://temp流封装器,其中一些数据存储在临时文件中(如果超过2MB)。只要尽快将字符串加载到流中以节省内存,然后就可以使用文件流功能。

$dataStream = fopen("php://temp", "w+b"); 
fwrite($dataStream, funcThatGetsData()); // try not to put data into a variable to save memory 

while (!feof($dataStream)) 
{ 
    $chunk = fread($dataStream, 1048576); // want to read 1MB at a time 
    doSomethingWithChunk($chunk); 
} 

fclose($dataStream); 

如果从另一个功能得到$data你可以绕过$dataStream代替。如果你必须在一个字符串$data事前,一定要打电话就可以了unset()释放内存:

$data = getData(); // string from some other function 
$dataStream = fopen("php://temp", "w+b"); 
fwrite($dataStream, $data); 
unset($data); // free 10MB of memory! 
... 

如果你想保持它所有的内存,你可以使用php://memory,但是你可能也只是使用在这种情况下一个字符串。

+1

这两种方法都可以工作。不幸的是,我对两种方法进行了基准测试,实际上它们比str_split慢*,所以我只是保持代码的方式。 :)尽管谢谢你非常透彻的答案! 仅供参考,'php:// memory'的数量级比'str_split'慢了几个数量级,而'substr'方法几乎与w /'str_split'差不多。然而,'str_split'只是几乎没有更快 - 并且读取/维护更容易。 最终结果:保持一切! – DOOManiac

+0

@DOOManiac:我想到虚拟流会比较慢 - 使用'php:// temp'后面的想法是它有更高的内存效率。即使将它全部存储在内存中,我敢打赌它使用的是原始C字节数组,而不是更方便但消耗更多内存的“$ string”。使用流和大块阅读功能可以防止你的记忆真的很高。如果这不是你的目标,那么'str_split'可能是你最好的选择。 ;) – coderstephen

+0

我的目标是速度,纯粹和简单。尽管谢谢你的回答! – DOOManiac

1

你可以用like;

$handle = @fopen("path_to_your_file", "r"); 
if ($handle) { 
    while (($buffer = fgets($handle, 1024)) !== false) { 
     doSomethingWithChunk($buffer); 
    } 
    fclose($handle); 
} 
+0

我会盲目的眼睛第二个答案:) –

+0

我不想从磁盘(再次)读取文件。它到达这里时已经被读入内存。另外它可能根本不是磁盘 - 它可能来自数据库读取或其他输入。非常抱歉,这个答案与我所需要的无关...... – DOOManiac

+1

如果你已经把它放在内存中,你需要高效地在内存中分配它。例如,您需要使用数组,然后您需要从该数组中获得100乘100。你不能使用fopen,fread等......他们是文件操作功能 –

相关问题