python3 大文件去重

目录

一、生成待去重数据

二、通过set按行去重

1. 按原值比较

2. 按md5比较

三、二路归并


一、生成待去重数据

每行是固定位数的数字串

import os
from random import randint

#-- from u_工具 import *


print("———— 开始 ————")
#-- 打点()

# 用来配置的变量
位数 = 13
行数 = 500 * 10000

输出目录 = "./a_输入"
输出文件 = f"{输出目录}/随机数.txt"


# 预处理
_00 = "".join(["0" for i in range(位数 - 1)])
_100 = "1" + _00
最小值 = int(_100)
_1000 = _100 + "0"
最大值 = int(_1000)

if not os.path.exists(输出目录):
    os.makedirs(输出目录)
#-- 输出文件 = 文件名防重_追加数字(输出文件)


# 实际处理
with open(输出文件,"a") as f:
    for i in range(行数):

        f.write(f"{randint(最小值, 最大值)}\n")

        百分比 = (i+1) / 行数 * 100
        if 百分比 == int(百分比):
            print(f"已完成{int(百分比)}%")


#-- 打点()
#-- print(f"\n总耗时:{计时(0)}")
print("———— 结束 ————")

 

 

二、通过set按行去重

1. 按原值比较

(1)读取全部数据

(2)用split来分行

(3)通过set数据结构来去除重复数据

(4)将set的数据写入文件

import os

#-- from u_工具 import *


print("———— 开始 ————")
#-- 打点()

# 用来配置的变量
输入目录 = "./a_输入"
输出目录 = "./b_输出"
输出文件 = f"{输出目录}/去重结果.txt"


# 预处理
# 目录不存在就手动建立
if not os.path.exists(输出目录):
    os.makedirs(输出目录)
if not os.path.exists(输入目录):
    os.makedirs(输入目录)
#-- 输出文件 = 文件名防重_追加数字(输出文件)

# 获取待去重文件
待去重文件列表 = []
待去重文件列表 = [f"{输入目录}/{i}" for i in os.listdir(输入目录)]
#-- getDeepFilePaths(待去重文件列表,输入目录,"txt")
print(f"\n总共{len(待去重文件列表)}个文件")

换行符 = b"\n"
if platform.system().lower() == 'windows':
    换行符 = b"\r\n"


# 实际处理
all_lines = []
文件个数 = 0
for 文件 in 待去重文件列表:
    文件个数 += 1
    print(f"\n处理第{文件个数}个文件")

    #-- 打点()
    # (1)读全部
    with open(文件, "rb") as f:
        data = f.read()

    # (2)split分行
    lines = data.split(换行符)
    all_lines.extend(lines)
    #-- 打点()
    #-- print(f"分行完毕,耗时:{计时()}")


# (3)集合去重
all_lines_set = set(all_lines)
all_lines_set.remove(b"")
#-- 打点()
#-- print(f"\n\n去重完毕,耗时:{计时()}")

# (4)循环写入
with open(输出文件,"ab") as f_rst:
    for line in all_lines_set:
        f_rst.write(line + 换行符)
#-- 打点()
#-- print(f"\n写入完毕,耗时:{计时()}")
print(f"\n输出文件:{输出文件}")


#-- 打点()
#-- print(f"\n\n总耗时:{计时(0)}")
print("———— 结束 ————")

附:

(2)用正则表达式来分行

import re

# (2)正则分行 二进制的话要加b, b''' '''
regx = '''[\w\~`\!\@\#\$\%\^\&\*\(\)\_\-\+\=\[\]\{\}\:\;\,\.\/\<\>\?]+'''
lines = re.findall(regx, data)

 

 

2. 按md5比较

import hashlib
import os

#-- from u_工具 import *


print("———— 开始 ————")
#-- 打点()

# 用来配置的变量
输入目录 = "./a_输入"
输出目录 = "./b_输出"
输出文件 = f"{输出目录}/去重结果.txt"


# 预处理
# 目录不存在就手动建立
if not os.path.exists(输出目录):
    os.makedirs(输出目录)
if not os.path.exists(输入目录):
    os.makedirs(输入目录)
#-- 输出文件 = 文件名防重_追加数字(输出文件)

# 获取待去重文件
待去重文件列表 = [f"{输入目录}/{i}" for i in os.listdir(输入目录)]
#-- 待去重文件列表 = []
#-- getDeepFilePaths(待去重文件列表,输入目录,"txt")
print(f"\n总共{len(待去重文件列表)}个文件")

def gen_md5(data):
    md5 = hashlib.md5()
    if repr(type(data)) == "<class 'str'>":
        data = data.encode('utf-8')
    md5.update(data)
    return md5.hexdigest()


# 实际处理
md5集 = set()
with open(输出文件, "a") as f_rst:
    文件个数 = 0
    for 文件 in 待去重文件列表:
        文件个数 += 1
        print(f"\n处理第{文件个数}个文件")

        # 计算总行数
        with open(文件, 'rb') as f:
            行数 = 0
            buf_size = 1024 * 1024
            buf = f.read(buf_size)
            while buf:
                行数 += buf.count(b'\n')
                buf = f.read(buf_size)

        # 读取、分行、去重、写入
        #-- 打点()
        i = 0
        for line_带换行 in open(文件):
            i += 1
            line = line_带换行.strip()
            md5值 = gen_md5(line)
            if md5值 not in md5集:
                md5集.add(md5值)
                f_rst.write(line_带换行)

            百分比 = i / 行数 * 10
            if 百分比 == int(百分比):
                print(f"已完成{int(百分比)*10}%")
                #-- 打点()
                #-- print(f"耗时:{计时()}")

print(f"\n输出文件:{输出文件}")


#-- 打点()
#-- print(f"\n\n总耗时:{计时(0)}")
print("———— 结束 ————")

 

 

三、二路归并

 

import hashlib
import os
import platform
import queue
import shutil
from uuid import uuid1

from u_工具 import *


print("———— 开始 ————")
打点()

# 1.用来配置的变量
输入目录 = "./a_输入"
输出目录 = "./b_输出"
输出文件 = f"{输出目录}/去重结果.txt"
临时目录 = "./c_临时"
小文件大小 = 50 * 1024 * 1024  # 50M


# 2.预处理
# 目录不存在就手动建立
if not os.path.exists(输出目录):
    os.makedirs(输出目录)
if not os.path.exists(输入目录):
    os.makedirs(输入目录)
if not os.path.exists(临时目录):
    os.makedirs(临时目录)
shutil.rmtree(临时目录)
os.makedirs(临时目录)
输出文件 = 文件名防重_追加数字(输出文件)

# 获取待去重文件
# 待去重文件列表 = [f"{输入目录}/{i}" for i in os.listdir(输入目录)]
待去重文件列表 = []
getDeepFilePaths(待去重文件列表,输入目录,"txt")
print(f"总共{len(待去重文件列表)}个文件")

换行符 = b"\n"
if platform.system().lower() == 'windows':
    换行符 = b"\r\n"


# 3.实际处理

# (1)分割大文件
打点()
待排序文件列表 = []
待补全数据 = b""
for 文件 in 待去重文件列表:
    with open(文件, 'rb') as f:
        buf = f.read(小文件大小)
        while buf:
            data = buf.split(换行符,1)
            新路径 = f"{临时目录}/无序_{序号(1)}_{uuid1()}.txt"
            with open(新路径, 'ab') as ff:
                ff.write(待补全数据 + data[0])
            待排序文件列表.append(新路径)
            try:
                待补全数据 = data[1]
            except:
                待补全数据 = b""
            buf = f.read(小文件大小)
    新路径 = f"{临时目录}/无序_{序号(1)}_{uuid1()}.txt"
    with open(新路径, 'ab') as ff:
            ff.write(待补全数据 + 换行符)
            待排序文件列表.append(新路径)
    待补全数据 = b""
del buf,data,待补全数据
打点()
print(f"\n分割大文件完成,共耗时:{计时()}")

# (2)排序小文件
打点()
序号_重置(1)
待归并文件队列 = queue.Queue()
for 文件 in 待排序文件列表:
    with open(文件, "rb") as f:
        data = f.read()
    data = set(data.split(换行符))
    if b"" in data:
        data.remove(b"")
    if 换行符 in data:
        data.remove(换行符)
    data = sorted(data)

    新路径 = f"{临时目录}/有序_{序号(1)}_{uuid1()}.txt"
    with open(新路径, 'ab') as ff:
        for line in data:
            ff.write(line + 换行符)
    待归并文件队列.put(新路径)
    os.remove(文件)
del data
打点()
print(f"\n排序小文件完成,共耗时:{计时()}")

# (3)归并小文件
打点("归并前")
序号_重置(1)
个数 = 待归并文件队列.qsize()
归并次数 = 个数 - 1
print(f"\n\n归并共{归并次数}次")
当前次数 = 0
while 个数 > 1:
    当前次数 += 1
    print(f"\n执行第{当前次数}次归并")
    文件路径a = 待归并文件队列.get()
    文件路径b = 待归并文件队列.get()
    新文件路径 = f"{临时目录}/{序号(1)}_{uuid1()}.txt"
    if 当前次数 == 归并次数:
        新文件路径 = 输出文件
    with open(文件路径a,"rb") as 文件a, open(文件路径b,"rb") as 文件b, open(新文件路径,"wb") as ff:
        # region 归并操作
        is_a_over = False
        is_b_over = False

        a = 文件a.readline().strip()
        b = 文件b.readline().strip()
        last = None

        while not (is_a_over and is_b_over):

            if is_a_over:
                b = 文件b.readline()
                if not b:
                    is_b_over = True
                else:
                    ff.write(b)

            elif is_b_over:
                a = 文件a.readline()
                if not a:
                    is_a_over = True
                else:
                    ff.write(a)

            else:
                # region 处理初始赋值
                if not a:
                    is_a_over = True
                    if not b:
                        is_b_over = True
                        continue
                    else:
                        ff.write(b + 换行符)
                        continue

                if not b:
                    is_b_over = True
                    ff.write(a + 换行符)
                    continue
                # endregion

                if a <= b:
                    if a == b or a == last:
                        a = 文件a.readline().strip()
                        if not a:
                            is_a_over = True
                            ff.write(b + 换行符)
                        continue
                    else:
                        last = a
                        ff.write(last + 换行符)
                        a = 文件a.readline().strip()
                        if not a:
                            is_a_over = True
                            ff.write(b + 换行符)
                        continue
                else:
                    if b == last:
                        b = 文件b.readline().strip()
                        if not b:
                            is_b_over = True
                            ff.write(a + 换行符)
                        continue
                    else:
                        last = b
                        ff.write(last + 换行符)
                        b = 文件b.readline().strip()
                        if not b:
                            is_b_over = True
                            ff.write(a + 换行符)
                        continue
        # endregion

    待归并文件队列.put(新文件路径)
    os.remove(文件路径a)
    os.remove(文件路径b)
    个数 = 待归并文件队列.qsize()
    打点()
    print(f"耗时:{计时()}")

打点("归并后")
print(f"\n\n归并小文件完成,共耗时:{计时('归并前','归并后')}")
print(f"\n输出文件:{输出文件}")


打点()
print(f"\n\n总耗时:{计时(0)}")
print("———— 结束 ————")

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值