zoukankan      html  css  js  c++  java
  • 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 并不是一次取出全部数据,而是生成一个可以循环的迭代器,内部会为生成的值配对连续的整型索引,就像一个非关联的数组。每次循环,根据游标取指定的一条数据,节约内存资源,有效防止内存溢出
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 交换Easy
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法训练 矩阵乘方
    QT中给各控件增加背景图片(可缩放可旋转)的几种方法
    回调函数实现类似QT中信号机制
    std::string的Copy-on-Write:不如想象中美好(VC不使用这种方式,而使用对小字符串更友好的SSO实现)
  • 原文地址:https://www.cnblogs.com/wangfengzhu/p/9168338.html
Copyright © 2011-2022 走看看