python学习—合并TXT文本文件

系列文章目录



功能说明

遍历文件夹,寻找特定格式文件,读写TXT文件,支持文件夹嵌套。
本代码目标为:

  • 1 指定文件夹,遍历并寻找TXT文本文件;
  • 2 新建TXT文件,将遍历找到的TXT文件内容,合并写入新建的TXT文件;
  • 3 支持嵌套文件夹内的txt文档。

1 准备工作

首先在D盘根目录下建立文件夹“mulu”,在该文件夹内存放多个txt格式文本文件,内容随意。
代码支持中文,所以文件夹和文件名可以是中文,这里使用我测试代码里的目录故不再修改。
我准备的文档包括:windows xp系统下新建的txt文档、windows 7系统建立的txt文档、windows 10系统txt文档、银河麒麟系统txt文档、其他txt文档。

(知识点) 关于txt文件的编码格式(ANSI、Unicode、UTF-8)

——ANSI: Windows 里说的「ANSI」其实是 Windows code pages,这个模式根据当前 locale 本地化默认语言选定具体的编码,比如这个默认语言,在简体中文版里,是GBK(所谓 GBK 编码,实际上更多地被叫做 CP936);在繁体中文版里,是BIG5;在日文版里是JIS。
也就是说一个中文文本,在Windows简体中文版系统内显示的是中文,到Windows日文版系统内显示的就不知道是什么东西了。
ANSI 是早期操作系统的遗留编码,在不同语言的系统中编码不同,Windows系统 把自己这些 code page 称作「ANSI」。
后期,编码系统发展出更大的字符集,改用其他的更优的编码格式了。

——Unicode: 一套抽象的兼容所有常用语言的编码方式,但是不适合计算机系统直接存储,Unicode是字符集和编码是明确区分的。主要包括 UTF-8、UTF-16 和 UTF-32。
UTF-16就是Windows模式的编码模式(Windows里说的Unicode一般都是指这种编码),用2个字节表示任意字符,注意:英文字符也占2个字节,这种编码可以表示65536个字符;
UTF-16,UTF-32:另外的unicode存储编码,结构简单,不能和ASCII混用。

——UTF-8: 一种将unicode转换成适合计算机存储的方式,相对其他的UTF-xx省空间,同时又可以和ASCII混用,结构相对复杂,底层处理相对慢。UTF-8不带BOM头。
此外,还有带BOM头的UTF-8:一种为了跨平台设计的文件起始标记,但很多程序没去处理这个,用了BOM反而常造成问题。

关于编码的详细介绍,可以查看以下文章 《Windows 记事本的 ANSI、Unicode、UTF-8 这三种编码模式有什么区别?》

转换关系

Windows早期版本(经测试,windows xp系统和windows7系统)新建的txt文档默认是ANSI字符集的,即 简中系统下,编码是GBK。
后来发展,Windows支持了Unicode,但当时大部分软件都是用ANSI编码的,unicode还不流行,Windows想了个办法,就是允许一个默认语言编码ANSI,当遇到一个字符串,不是unicode的时候,就用默认语言编码解释。在系统设置-区域和语言选项里可以改默认语言。

windows 10 开始,新建的txt文档默认为UTF-8格式编码。
目前国产系统,银河麒麟系统,默认使用的也是 UTF-8格式编码。

当打开txt文档时,使用错误的编码系统,有可能会发生读错误,即使能够读写,也是乱码。

所以,需要 判定 txt文本文档的编码格式,统一转换成一种格式 UTF-8编码,再进行读写合并操作。
打开txt文档 > 另存为 > 编码,可以选择编码格式,转换编码格式。
1

2 第一版代码

第一版本的代码,主要实现一个合并的操作。

(1) 确定目录、新建最终的结果txt文档

filedir = r"d:\mulu"  # 第一版只考虑一层目录,里面不存在文件夹
result_file = os.path.join(filedir, 'result.txt') # 合并的目标txt文档

(2) 文件排序

txt文档的名称可能有顺序,最好按照名称顺序合并,故给文件列表排序。

filenames = os.listdir(filedir)
filenames.sort()

(3) 遍历文件夹

思路是:先打开 result_file最终txt文档,然后遍历 文件夹内的txt文档并逐行读写入 最终txt文档。

with open(result_file, 'w', encoding='utf-8') as f:
    # 遍历文件名
    for i, filename in enumerate(filenames, start=1):
        # print(i)  # 文件计数
        filepath = os.path.join(filedir, filename)

知识点: 关于utf-8 与 utf-8-sig 有什么区别

utf-8 以字节为编码单元,它的字节顺序在所有系统中都是一样的,没有字节序问题,也因此它实际上并不需要 BOM;
uft-8-sig 中 sig 全拼为 signature,即带有签名的 utf-8(UTF-8 with BOM);
BOM 全称 ByteOrder Mark,字节顺序标记,出现在文本文件头部,Unicode编码标准中用于标识文件是采用哪种格式的编码。

为什么写入 csv 文件要用 utf-8-sig 编码?
Excel 在读取 csv 文件的时候是通过读取文件头上的 BOM 来识别编码的,如果文件头无 BOM 信息,则默认按照 Unicode 编码读取。
当我们使用 utf-8 编码来生成 csv 文件的时候,并没有生成 BOM 信息,Excel 就会自动按照 Unicode 编码读取,就会出现乱码问题了。

为什么写入 txt 文件要用 utf-8 编码?
在写入 txt 文件时,Windows 会默认转码成 gbk,遇到某些 gbk 不支持的字符就会报错,在打开文件时就声明编码方式为 utf-8 就能避免这个错误。
此处转载自《【Python 必会技巧】利用 utf-8-sig 编码格式解决写入 csv 文件乱码问题》,感谢大佬分享。

(4) 逐行读写,合并txt文档

判定文件后缀为“.txt”格式,逐行读取,写入最终的txt文档。每个 文档内容之间,用空行间隔区分。
(此处可以视情况修改间隔符号,或者删除该行代码,使上下文不间隔)。

//遍历单个文件,读取行数
        if filename.lower().endswith(".txt"):  # 强制将文件名 转为 小写字母,防止漏掉  大写 TXT
            print(filepath)
            with open(filepath, encoding='utf-8', errors='ignore') as file:
                for line in file:
                    f.write(line)
            f.write('\n')  # 添加回车键,在内容结尾处断行用
        f.write('\n')  # 添加间隔空行,强行断开两个文件

(5) 第一版完整代码

# coding=utf-8
import os


filedir = r"d:\mulu"  
result_file = os.path.join(filedir, 'result.txt')
filenames = os.listdir(filedir)
filenames.sort()

with open(result_file, 'w', encoding='utf-8') as f:
    # 遍历文件名
    for i, filename in enumerate(filenames, start=1):
        # print(i)
        filepath = os.path.join(filedir, filename)

        if filename.lower().endswith(".txt"):
            print(filepath)
            with open(filepath, encoding='utf-8', errors='ignore') as file:
                for line in file:
                    f.write(line)
            f.write('\n')  # 添加回车键,在内容结尾处断行用
        f.write('\n')  # 添加间隔空行,强行断开两个文件

合并后的txt文档 ,结果如下图:
2
可以看到,有些txt文档的内容出现了乱码,说明这些文档的编码格式不是 UTF-8,我的代码强制使用 UTF-8 进行读写,导致了乱码。

3 第二版代码

首先需要解决的就是,统一编码问题,总体思路是:先判定格式,再转换格式另存为新文档,再进行合并操作。

(1) 判定txt编码格式

需要导入 chardet 库,检测txt文档的编码格式,如果没有需要先行安装。这里定义检测函数 detect_encoding(),参数为遍历的txt文档。

import chardet

def detect_encoding(file_path):
    with open(file_path, 'rb') as oldfile:
        raw_data = oldfile.read()
        result = chardet.detect(raw_data)
        encoding = result['encoding']
        return encoding

(2) 转换编码,另存为UTF-8格式

判定检测txt文档的编码格式,如果不是 UTF-8 格式,用检测到的编码格式读取文件,写入 名称为 new_name 的新文件内,从而实现 转换编码格式另存为操作。

 encoding_format = detect_encoding(filepath)
 if encoding_format == 'utf-8':
 	continue
 else:
 	with open(filepath, 'r', encoding=encoding_format) as file:
		content = file.read()
        new_file_path = os.path.join(filedir, f'new_{filename}')
    with open(new_file_path, 'w', encoding='utf-8') as new_file:
        new_file.write(content)

(3) 读写合并函数

创建合并函数 merge_txt(txt, result_file), 逐行读取txt文件,写入最终txt文档。参数为 统一格式的txt文档 和 最终文档。

def merge_txt(txt, result_file):
    with open(txt, encoding='utf-8', errors='ignore') as file:
        for line in file:
            result_file.write(line)
        result_file.write('\n')  # 添加间隔空行
    result_file.write('\n')  # 添加间隔空行,强行断开两个文件的内容

(4) 调用函数

调用 main()函数,给定参数,调用函数完成 合并。

def main():
    filedir = r"d:\mulu"  # 根文件夹目录
    result_file_path = os.path.join(filedir, 'result.txt')

    with open(result_file_path, 'w', encoding='utf-8') as result_file:
        merge_txt_files(filedir, result_file)


if __name__ == "__main__":
    main()

(5) 完整代码

查看上一步(4)可知,在给定参数时,在目录下面,先行新建了 result_file 最终文档,即该空白文档也参与了遍历过程,需要将其跳过。
同时,第二版代码也考虑了 嵌套 文件夹的情况。完整代码如下:

import os
import chardet


def detect_encoding(file_path):
    with open(file_path, 'rb') as oldfile:
        raw_data = oldfile.read()
        result = chardet.detect(raw_data)
        encoding = result['encoding']
        return encoding


def merge_txt(txt, result_file):
    with open(txt, encoding='utf-8', errors='ignore') as file:
        for line in file:
            result_file.write(line)
        result_file.write('\n')  # 添加间隔空行
    result_file.write('\n')  # 添加间隔空行,强行断开两个文件的内容


def merge_txt_files(filedir, result_file):
    # 遍历当前文件夹中的文件和文件夹
    filenames = os.listdir(filedir)
    filenames.sort()
    for filename in filenames:
        filepath = os.path.join(filedir, filename)

        # 跳过 新建的  空 result.txt文档
        if filename == 'result.txt':
            continue
        # 如果是文件夹,则递归调用函数遍历子文件夹
        elif os.path.isdir(filepath):
            merge_txt_files(filepath, result_file)
        # 如果是txt文件,则将内容写入结果文件
        elif filename.lower().endswith(".txt"):
            encoding_format = detect_encoding(filepath)
            if encoding_format == 'utf-8':
                merge_txt(filepath, result_file)
            else:
                with open(filepath, 'r', encoding=encoding_format) as file:
                    content = file.read()
                new_file_path = os.path.join(filedir, f'new_{filename}')
                with open(new_file_path, 'w', encoding='utf-8') as new_file:
                    new_file.write(content)
                merge_txt(new_file_path, result_file)


def main():
    filedir = r"d:\mulu"  # 根文件夹目录
    result_file_path = os.path.join(filedir, 'result.txt')

    with open(result_file_path, 'w', encoding='utf-8') as result_file:
        merge_txt_files(filedir, result_file)


if __name__ == "__main__":
    main()

再次看一下合并后的txt文档,如下图:
在这里插入图片描述

可以看到,合并的txt文档内容没有乱码了,而且在目录中有转换完格式另存的txt文档,可以直观的看到哪些文档的编码格式不一致。

4 后记

通过以上代码,可以实现 嵌套文件夹内的txt文档的合并操作,同时 也学习了 不同编码格式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da-peng-song

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值