php 处理 10W 及以上csv 文件,使用生成器 yield

  前几天有个任务是要解密excel中某个字段,本来是一个非常简单的事情,但由于Excel近10M、有几万数据,使用phpexcel一直load不了,内存和运行时间都已经设为最大,仍然处理不完,尤其是内存,一直会爆掉,而且进程所占CPU爆满,最后各种问度娘,最后想到了前段时间看的生成器 yield, 刚好是一个测试的机会

  

class Qushu
{
    public function getDg(){
        set_time_limit(0);

        $file = request()->get('file');
        $path = 'D:/path/'.$file.'.csv';

        $key = '********';

        $header = ['订单','姓名','电话','地址','证件号','机构','社会代码','税务','测试','测试电话','得知','物品名称'];

        $output = $this->csvSet("导出表名",$header);

        $data = $this->getCsv($path);

        $i = 0;
        foreach ($data as $k=>$v)
        {
            if (!$v) break;

            $idcard = iconv('GBK',"UTF-8//TRANSLIT//IGNORE",$v[4]);
            // 根据条件判断是否要处理字段
            if (!empty($idcard) && strlen($idcard)>18){
                // 处理字段的code....
                $v[4] = '';
            }

            //输出csv内容
            fputcsv($output, array_values($v));

            $i++;
        }
        //关闭文件句柄
        fclose($output) or die("can't close php://output");
        exit;


    }

    /**
    *获取csv内容 使用 yield
    */
    public function getCsv($fname)
    {

        $handle = fopen("$fname", 'rb');

        while (feof($handle)===false) {
            # code...
            yield fgetcsv($handle);
        }

        fclose($handle);
    }

    /**设置csv*/
    public function csvSet($name,$head)
    {
        try {
//为fputcsv()函数打开文件句柄
            $output = fopen('php://output', 'w') or die("can't open php://output");

            //告诉浏览器这个是一个csv文件
            header("Content-Type: application/csv");
            header("Content-Disposition: attachment; filename=$name.csv");
            header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
            header('Expires:0');
            header('Pragma:public');

            // 文件名转码
            $name = iconv('utf-8', 'gbk', $name);

            //输出表头
            foreach ($head as $i => $v) {
                //CSV的Excel支持GBK编码,一定要转换,否则乱码
                $head[$i] = iconv('utf-8', 'gbk', $v);

            }

            fputcsv($output, $head);

            return $output;
        }catch (Exception $e){

        }
    }
}  

  从上面可以看出,只是通过 yield 标识就处理好了一个生成器,调用了 getCsv 方法获取到一个迭代器,那么通过循环此迭代器,进行逻辑操作即可。

注意:

  1. 虽然yield节约了运行内存,但是运行时间仍然需要,因而需要设置运行时间

总结:

  1. 生成器,提供了一种更容易的方法实现迭代,性能开销和复杂性大大降低
  2. 生成器函数看起来像普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值,并且每一次的生成返回值只是暂停当前的执行状态,当下次调用生成器函数时,PHP会从上次暂停的状态继续执行下去
  3. 使用生成器处理大文件,由于yield 并不是一次取出全部数据,而是生成一个可以循环的迭代器,内部会为生成的值配对连续的整型索引,就像一个非关联的数组。每次循环,根据游标取指定的一条数据,节约内存资源,有效防止内存溢出

posted on 2018-06-11 17:55  殊珠子  阅读(718)  评论(0编辑  收藏  举报

导航