PHP如何在两个大文件中找出相同的记录?
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.txt,b.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如何在两个大文件中找出相同的记录?的更多相关文章
- BD面试题1-两个大文件中找出公共记录[转载]
转自:https://blog.csdn.net/tiankong_/article/details/77234726#commentBox 1.题目 给定a.b两个文件,各存放50亿个url,每个u ...
- 如何在 Linux 中找出最近或今天被修改的文件
1. 使用 ls 命令,只列出你的 home 文件夹中今天的文件. ls -al --time-style=+%D | grep `date +%D` 其中: -a- 列出所有文件,包括隐藏文件 -l ...
- C语言:对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中。-在数组中找出最小值,并与第一个元素交换位置。
//对传入sp的字符进行统计,三组两个相连字母“ea”"ou""iu"出现的次数,并将统计结果存入ct所指的数组中. #include <stdio.h& ...
- 在一个数组中,除了两个数外,其余数都是两两成对出现,找出这两个数,要求时间复杂度O(n),空间复杂度O(1)
题目:在一个数组中,除了两个数外,其余数都是两两成对出现,找出这两个数,要求时间复杂度O(n),空间复杂度O(1) 分析:这道题考察位操作:异或(^),按位与(&),移位操作(>> ...
- 刷题之给定一个整数数组 nums 和一个目标值 taget,请你在该数组中找出和为目标值的那 两个 整数
今天下午,看了一会github,想刷个题呢,就翻出来了刷点题提高自己的实际中的解决问题的能力,在面试的过程中,我们发现,其实很多时候,面试官 给我们的题,其实也是有一定的随机性的,所以我们要多刷更多的 ...
- EXCELL中怎么将两列数据对比,找出相同的和不同的数据?
假设你要从B列中找出A列里没有的数据,那你就在C1单元格里输入“=IF(ISNA(VLOOKUP(B1,A:A,1,0)),"F","T")”显示T就表示有,F ...
- 海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)
前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小堆比较好一些. 先拿10000个数建堆, ...
- 海量数据中找出前k大数(topk问题)
海量数据中找出前k大数(topk问题) 前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小 ...
- Leetcode33--->Search in Rotated Sorted Array(在旋转数组中找出给定的target值的位置)
题目: 给定一个旋转数组,但是你不知道旋转位置,在旋转数组中找出给定target值出现的位置:你可以假设在数组中没有重复值出现 举例: (i.e., 0 1 2 4 5 6 7 might becom ...
- C语言:从p所指字符串中找出ASCII码最大的字符,将其放在第一个位置上,并将该字符前的原字符向后顺序移动。-使字符串的前导*号不得多于n个,若多余n个,则删除多余的*号,
//fun函数:从p所指字符串中找出ASCII码最大的字符,将其放在第一个位置上,并将该字符前的原字符向后顺序移动. #include <stdio.h> void fun( char * ...
随机推荐
- Cesium 结合Echarts绘制航线图
<template> <div id="cesiumContainer"></div> </template> <script ...
- 百题计划-1 codeforces1181C Flag 暴力模拟
https://codeforces.com/contest/1181/problem/C 题意:给一个n*m的格子(n,m<=1000),每个格子有个颜色,求可以条纹子矩阵的数量. 条纹矩阵就 ...
- centos8.5安装kvm及kvm虚拟机的端口映射问题
1.安装KVM grep -Ei 'vmx|svm' /proc/cpuinfo|more #查看硬件是否支持虚拟化 yum install -y virt-* libvirt qemu-img ...
- v3.x compoistion api 是什么
在Vue 2.x的基础上要解决的问题: 1. 更低成本的达到逻辑复用(mix in :混入,找不着,命名冲突) 2. 代码组织 3. 类型推导更稳定 目标:使得Vue在大型应用中更有竞争力.
- 【Python】变量&数据类型&运算符
一.Python变量&数据类型&运算符 1.print()函数 1.1 输出数字,字符串,含有运算符的表达式 print(123) print('hello world') print ...
- lxml.etree.XMLSyntaxError: Opening and ending tag mismatch: meta line 4 and head, line 6, column 12 报错分析与解决方案
报错分析: 我们检查代码没有任何问题,但报错显示:开始和结束标记不匹配. html:因为html是超文本标记语言,代码不规范也能解析. python:python是编程语言,代码不规范则解析不了. 解 ...
- 关于前端:解决elementUI的对话框Dialog组件点击自动跳转到页面顶部问题
查看文档寻找解决办法 增加这两个参数即可完满解决 <el-dialog :lock-scroll="false" :append-to-body="true&quo ...
- Python自动化测试更新selenium的两种方式
第一种手动实现: 来源 https://www.codeleading.com/article/73395290617/ import os import re import winreg impor ...
- es6数组去重、数组中的对象去重 && 删除数组(按条件或指定具体元素 如:id)&& 筛选去掉没有子组件的父组件
// 数组去重 { const arr = [1,2,3,4,1,23,5,2,3,5,6,7,8,undefined,null,null,undefined,true,false,true,'中文' ...
- python import导入失败 相对路径 绝对路径
首先我们有这样一个目录结构 经过我无数次的调试,我发现从test3调用spider以及downloadss文件可以直接import调用 但是从外部的app.py文件调用,spider.py文件死活调用 ...
