1、引言

给定a,b两个文件, 分别有x,y行数据, 其中(x, y均大于10亿), 机器内存限制100M,该如何找出其中相同的记录?

2、思路

  • 处理该问题的困难主要是无法将这海量数据一次性读进内存中.

  • 一次性读不进内存中,那么是否可以考虑多次呢?如果可以,那么多次读入要怎么计算相同的值呢?

  • 我们可以用分治思想, 大而化小。相同字符串的值hash过后是相等的, 那么我们可以考虑使用hash取模, 将记录分散到n个文件中。这个n怎么取呢?PHP 100M内存,数组大约可以存100w的数据, 那么按a,b记录都只有10亿行来算, n至少要大于200。

  • 此时有200个文件,相同的记录肯定在同一个文件中,并且每个文件都可以全部读进内存。那么可以依次找出这200个文件中各自相同的记录,然后输出到同一个文件中,得到的最终结果就是a, b两个文件中相同的记录。

  • 找一个小文件中相同的记录很简单了吧,将每行记录作为hash表的key, 统计key的出现次数>=2就可以了。

3、实操

10亿个文件太大了,实操浪费时间,达到实践目的即可。

问题规模缩小为: 1M内存限制, a, b各有10w行记录, 内存限制可以用PHP的ini_set('memory_limit', '1M');来限制。

4、生成测试文件

生成随机数用于填充文件:

/**
* 生成随机数填充文件
* Author: ClassmateLin
* Email: classmatelin.site@gmail.com
* Site: https://www.classmatelin.top
* @param string $filename 输出文件名
* @param int $batch 按多少批次生成数据
* @param int $batchSize 每批数据的大小
*/
function generate(string $filename, int $batch=1000, int $batchSize=10000)
{
for ($i=0; $i<$batch; $i++) {
$str = '';
for ($j=0; $j<$batchSize; $j++) {
$str .= rand($batch, $batchSize) . PHP_EOL; // 生成随机数
}
file_put_contents($filename, $str, FILE_APPEND); // 追加模式写入文件
}
} generate('a.txt', 10);
generate('b.txt', 10);

5、分割文件

  • a.txtb.txt通过hash取模的方式分割到n个文件中.

    /**
    * 用hash取模方式将文件分散到n个文件中
    * Author: ClassmateLin
    * Email: classmatelin.site@gmail.com
    * Site: https://www.classmatelin.top
    * @param string $filename 输入文件名
    * @param int $mod 按mod取模
    * @param string $dir 文件输出目录
    */
    function spiltFile(string $filename, int $mod=20, string $dir='files')
    {
    if (!is_dir($dir)){
    mkdir($dir);
    } $fp = fopen($filename, 'r'); while (!feof($fp)){
    $line = fgets($fp);
    $n = crc32(hash('md5', $line)) % $mod; // hash取模
    $filepath = $dir . '/' . $n . '.txt'; // 文件输出路径
    file_put_contents($filepath, $line, FILE_APPEND); // 追加模式写入文件
    } fclose($fp);
    } spiltFile('a.txt');
    spiltFile('b.txt');
  • 执行splitFile函数, 得到如下图files目录的20个文件。

6、查找重复记录

现在需要查找20个文件中相同的记录, 其实也就是找一个文件中的相同记录,操作个20次。

  • 找一个文件中的相同记录:

    /**
    * 查找一个文件中相同的记录输出到指定文件中
    * Author: ClassmateLin
    * Email: classmatelin.site@gmail.com
    * Site: https://www.classmatelin.top
    * @param string $inputFilename 输入文件路径
    * @param string $outputFilename 输出文件路径
    */
    function search(string $inputFilename, $outputFilename='output.txt')
    {
    $table = [];
    $fp = fopen($inputFilename, 'r'); while (!feof($fp))
    {
    $line = fgets($fp);
    !isset($table[$line]) ? $table[$line] = 1 : $table[$line]++; // 未设置的值设1,否则自增
    } fclose($fp); foreach ($table as $line => $count)
    {
    if ($count >= 2){ // 出现大于2次的则是相同的记录,输出到指定文件中
    file_put_contents($outputFilename, $line, FILE_APPEND);
    }
    }
    }
  • 找出所有文件相同记录:
    /**
    * 从给定目录下文件中分别找出相同记录输出到指定文件中
    * Author: ClassmateLin
    * Email: classmatelin.site@gmail.com
    * Site: https://www.classmatelin.top
    * @param string $dirs 指定目录
    * @param string $outputFilename 输出文件路径
    */
    function searchAll($dirs='files', $outputFilename='output.txt')
    {
    $files = scandir($dirs); foreach ($files as $file)
    {
    $filepath = $dirs . '/' . $file;
    if (is_file($filepath)){
    search($filepath, $outputFilename);
    }
    }
    }
  • 到这里已经解决了大文件处理的空间问题,那么时间问题该如何处理? 单机可通过利用CPU的多核心处理,不够的话通过多台服务器处理。

7、完整代码

<?php
ini_set('memory_limit', '1M'); // 内存限制1M /**
* 生成随机数填充文件
* Author: ClassmateLin
* Email: classmatelin.site@gmail.com
* Site: https://www.classmatelin.top
* @param string $filename 输出文件名
* @param int $batch 按多少批次生成数据
* @param int $batchSize 每批数据的大小
*/
function generate(string $filename, int $batch=1000, int $batchSize=10000)
{
for ($i=0; $i<$batch; $i++) {
$str = '';
for ($j=0; $j<$batchSize; $j++) {
$str .= rand($batch, $batchSize) . PHP_EOL; // 生成随机数
}
file_put_contents($filename, $str, FILE_APPEND); // 追加模式写入文件
}
} /**
* 用hash取模方式将文件分散到n个文件中
* Author: ClassmateLin
* Email: classmatelin.site@gmail.com
* Site: https://www.classmatelin.top
* @param string $filename 输入文件名
* @param int $mod 按mod取模
* @param string $dir 文件输出目录
*/
function spiltFile(string $filename, int $mod=20, string $dir='files')
{
if (!is_dir($dir)){
mkdir($dir);
} $fp = fopen($filename, 'r'); while (!feof($fp)){
$line = fgets($fp);
$n = crc32(hash('md5', $line)) % $mod; // hash取模
$filepath = $dir . '/' . $n . '.txt'; // 文件输出路径
file_put_contents($filepath, $line, FILE_APPEND); // 追加模式写入文件
} fclose($fp);
} /**
* 查找一个文件中相同的记录输出到指定文件中
* Author: ClassmateLin
* Email: classmatelin.site@gmail.com
* Site: https://www.classmatelin.top
* @param string $inputFilename 输入文件路径
* @param string $outputFilename 输出文件路径
*/
function search(string $inputFilename, $outputFilename='output.txt')
{
$table = [];
$fp = fopen($inputFilename, 'r'); while (!feof($fp))
{
$line = fgets($fp);
!isset($table[$line]) ? $table[$line] = 1 : $table[$line]++; // 未设置的值设1,否则自增
} fclose($fp); foreach ($table as $line => $count)
{
if ($count >= 2){ // 出现大于2次的则是相同的记录,输出到指定文件中
file_put_contents($outputFilename, $line, FILE_APPEND);
}
}
} /**
* 从给定目录下文件中分别找出相同记录输出到指定文件中
* Author: ClassmateLin
* Email: classmatelin.site@gmail.com
* Site: https://www.classmatelin.top
* @param string $dirs 指定目录
* @param string $outputFilename 输出文件路径
*/
function searchAll($dirs='files', $outputFilename='output.txt')
{
$files = scandir($dirs); foreach ($files as $file)
{
$filepath = $dirs . '/' . $file;
if (is_file($filepath)){
search($filepath, $outputFilename);
}
}
} // 生成文件
generate('a.txt', 10);
generate('b.txt', 10); // 分割文件
spiltFile('a.txt');
spiltFile('b.txt'); // 查找记录
searchAll('files', 'output.txt');

摘自:https://mp.weixin.qq.com/s/S1OOVhHtfyVDsWVXd-nORQ

PHP如何在两个大文件中找出相同的记录?的更多相关文章

  1. BD面试题1-两个大文件中找出公共记录[转载]

    转自:https://blog.csdn.net/tiankong_/article/details/77234726#commentBox 1.题目 给定a.b两个文件,各存放50亿个url,每个u ...

  2. 如何在 Linux 中找出最近或今天被修改的文件

    1. 使用 ls 命令,只列出你的 home 文件夹中今天的文件. ls -al --time-style=+%D | grep `date +%D` 其中: -a- 列出所有文件,包括隐藏文件 -l ...

  3. C语言:对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中。-在数组中找出最小值,并与第一个元素交换位置。

    //对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中. #include <stdio.h& ...

  4. 在一个数组中,除了两个数外,其余数都是两两成对出现,找出这两个数,要求时间复杂度O(n),空间复杂度O(1)

    题目:在一个数组中,除了两个数外,其余数都是两两成对出现,找出这两个数,要求时间复杂度O(n),空间复杂度O(1) 分析:这道题考察位操作:异或(^),按位与(&),移位操作(>> ...

  5. 刷题之给定一个整数数组 nums 和一个目标值 taget,请你在该数组中找出和为目标值的那 两个 整数

    今天下午,看了一会github,想刷个题呢,就翻出来了刷点题提高自己的实际中的解决问题的能力,在面试的过程中,我们发现,其实很多时候,面试官 给我们的题,其实也是有一定的随机性的,所以我们要多刷更多的 ...

  6. EXCELL中怎么将两列数据对比,找出相同的和不同的数据?

    假设你要从B列中找出A列里没有的数据,那你就在C1单元格里输入“=IF(ISNA(VLOOKUP(B1,A:A,1,0)),"F","T")”显示T就表示有,F ...

  7. 海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)

    前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小堆比较好一些. 先拿10000个数建堆, ...

  8. 海量数据中找出前k大数(topk问题)

    海量数据中找出前k大数(topk问题) 前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小 ...

  9. Leetcode33--->Search in Rotated Sorted Array(在旋转数组中找出给定的target值的位置)

    题目: 给定一个旋转数组,但是你不知道旋转位置,在旋转数组中找出给定target值出现的位置:你可以假设在数组中没有重复值出现 举例: (i.e., 0 1 2 4 5 6 7 might becom ...

  10. C语言:从p所指字符串中找出ASCII码最大的字符,将其放在第一个位置上,并将该字符前的原字符向后顺序移动。-使字符串的前导*号不得多于n个,若多余n个,则删除多余的*号,

    //fun函数:从p所指字符串中找出ASCII码最大的字符,将其放在第一个位置上,并将该字符前的原字符向后顺序移动. #include <stdio.h> void fun( char * ...

随机推荐

  1. ubuntu20.04系统中扩展swap分区

    1.首先停止/swapfile #swapon /swapfile 2.删除以前的/swapfile #rm -rf swapfile 3.创建新的/swapfile(以2G为例) #dd if=/d ...

  2. Android Studio 修改jar包中的.class代码

    修改a.jar中的b.class文件: 1.在Android Studio的项目中引入a.jar,sync,通过反编译找到并拷贝b.java的代码 2.使用压缩软件打开a.jar,并删掉需要修改的文件 ...

  3. linux清除恶意程序流程-kdevtmpfsi清除

    TOP命令查看发现kdevtmpfsi进程跑满CPU, 处理如下: 解决过程 1.清除被新增的用户名和密码# 找到账户ID和权限组都是0跟root同级别的和不认识的, 删掉保存.   more /va ...

  4. serializers序列化函数简单入门

    1. 创建Django项目和应用程序 首先,我们需要创建一个Django项目和一个Django应用程序.如果你已经有了Django项目和应用程序,请跳过这一步. $ django-admin star ...

  5. 2022-04-12内部群每日三题-清辉PMP

    1.一个项目的成本绩效指数(CPI)为1.2,且关键路径上的一个可交付成果落后于进度. 如果项目经理将项目回正轨,项目会发生什么情况? A.活动将并行执行 B.范围将被修改 C.成本和风险将会增加 D ...

  6. vue的表单

    你可以用 v-model 指令在表单控件元素上创建双向数据绑定. 输入框 实例中演示了 input 和 textarea 元素中使用 v-model 实现双向数据绑定: <!DOCTYPE ht ...

  7. kora 简单使用实现Api接口 以及mongodb简单使用

    //api.jsconst Koa = require('koa'); const router = require('koa-router')(); //路由 const bodyParser = ...

  8. PostScript语言教程(七、条件语句)

    POSTSCRIPT语言中有许多操作符用于制定程序内流的控制.我们在前一章使用了一个repeat运算.所有的控制操作符都使用了之前简要提到的对象类型,即函数,用于我们调用. 7.1.函数 函数(既过程 ...

  9. 解决href 不下载直接跳转到新的页面

    1.下载 不支持所有浏览器 2 var eleTextarea = document.querySelector('textarea'); var eleButton = document.query ...

  10. bzoj 4176

    题意:求$\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$ 首先推一发式子: $\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$ 有一个结论:$d(nm)=\s ...