2014-01-14 85 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等......他們是文件操作功能 –

相關問題