php并发取源码,PHP读取大文件源码示例-Swoole多进程读取大文件
PHP讀取大文件源碼示例,通過(guò)PHP讀取過(guò)大、超大型文件的思路及解決方案。
在日常讀取文件時(shí),若文件 不是很大,通常使用file_get_contents,將內(nèi)容一次性載入的變量中,也可以遠(yuǎn)程加載網(wǎng)頁(yè)或者遠(yuǎn)端文件。
若加載超過(guò)PHP限制的內(nèi)存大小,或者超過(guò)本機(jī)內(nèi)存大小的文件進(jìn)程就會(huì)報(bào)錯(cuò)或者崩掉。
為了解決這個(gè)問(wèn)題,我們采用使用完畢并釋放的原則來(lái)讀取大文件。
單線程讀入
如果不考慮多線程的情況下,單線程讀取大文件采用while fread就可以實(shí)現(xiàn)。
如下代碼
$handle = fopen("./big.txt", "rb");
while (!feof($handle)) {
$contents = fread($handle, 8192);
// 業(yè)務(wù)處理
unset($contents); // 釋放掉變量
}
fclose($handle);
feof是判斷是否到文件尾,如果沒(méi)有到文件尾,則會(huì)一直while循環(huán),并執(zhí)行讀取操作。每次讀取8192字節(jié),然后使用過(guò)后將其釋放掉。
往往很多文件并不是一行的,有多行內(nèi)容。需要將每行內(nèi)容讀取出來(lái)當(dāng)做一條數(shù)據(jù)處理,也可以使用fgets。
即如下代碼:
$handle = fopen("./big.txt", "rb");
while (!feof($handle)) {
$contents = fgets($handle, 1024);
// 業(yè)務(wù)處理
unset($contents); // 釋放掉變量
}
fclose($handle);
這里的fgets第二個(gè)參數(shù),默認(rèn)為1024字節(jié)。即默認(rèn)讀取一行數(shù)據(jù),如果一行數(shù)據(jù)小于1024字節(jié),則完整讀取。如果超出1024字節(jié),則只取前1024字節(jié)。遇到換行符"\n"或者"\r\n"或者結(jié)束符會(huì)停止讀取。
如果遇到變態(tài)的文件,很多行都只有1000長(zhǎng)度,某一行有8000長(zhǎng)度,如果在不清楚的情況下,就很難掌控,若要完整讀取就需要指定讀取的最大字節(jié),8000才能將每一行完整讀取。
還有一種方法就是自己處理?yè)Q行符。默認(rèn)讀取1024字節(jié),然后放置到內(nèi)存中,使用過(guò)后再將其釋放。這種操作很節(jié)省內(nèi)存,但是在邏輯處理上需要自己處理?yè)Q行符。
如,讀取1024字節(jié),沒(méi)有換行符,則保存數(shù)據(jù)繼續(xù)讀取。再讀取1024字節(jié),判斷其中是否有換行符,如果有則處理最開(kāi)始到換行符中的數(shù)據(jù)。再將剩下的數(shù)據(jù)保存,等待下一次讀取,直到整個(gè)文件讀取完畢。
$handle = fopen("./big.txt", "rb");
$contents = "";
while (!feof($handle)) {
$contents .= fread($handle, 8192);
// 判斷讀取到的內(nèi)容是否包含換行符,包含則進(jìn)入循環(huán)體
while(strpos($contents, "\n") !== false){
$eol_pos= strpos($contents, "\n");
$line = substr($contents, 0, $eol_pos);
// $line為一行的數(shù)據(jù),進(jìn)行業(yè)務(wù)處理,并釋放
unset($line);
$contents = substr($eol_pos, 0);// 將剩余內(nèi)容放置到變量中以供下次使用
}
}
fclose($handle);
多線程讀取
PHP默認(rèn)沒(méi)有多線程,這里可以采用多進(jìn)程的方式實(shí)現(xiàn),或者swoole的多進(jìn)程來(lái)實(shí)現(xiàn)。
例如讀取一個(gè)8GB文件,分8個(gè)線程,每個(gè)線程讀取1GB數(shù)據(jù)內(nèi)容。或者更多線程進(jìn)行拆分工作內(nèi)容。
獲取文件大小
首先第一步,就是獲取整個(gè)文件的體積大小,然后計(jì)算每個(gè)線程應(yīng)該負(fù)責(zé)處理的一部分內(nèi)容。
function length($filename)
{
$handle = fopen($filename, "rb");
$currentPos = ftell($handle);
fseek($handle, 0, SEEK_END);
$length = ftell($handle);
fseek($handle, $currentPos);
// $length 文件總長(zhǎng)度
return $length;
}
echo length("./big.txt");
處理邏輯實(shí)現(xiàn)過(guò)程
這里主要說(shuō)明下作了哪些內(nèi)容,首先是設(shè)定分配總的線程數(shù)。然后根據(jù)設(shè)置的線程數(shù),計(jì)算每個(gè)線程要讀取的數(shù)據(jù)大小,即從哪里開(kāi)始讀,讀到哪里結(jié)束。
然后必定會(huì)出現(xiàn)拆分后,讀到不完整行的情況,在這里來(lái)解決這種前半行或者后半行的意外情況。
解決邏輯就是,假設(shè)線程開(kāi)始的讀取位置在某一行的中間,我們一個(gè)字符向前移動(dòng),移動(dòng)到上個(gè)換行符(也可能是最開(kāi)始)即可獲取到整行文本內(nèi)容。
處理掉殘行數(shù)據(jù)之后,使用yield來(lái)傳遞數(shù)據(jù)給業(yè)務(wù)處理。
$filename = "./big.txt";
$maxProcess = 8;// 分配8個(gè)線程
$length = length($filename);
$singleProcessLength = ceil($length / $maxProcess);
// 線程負(fù)責(zé)讀取的內(nèi)容
function processRead($filename, $index, $singleProcessLength)
{
$fh = fopen($filename, 'r');
$beginPos = $index * $singleProcessLength;
//結(jié)束位置=線程序列*線程處理數(shù)據(jù)長(zhǎng)度+線程處理數(shù)據(jù) - 1 (長(zhǎng)度轉(zhuǎn)指針,實(shí)際結(jié)束指針小于結(jié)束長(zhǎng)度)
$endPos = $index * $singleProcessLength + $singleProcessLength - 1;
fseek($fh, $beginPos);
echo '線程:' . $index . ',起始位置:' . $beginPos . ',結(jié)束位置:' . $endPos . PHP_EOL;
//移動(dòng)到上個(gè)\n 以便首次順利獲取整行內(nèi)容
while (fseek($fh, -1, SEEK_CUR) === 0) {
if (fread($fh, 1) == "\n" || ftell($fh) <= 0) {
break;
}
fseek($fh, -1, SEEK_CUR);
}
echo '線程:' . $index . ',移動(dòng)完畢!!!!!' . PHP_EOL;
//整行讀取數(shù)據(jù)
//結(jié)束時(shí)位置超過(guò)預(yù)計(jì)結(jié)束位置是正常狀況,fgets 讀取一整行內(nèi)容
//預(yù)計(jì)結(jié)束位置可能在行內(nèi),所以產(chǎn)生不同結(jié)果。
while (ftell($fh) <= $endPos && !feof($fh)) {
yield $raw = fgets($fh);
}
echo '進(jìn)程' . $index . '結(jié)束時(shí) 指針位置:' . ftell($fh) . ', 應(yīng)該到:' . $endPos . PHP_EOL;
fclose($fh);
}
foreach(range(0,$maxProcess - 1) as $index){
// 多線程采用多線程的方式創(chuàng)建,這里采用yield回調(diào)。
foreach(processRead($filename, $index, $singleProcessLength) as $value){
// $value為每一行的內(nèi)容,處理后釋放
unset($value);
}
}
如上所述就是PHP的大文件讀取解決方案,大部分用于多線程讀取大文件場(chǎng)景。
總結(jié)
以上是生活随笔為你收集整理的php并发取源码,PHP读取大文件源码示例-Swoole多进程读取大文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C指针原理(17)-C指针基础
- 下一篇: php将get传参解析成数组,php解析