请问大家一个问题,小弟想实现一个文件对比的功能,按照文本固定一列为关联列,来对比其他列是否相同,并输出不同的、文件 1 不存在关联列的、文件 2 不存在关联列的所有内容

2019-06-03 14:16:24 +08:00
 praynise
对这个需求,我举个栗子如下:
文件 1 和文件 2 均含有客户 ID、客户年龄以及客户地址,要找出以下 3 种数据:
1、客户在文件 1、文件 2 都存在,但是地址或年龄至少有一个属性不同(变化数据)
2、客户在文件 1 中存在,但是文件 2 中不存在(新增数据)
3、客户在文件 2 中存在,但是文件 1 中不存在(删除数据)

大概意思就是这样。这个需求实际上用数据库的话很好解决,但是碍于环境限制,无法将数据装载入数据库中再进行关联对比 /(ㄒoㄒ)/~~,必须直接在外部进行对比。每天处理的文件大概在 800G 左右,文件数量大概在 200 左右,最大文件大概在 50G 左右,设备为 8Core CPU,内存 32G。文件比较大,对性能也有一些要求。随意想请教大家,是否有什么好的实现思路,如何对比能够高效。非常感谢大家~
2606 次点击
所在节点    Go 编程语言
12 条回复
dongyx
2019-06-03 14:44:52 +08:00
直接用 diff 能够处理吗?
praynise
2019-06-03 15:41:42 +08:00
@dongyx 直接用 diff 的话,需要解析 diff 输出内容,会比较麻烦
lonelinsky
2019-06-03 16:01:20 +08:00
先外部 sort 排序之后,然后两个文件同时读着处理下? 不过文件有点大,不太确定 sort 命令的性能。
liprais
2019-06-03 16:07:25 +08:00
不能用数据库就学学数据库怎么做的呗
文件 1 文件 2 都按年龄分桶,然后每个桶对应的去比较呗
你地址格式化的好的话也可以先按地址分桶再按年龄分桶,然后随便搞个 hash 查找就行了
或者你可以用 pandas........
praynise
2019-06-04 10:26:42 +08:00
@liprais pandas 加载太大的文件内存就撑不住了 orz
liprais
2019-06-04 10:44:55 +08:00
@praynise 分好桶之后再用呗
dongyx
2019-06-04 11:22:50 +08:00
@praynise 哦哦,那你可以这样做,先把两个文件按照客户 id 做外排序。然后利用类似于归并排序的归并过程。

我给你准备了一个简单的 sample:

$ cat before.txt
Smith 26 Beijing
Sam 47 Washington
Mary 15 Tokyo

$ cat after.txt
Smith 26 Beijing
Sam 57 Washington
Nancy 21 Paris

这个 Sample 里,新增了 Nancy,删除了 Mary,修改了 Sam。

先做外排序:

$ sort -k1 before.txt > sorted_before.txt
$ sort -k1 after.txt > sorted_after.txt

然后利用归并过程来实现你的需求,具体是这样的:

两个迭代器或者指针 fp_before, fp_after 分别指向 sorted_before.txt 和 sorted_after.txt 的第一行,进行如下迭代:

1. 如果 before_key > after_key,说明 after 中的第一行是新增的,输出结果,fp_after 移动到下一行
2. 如果 before_key < after_key,如果 before 中的第一行被删除了,输出结果,fp_before 移动到下一行
3. 如果 before_key == after_key,两个指针都移动下一行,但是移动前先对比当前两条记录,如果不一致,说明修改了

迭代完后,如果 fp_before 没有到文件末尾,那么剩下的内容都会被删除的记录;如果 fp_after 没有到末尾,那么剩下的内容都会新增的记录。

很遗憾,我不会 Go,用 Python3 写了份代码供你参考,时间有限写得不好请见谅:

#!/usr/env/python3
# -*- coding: utf-8 -*-

with open("sorted_before.txt") as fp_before, \
open("sorted_after.txt") as fp_after:

before = fp_before.readline()
after = fp_after.readline()

while before and after:
before = before.rstrip()
after = after.rstrip()

before_key = before.split()[0]
after_key = after.split()[0]

if after_key < before_key:
print("+", after)
after = fp_after.readline()

elif after_key > before_key:
print("-", before)
before = fp_before.readline()

else:
if before != after:
print(' ', before, "=>", after)
before = fp_before.readline()
after = fp_after.readline()

for before_surplus in fp_before:
print("-", before_surplus)

for after_surplus in fp_after:
print("+", after_surplus)

这份代码跑上面的 sample 的结果:

- Mary 15 Tokyo
+ Nancy 21 Paris
Sam 47 Washington => Sam 57 Washington
dongyx
2019-06-04 11:24:46 +08:00
Python 的缩进粘贴过来没有了,我给个 gist 吧: https://gist.github.com/dongyx/a052cadf160896b879cdfb4d48e2f038
dongyx
2019-06-04 11:32:34 +08:00
typo:

2. 如果 before_key < after_key,*如果* before 中的第一行被删除了,输出结果,fp_before 移动到下一行

=>

2. 如果 before_key < after_key,*说明* before 中的第一行被删除了,输出结果,fp_before 移动到下一行
praynise
2019-06-04 13:09:21 +08:00
@dongyx 我用 go 实现以下,非常感谢~
dongyx
2019-06-10 12:18:07 +08:00
@praynise 你那边试了吗?能不能解决问题?我对这个问题挺感兴趣的,很希望能得到你的后续反馈。
praynise
2019-06-14 08:15:10 +08:00
@dongyx 抱歉由于工作原因,我这边还没有抽出时间实现这个对比,后续待我完成给您后续反馈,非常感谢~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/570320

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX