影刀RPA 文件编码问题:UTF-8、GBK、BOM

0 阅读6分钟

影刀RPA 文件编码问题:UTF-8、GBK、BOM

署名:林焱

什么情况用什么

在这里插入图片描述

RPA流程中读写文件时最头疼的就是编码问题——采集的网页是UTF-8,保存到CSV后Excel打开乱码;读取同事用Windows记事本保存的文件报UnicodeDecodeError;处理旧系统导出的GBK编码数据。

编码使用场景特点
UTF-8通用、网页、API国际标准,变长编码
UTF-8 with BOMWindows兼容带BOM头,Excel可正确识别
GBK/GB2312Windows中文系统中文专用,不兼容国际
latin-1兜底不会报错但可能乱码

怎么做

一:读写文件指定编码

在这里插入图片描述

# 写入文件
with open(r"D:\data\output.txt", "w", encoding="utf-8") as f:
    f.write("中文内容")

# 读取文件
with open(r"D:\data\input.txt", "r", encoding="utf-8") as f:
    content = f.read()

# CSV文件用utf-8-sig(带BOM,Excel兼容)
import csv
with open(r"D:\data\data.csv", "w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["姓名", "年龄"])

二:自动检测文件编码

[video(video-Tv68IEDv-1784312237807)(type-csdn)(url-live.csdn.net/v/embed/526…)]

def detect_encoding(file_path, sample_size=1024):
    """尝试检测文件编码"""
    encodings = ["utf-8-sig", "utf-8", "gbk", "gb2312", "gb18030", "big5", "latin-1"]
    
    with open(file_path, "rb") as f:
        raw = f.read(sample_size)
    
    for enc in encodings:
        try:
            raw.decode(enc)
            return enc
        except (UnicodeDecodeError, LookupError):
        ![在这里插入图片描述](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/45e4654c9e76498cae08663599480f08~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxX1JQQUFJ:q75.awebp?rk3s=f64ab15b&x-expires=1786209365&x-signature=VPveNZAdGqYaHwYaEaz4Psag%2BuA%3D)

            continue
    
    return "utf-8"  # 默认回退

# 使用
encoding = detect_encoding(r"D:\data\unknown.txt")
print(f"检测到编码: {encoding}")

with open(r"D:\data\unknown.txt", "r", encoding=encoding) as f:
    content = f.read()

三:编码转换

def convert_encoding(input_path, output_path, from_enc=None, to_enc="utf-8"):
    """转换文件编码"""
    if from_enc is None:
        from_enc = detect_encoding(input_path)
    
    with open(input_path, "r", encoding=from_enc) as f:
        content = f.read()
    
    with open(output_path, "w", encoding=to_enc) as f:
        f.write(content)
    
    print(f"转换完成: {from_enc}{to_enc}")

# GBK转UTF-8
convert_encoding(
    r"D:\data\gbk文件.txt",
    r"D:\data\utf8文件.txt",
    from_enc="gbk",
    to_enc="utf-8"
)

四:处理BOM头

# 检查文件是否有BOM
def has_bom(file_path):
    with open(file_path, "rb") as f:
        return f.read(3) == b'\xef\xbb\xbf'

# 去掉BOM头
def remove_bom(file_path):
    with open(file_path, "rb") as f:
        content = f.read()
    
    if content[:3] == b'\xef\xbb\xbf':
        content = content[3:]
    
    with open(file_path, "wb") as f:
        f.write(content)

# 读取时自动处理BOM
# 用utf-8-sig读取会自动去掉BOM
with open("file.txt", "r", encoding="utf-8-sig") as f:
    content = f.read()
    # content不会有BOM字符

完整流程:编码兼容读写工具

在这里插入图片描述

import os

class EncodingSafeFile:
    """编码安全的文件读写工具"""
    
    # 常见编码优先级
    ENCODINGS = ["utf-8-sig", "utf-8", "gbk", "gb18030", "latin-1"]
    
    @classmethod
    def read(cls, file_path, encoding=None):
        """安全读取文件,自动检测编码"""
        if encoding:
            with open(file_path, "r", encoding=encoding) as f:
                return f.read()
        
        # 自动检测
        for enc in cls.ENCODINGS:
            try:
                with open(file_path, "r", encoding=enc) as f:
                    return f.read()
            except UnicodeDecodeError:
                continue
        
        # 兜底:用latin-1(不会报错,但中文会乱码)
        with open(file_path, "r", encoding="latin-1") as f:
            return f.read()
    
    @classmethod
    def write(cls, file_path, content, encoding="utf-8-sig"):
        """安全写入文件"""
        os.makedirs(os.path.dirname(file_path) if os.path.dirname(file_path) else ".", exist_ok=True)
        with open(file_path, "w", encoding=encoding) as f:
            f.write(content)
    
    @classmethod
    def read_lines(cls, file_path, encoding=None):
        """逐行读取"""
        if encoding:
            enc = encoding
        else:
            # 先检测编码
            for e in cls.ENCODINGS:
                try:
                    with open(file_path, "r", encoding=e) as f:
                        f.read(100)
                    enc = e
                    break
                except UnicodeDecodeError:
                    continue
            else:
                enc = "latin-1"
        
        with open(file_path, "r", encoding=enc) as f:
            for line in f:
                yield line.rstrip("\n\r")

# 使用
content = EncodingSafeFile.read(r"D:\data\未知编码.txt")
print(f"读取成功: {len(content)}字符")

EncodingSafeFile.write(r"D:\data\输出.txt", "测试中文内容")

有什么坑

坑一:UTF-8文件在Excel中乱码

现象:Python用UTF-8编码写的CSV/TXT文件,用Excel打开后中文全是乱码。

在这里插入图片描述

原因:Excel在Windows中文系统下默认用GBK编码打开文本文件,UTF-8文件没有BOM头时Excel无法识别。

解决:写入时用utf-8-sig(带BOM的UTF-8):

# CSV文件
import csv
with open("data.csv", "w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["姓名", "年龄", "部门"])

# 普通文本
with open("data.txt", "w", encoding="utf-8-sig") as f:
    f.write("中文内容")

# UTF-8-sig和UTF-8的区别:
# UTF-8: 不带BOM头,文件开头直接是内容
# UTF-8-sig: 带BOM头(3字节:EF BB BF),Excel能正确识别编码

坑二:读取GBK文件报UnicodeDecodeError

在这里插入图片描述

现象:读取Windows记事本保存的文件,报UnicodeDecodeError: 'utf-8' codec can't decode byte...

原因:Windows记事本默认用ANSI编码(中文系统下是GBK),用UTF-8读取会失败。

解决:指定GBK编码,或自动检测:

[video(video-tywgmDBh-1784312244497)(type-csdn)(url-live.csdn.net/v/embed/526…)] 在这里插入图片描述

# 方案1:直接用GBK
with open("file.txt", "r", encoding="gbk") as f:
    content = f.read()

# 方案2:用errors参数忽略错误(可能丢失字符)
with open("file.txt", "r", encoding="utf-8", errors="ignore") as f:
    content = f.read()

# 方案3:用errors="replace"替换无法解码的字符
with open("file.txt", "r", encoding="utf-8", errors="replace") as f:
    content = f.read()

# 方案4:自动检测(推荐)
def safe_read(file_path):
    for enc in ["utf-8-sig", "utf-8", "gbk", "gb18030", "latin-1"]:
        try:
            with open(file_path, "r", encoding=enc) as f:
                return f.read()
        except UnicodeDecodeError:
            continue
    return ""

content = safe_read("file.txt")

坑三:BOM头导致字符串开头多出不可见字符

现象:读取UTF-8 with BOM文件后,字符串开头多了\ufeff字符,导致字符串比较失败。

原因utf-8编码读取带BOM的文件时,BOM不会被自动去掉,变成\ufeff字符。

解决:用utf-8-sig编码读取,或手动去掉:

在这里插入图片描述

# 正确:用utf-8-sig读取(自动去BOM)
with open("file.txt", "r", encoding="utf-8-sig") as f:
    content = f.read()
# content开头没有\ufeff

# 手动去掉
content = content.lstrip("\ufeff")

# 检查是否有BOM字符
if content.startswith("\ufeff"):
    content = content[1:]

# 常见错误:用utf-8读取BOM文件
# with open("file.txt", "r", encoding="utf-8") as f:  ❌ content开头有\ufeff
#     content = f.read()
# if content == "hello":  ❌ 永远不等,因为实际是"\ufeffhello"

坑四:混合编码文件读取失败

现象:一个文件大部分是UTF-8编码,但中间有少部分GBK字符,导致读取中途报错。

原因:文件编码不统一,可能是从不同来源拼接的。

在这里插入图片描述

解决:用errors参数或逐段处理:

# 方案1:忽略错误字符
with open("file.txt", "r", encoding="utf-8", errors="ignore") as f:
    content = f.read()
# 丢弃无法解码的字节

# 方案2:替换错误字符
with open("file.txt", "r", encoding="utf-8", errors="replace") as f:
    content = f.read()
# 无法解码的字节替换为�

# 方案3:逐字节处理,混合编码解码
def read_mixed_encoding(file_path):
    result = []
    with open(file_path, "rb") as f:
        data = f.read()
    
    i = 0
    while i < len(data):
        # 尝试UTF-8解码
        try:
            char = data[i:i+4].decode("utf-8")
            ![在这里插入图片描述](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/393fa18ed4574358ac0d71adea9c7d73~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxX1JQQUFJ:q75.awebp?rk3s=f64ab15b&x-expires=1786209365&x-signature=RJRjalHPiUnCZow2wIeX1eqsCUE%3D)

            result.append(char)
            i += len(char.encode("utf-8"))
            continue
        except UnicodeDecodeError:
            pass
        
        # 回退到GBK
        try:
            char = data[i:i+2].decode("gbk")
            result.append(char)
            i += 2
            continue
        except UnicodeDecodeError:
            pass
        
        # 跳过无法解码的字节
        i += 1
    
    return "".join(result)