温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PHP中Swoole多进程读取大文件示例

发布时间:2021-06-22 14:21:37 来源:亿速云 阅读:396 作者:chen 栏目:大数据

这篇文章主要讲解了“PHP中Swoole多进程读取大文件示例”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP中Swoole多进程读取大文件示例”吧!

PHP读取大文件源码示例,通过PHP读取过大、超大型文件的思路及解决方案。

在日常读取文件时,若文件 不是很大,通常使用file_get_contents,将内容一次性载入的变量中,也可以远程加载网页或者远端文件。

若加载超过PHP限制的内存大小,或者超过本机内存大小的文件进程就会报错或者崩掉。

为了解决这个问题,我们采用使用完毕并释放的原则来读取大文件。

单线程读入

如果不考虑多线程的情况下,单线程读取大文件采用while fread就可以实现。

如下代码

$handle = fopen("./big.txt", "rb");
while (!feof($handle)) {
  $contents = fread($handle, 8192);
  // 业务处理
  unset($contents); // 释放掉变量
}
fclose($handle);

feof是判断是否到文件尾,如果没有到文件尾,则会一直while循环,并执行读取操作。每次读取8192字节,然后使用过后将其释放掉。

往往很多文件并不是一行的,有多行内容。需要将每行内容读取出来当做一条数据处理,也可以使用fgets。

即如下代码:

$handle = fopen("./big.txt", "rb");
while (!feof($handle)) {
  $contents = fgets($handle, 1024);
  // 业务处理
  unset($contents); // 释放掉变量
}
fclose($handle);

这里的fgets第二个参数,默认为1024字节。即默认读取一行数据,如果一行数据小于1024字节,则完整读取。如果超出1024字节,则只取前1024字节。遇到换行符"\n"或者"\r\n"或者结束符会停止读取。

如果遇到变态的文件,很多行都只有1000长度,某一行有8000长度,如果在不清楚的情况下,就很难掌控,若要完整读取就需要指定读取的最大字节,8000才能将每一行完整读取。

还有一种方法就是自己处理换行符。默认读取1024字节,然后放置到内存中,使用过后再将其释放。这种操作很节省内存,但是在逻辑处理上需要自己处理换行符。

如,读取1024字节,没有换行符,则保存数据继续读取。再读取1024字节,判断其中是否有换行符,如果有则处理最开始到换行符中的数据。再将剩下的数据保存,等待下一次读取,直到整个文件读取完毕。

$handle = fopen("./big.txt", "rb");
$contents = "";
while (!feof($handle)) {
  $contents .= fread($handle, 8192);

  // 判断读取到的内容是否包含换行符,包含则进入循环体
  while(strpos($contents, "\n") !== false){
      $eol_pos= strpos($contents, "\n");
      $line = substr($contents, 0, $eol_pos);
      // $line为一行的数据,进行业务处理,并释放
      unset($line);

      $contents = substr($eol_pos, 0);// 将剩余内容放置到变量中以供下次使用
  }
}
fclose($handle);

多线程读取

PHP默认没有多线程,这里可以采用多进程的方式实现,或者swoole的多进程来实现。

例如读取一个8GB文件,分8个线程,每个线程读取1GB数据内容。或者更多线程进行拆分工作内容。

获取文件大小

首先第一步,就是获取整个文件的体积大小,然后计算每个线程应该负责处理的一部分内容。

function length($filename)
{
	$handle = fopen($filename, "rb");
	$currentPos = ftell($handle);
	fseek($handle, 0, SEEK_END);
	$length = ftell($handle);
	fseek($handle, $currentPos);
	// $length 文件总长度
	return $length;
}
echo length("./big.txt");

处理逻辑实现过程

这里主要说明下作了哪些内容,首先是设定分配总的线程数。然后根据设置的线程数,计算每个线程要读取的数据大小,即从哪里开始读,读到哪里结束。

然后必定会出现拆分后,读到不完整行的情况,在这里来解决这种前半行或者后半行的意外情况。

解决逻辑就是,假设线程开始的读取位置在某一行的中间,我们一个字符向前移动,移动到上个换行符(也可能是最开始)即可获取到整行文本内容。

处理掉残行数据之后,使用yield来传递数据给业务处理。

$filename = "./big.txt";
$maxProcess = 8;// 分配8个线程


$length = length($filename);
$singleProcessLength = ceil($length / $maxProcess);

// 线程负责读取的内容
function processRead($filename, $index, $singleProcessLength)
{
	$fh = fopen($filename, 'r');
	
	$beginPos = $index * $singleProcessLength;
	//结束位置=线程序列*线程处理数据长度+线程处理数据 - 1 (长度转指针,实际结束指针小于结束长度)
	$endPos = $index * $singleProcessLength + $singleProcessLength - 1;

	fseek($fh, $beginPos);
	echo '线程:' . $index . ',起始位置:' . $beginPos . ',结束位置:' . $endPos . PHP_EOL;
	//移动到上个\n 以便首次顺利获取整行内容
	while (fseek($fh, -1, SEEK_CUR) === 0) {
		if (fread($fh, 1) == "\n" || ftell($fh) <= 0) {
			break;
		}
		fseek($fh, -1, SEEK_CUR);
	}
	echo '线程:' . $index . ',移动完毕!!!!!' . PHP_EOL;

	//整行读取数据
	//结束时位置超过预计结束位置是正常状况,fgets 读取一整行内容
	//预计结束位置可能在行内,所以产生不同结果。
	while (ftell($fh) <= $endPos && !feof($fh)) {
		yield $raw = fgets($fh);
	}
	echo '进程' . $index . '结束时 指针位置:' . ftell($fh) . ', 应该到:' . $endPos . PHP_EOL;
	fclose($fh);
}

foreach(range(0,$maxProcess - 1) as $index){
	// 多线程采用多线程的方式创建,这里采用yield回调。
	foreach(processRead($filename, $index, $singleProcessLength) as $value){
        // $value为每一行的内容,处理后释放
        unset($value);
    } 
}

感谢各位的阅读,以上就是“PHP中Swoole多进程读取大文件示例”的内容了,经过本文的学习后,相信大家对PHP中Swoole多进程读取大文件示例这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI