PHP有限内存处理大文件(从两个文件提取相同行)

面对超过1G的大文件,使用PHP在内存限制256M的情况下找出两文件的相同行。通过split命令分割文件,然后用PHP逐个对比小文件,利用array_intersect()获取交集,最终合并结果并去重。

面试题:

有两个文件文件,大小都超过了1G,一行一条数据,每行数据不超过500字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP最大允许内内为256M。

 

解题步骤

遇到此问题,首先先尝试从小范围解决,比如题目中,要求查找两个文件相同的行,那么我们可以先读取文件到内存中(数组),然后通过array_intersect()函数获取两个数组的交集,即为相同的行,此时,小范围的问题已经找到解决方法了。

再回到问题本身,两个文件都超过1G,PHP最大使用内存是256M(也有可能是128M,甚至10M,这不用担心),在此条件下,直接读取文件到内存,显然不可行,我们可以考虑使用split命令将其分割成若干个小文件,再运用上面的思路逐个对比小文件,得到的结果,再合并到结果集里面,然后再去重,即得到题目要求。

步骤一、通过php生成两个大容量数据文件

<?php

function build_file($max)
{

        $file1 = fopen("/tmp/file1.txt", "w");
        $file2 = fopen("/tmp/file2.txt", "w");
        for($i =0; $i < $max; $i++) {
                $text1 = md5(microtime() . rand(100, 999)) . "\r\n";
                $text2 = md5(microtime() . rand(100, 999)) . "\r\n";
                fwrite($file1, $text1);
                fwrite($file2, $text2);
        }

        fclose($file1);
        fclose($file2);

        return true;
}

//同时生成两个拥有100万行测试数据的文本文件
build_file(1000000);

 

步骤二、通过split命令分割数据文件为若干个小文件

mkdir /tmp/split1
mkdir /tmp/split2

cd /tmp/split1
split -l 10000 /tmp/file1.txt

cd /tmp/split2
split -l 10000 /tmp/file2.txt


 

ll -h /tmp/split1/ 

total 33M
-rw-r--r-- 1 root root 333K Jul  8 17:18 xaa
-rw-r--r-- 1 root root 333K Jul  8 17:18 xab
-rw-r--r-- 1 root root 333K Jul  8 17:18 xac
-rw-r--r-- 1 root root 333K Jul  8 17:18 xad
-rw-r--r-- 1 root root 333K Jul  8 17:18 xae
-rw-r--r-- 1 root root 333K Jul  8 17:18 xaf
-rw-r--r-- 1 root root 333K Jul  8 17:18 xag
-rw-r--r-- 1 root root 333K Jul  8 17:18 xah
-rw-r--r-- 1 root root 333K Jul  8 17:18 xai
-rw-r--r-- 1 root root 333K Jul  8 17:18 xaj
-rw-r--r-- 1 root root 333K Jul  8 17:18 xak
......

 步骤三、编写具体实现逻辑


<?php


ini_set("memory_limit", "128m");


//读取文件内容到数组
function read($file, $max = 0)
{
        $rows = [];
        $handle = fopen($file, "r");
        while(!feof($handle)) {
                $rows[] = fgets($handle, 1024);
        }
        fclose($handle);

        return $rows;
}


//遍历某个路径,读取所有文件名
function read_dir($dir)
{
        $files = [];
        $handle = opendir($dir);

        while(false !== ($file = readdir($handle))) {
                if ($file == '.' || $file == '..') continue;
                $files[] = $dir . $file;
        }

        closedir($handle);
        return $files;
}


$dir1 = "/tmp/split1/";
$dir2 = "/tmp/split2/";

$files1 = read_dir($dir1);
$files2 = read_dir($dir2);


$result = [];
//遍历子文件,分别对比,获取子文件交集,再merge结果
foreach ($files1 as $f1) {
        $data1 = read($f1);
        foreach ($files2 as $f2) {
                $data2 = read($f2);
                $retval = [];
                $retval = array_intersect($data1, $data2);
                if (array_values($retval)[0] !== false){
                        $result = array_merge($result, $retval);
                }
        }
}


var_dump(array_unique($result));
die;

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值