加入收藏 | 设为首页 | 会员中心 | 我要投稿 航空爱好网 (https://www.ikongjun.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

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

发布时间:2022-10-26 14:55:41 所属栏目:PHP教程 来源:
导读:  这篇文章主要讲解了“PHP中Swoole多进程读取大文件示例”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PHP中Swoole多进程读取大文件示例&rd
  这篇文章主要讲解了“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是判断是否到文件尾,如果没有到文件尾php单线程,则会一直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多进程读取大文件示例这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。
 

(编辑:航空爱好网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!