Python - 文本处理

img

(图: Python 文件I/O | 菜鸟教程)

一. 前言

文本处理, 简单而不简单.

open(
    file,
    mode='r',
    buffering=-1,
    encoding=None,
    errors=None,
    newline=None,
    closefd=True,
    opener=None
)
  • 文件路径, 必须参数
  • 打开文件方式(默认以只读的方式, 避免意外写入损毁文件), 核心参数
  • 缓冲设置, 重要参数
  • 文件编码格式, 一般使用utf-8, Windows上默认使用使用ansi(即系统页编码, 如时区格式为中国, 对应的则是gbk/gb2312), 核心参数
  • 错误处理, 较少使用
  • 换行符, 重要参数
  • 控制关闭文件时是否关闭底层的「文件描述符」( 系统级的文件标识), 基本不用参数
  • 自定义文件打开的底层函数, 替代系统默认的打开逻辑, 基本不用参数

一般常见的参数使用组合:

open(
    file,
    mode='r',
    encoding=None,
)

高频操作方式

open(
    file,
    mode='rb',
    encoding=None,
    buffering=-1
)

指定缓冲参数, 需要考虑性能, 一般针对大型文件的处理

open(
    file,
    mode='r',
    encoding=None,
    newline=None
)

需要考虑换行符号, 如\n, \r\n, 系统差异

open(
    file,
    mode='r',
    encoding=None,
    errors=None
)

错误处理, 针对某些系统生成的不规范文件.

二. 参数详解

img

默认为只读模式, 避免文件损坏, +号表示读写皆可, 默认为文本模式, 二进制需要加b.

Character Meaning
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' open for exclusive creation, failing if the file already exists
'a' open for writing, appending to the end of file if it exists
'b' binary mode
't' text mode (default)
'+' open for updating (reading and writing)

注意, 增加了一个x模式, 排他模式, 不允许文件已经存在. (这个参数在很多非官版的文档都未提及)

维度 取值 / 说明
读写基础 r: 只读( 默认) , 文件指针在开头; w: 只写, 覆盖原有内容( 不存在则创建, 存在则清空) ; a: 追加, 文件指针在末尾( 不存在则创建, 写入内容追加到最后) ;
读写混合 r+: 读写, 文件指针在开头( 文件不存在抛错) ; w+: 读写, 先清空文件( 不存在则创建) ; a+: 读写, 指针在末尾( 写入追加, 读取需先 seek(0) 移动指针) ;
文本 / 二进制 b( 如 rb/wb/ab/r+b) : 二进制模式( 处理图片 / 视频 / 非文本文件) , 读取返回 bytes, 写入需传 bytes; 不加 b: 文本模式( 默认) , 读取返回 str, 写入传 str;
  • 文本模式: 会处理字符编码( 依赖 encoding 参数, 即, 这个参数只针对文本有意义) , 换行符( 依赖 newline 参数) ;
  • 二进制模式: 直接操作字节流, encoding 参数无效, 无换行符转换, 适合非文本文件.

2.1 进阶参数

2.1.1 encoding

需要稍微注意的是Windows上的历史遗留问题: 「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别? 网页代码一般使用哪个? - 知乎

(win10之后, 内置的文本编辑器默认保存文件不再带有bom, 同时也默认保存格式为utf-8)

注意这是个坑, 假如没有指定编码格式, 默认使用Windows上的code page(和语言时区设置相关)

with open('check.txt', 'r') as f:
    print(f.encoding)
# cp936, 即gbk

为什么Windows默认使用GBK(CP936)而不是GB18030(CP54936)? - 知乎

PS D:\Code\python_workspace> chcp
Active code page: 936

powershell的默认代码页, 这个特性会导致vscode/pycharm等打印中文字符时出现乱码.

def detect_bom_encoding(file_path):
    """检测文件BOM, 返回编码( 无则返回None) """
    bom_map = {
        b'\xef\xbb\xbf': 'utf-8-sig',
        b'\xfe\xff': 'utf-16-be',
        b'\xff\xfe': 'utf-16-le',
        b'\x00\x00\xfe\xff': 'utf-32-be',
        b'\xff\xfe\x00\x00': 'utf-32-le'
    }

    with open(file_path, 'rb') as f:
        # 读取前4字节( 覆盖所有BOM长度) 
        header = f.read(4)
        for bom, encoding in bom_map.items():
            if header.startswith(bom):
                return encoding
    return None

# 测试
file_path = "test.txt"
bom_encoding = detect_bom_encoding(file_path)
if bom_encoding:
    print(f"检测到BOM, 编码为: {bom_encoding}")
else:
    print("无BOM, 需进一步推断编码")

对于文本的编码检测一般常用的库:

chardet - PyPI

charset-normalizer - PyPI

cchardet - PyPI

import cchardet

def detect_encoding_cchardet(file_path):
    """用cchardet推断编码( 高性能) """
    with open(file_path, 'rb') as f:
        raw_data = f.read(10240)
        result = cchardet.detect(raw_data)
    encoding = result['encoding']
    confidence = result['confidence']
    return encoding, confidence

# 测试
encoding, confidence = detect_encoding_cchardet(file_path)
print(f"推断编码: {encoding}, 置信度: {confidence:.2f}")

假如检测出是gbk/gb2312类似编码, 应该使用现行覆盖面更广的gb18030, 否则很大概率会出现错误.

2.1.2 buffering

buffering( 可选, 默认 -1)

为什么需要缓冲区呢?

答案很简单, 相对于代码运行的CPU/内存, 磁盘IO的速度可以说是非常非常慢的, 假如写入数据时每次更新一部分数据(如, 逐行写入)都要等待写入磁盘那么程序的速度将变得无比的慢, 所以需要一个缓冲区, 等待数据足够多了, 一次性写入磁盘, 避免反复和磁盘IO交互.

从硬件设备上也能窥其中一二, 例如sata协议(机械硬盘)一般走南桥, 而不是通过pcie直连cpu(现在高端主板大多为ssd(nvme)提供足够的pcie直连, 而不再经过南桥). 同时存储/内存和cpu整合设计也正在成为一种趋势.

取值 说明
-1 系统默认缓冲区( 文本文件: 行缓冲; 二进制文件: 固定大小缓冲, 通常 4KB/8KB) ;
0 仅二进制模式有效, 无缓冲区( 直接读写磁盘, 性能极差, 慎用) ;
正数 自定义缓冲区大小( 单位: 字节) , 如 8192( 8KB) , 65536( 64KB) , 越大 I/O 越少;
import time

with open('test.txt', mode='wb', buffering=0) as f:
    f.write(b'test\n')
    time.sleep(10)
    f.write(b'end')
with open('test.txt', encoding='utf-8'm mode='w', buffering=0) as f:
    f.write('test\n')
    time.sleep(10)
    f.write('end')

简单测试, 等待写入和直接写入.

假如需要马上写盘, 可以调用flush().

buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable when writing in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer. Note that specifying a buffer size this way applies for binary buffered I/O, but TextIOWrapper (i.e., files opened with mode='r+') would have another buffering. To disable buffering in TextIOWrapper, consider using the write_through flag for io.TextIOWrapper.reconfigure(). When no buffering argument is given, the default buffering policy works as follows:

  • Binary files are buffered in fixed-size chunks; the size of the buffer is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) when the device block size is available. On most systems, the buffer will typically be 128 kilobytes long.
  • " Interactive" text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above for binary files.
(base) lian@jarvis:~$ python
Python 3.12.10 | packaged by conda-forge | (main, Apr 10 2025, 22:21:13) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import io
>>> io.DEFAULT_BUFFER_SIZE
8192(8K)
维度 对读取的影响 对写入的影响
核心逻辑 控制「磁盘→内存」的读取批次 控制「内存→磁盘」的写入批次
性能趋势 缓冲区越大, 读取越快( 上限是磁盘物理速度) 缓冲区越大, 写入越快( 上限是磁盘物理速度)
特殊取值 buffering=0 仅二进制有效, 读取极慢 buffering=0 仅二进制有效, 写入极慢但实时

2.1.3 errors

errors( 可选, 默认 None)

  • 作用: 指定文本模式下编码 / 解码错误的处理方式( 二进制模式无效) .

  • 常用取值:

    取值 说明
    'strict' 默认, 遇到编码错误抛 UnicodeError;
    'ignore' 忽略错误字符( 可能丢失数据, 适合非关键文本) ;
    'replace' 替换错误字符( 保留结构, 丢失具体字符) ;
    'backslashreplace' 用转义序列( 如 \x80) 替换错误字符;

2.1.4 newline

newline( 可选, 默认 None)

  • 作用: 文本模式下, 控制换行符的读取 / 写入转换规则( 二进制模式无效) .

  • 取值规则:

    取值 读取行为( 将文件中的换行符转成什么) 写入行为( 将 \n 转成什么)
    None 自动识别 \n/\r\n/\r, 统一转 \n 转成系统默认换行符( Windows: \r\n, Linux: \n) ;
    '' 不转换换行符( 保留文件原始换行符) 不转换( \n 原样写入) ;
    '\n' 仅识别 \n 为换行符 \n 原样写入;

img

import csv

fieldnames = ['Name', 'Age']
with open('output.csv', 'w', newline='') as file:
    dict_writer = csv.DictWriter(file, fieldnames=fieldnames)

    dict_writer.writeheader()  # 写入表头
    dict_writer.writerow({'Name': 'Alice', 'Age': 25})

import csv

fieldnames = ['Name', 'Age']
with open('output1.csv', 'w', newline='\r') as file:
    dict_writer = csv.DictWriter(file, fieldnames=fieldnames)

    dict_writer.writeheader()  # 写入表头
    dict_writer.writerow({'Name': 'Alice', 'Age': 25})

不同符号带来的差异.

2.1.5 closefd

closefd( 可选, 默认 True)

  • 作用: 控制关闭文件时是否关闭底层的「文件描述符」( 系统级的文件标识) .
  • 取值规则:
    • True( 默认) : 关闭文件对象时, 同时关闭底层文件描述符;
    • False: 仅关闭文件对象, 不关闭文件描述符( 仅当 file 参数是文件描述符( 整数) 时有效) ;
  • 注意: 若 file 是文件路径( 99% 的场景) , 设置 closefd=False 会直接报错; 仅底层文件操作( 如 os.open() 返回的文件描述符) 才会用到.

2.1.6 opener

opener( 可选, 默认 None)

  • 作用: 自定义文件打开的底层函数, 替代系统默认的打开逻辑.

  • 用法: opener 是一个函数, 接收两个参数( file 路径, flags 打开模式标志位) , 返回文件描述符( 整数) .

  • 使用场景: 自定义权限校验, 加密文件打开, 访问远程文件等小众场景.

  • 示例: 自定义文件打开权限( Linux/Mac 有效)

    import os
    # 自定义opener: 打开文件时设置权限为0o666( 所有用户可读可写) 
    def my_opener(path, flags):
        return os.open(path, flags, 0o666)
    
    with open('test.txt', 'w', opener=my_opener) as f:
        f.write('自定义权限打开')
    

三. 文件对象

一般而言, 对于操作文件, 建议在with语句之下使用, 避免文件打开后没有关闭的问题.

with open('test.txt', 'w') as f:
    f.write('test')
属性名 作用 应用场景
name 返回打开的文件路径 / 名称( 字符串) 批量处理文件时, 记录当前操作的文件( 如日志打印: print(f"处理文件: {f.name}"))
mode 返回文件的打开模式( 如 'r'/'w'/'a+'/'rb', 字符串) 判断文件是否为写入模式, 避免只读模式下调用 write() 报错: if 'w' in f.mode: ...
closed 返回文件是否已关闭( 布尔值) 调试时检查文件是否意外关闭; 自定义文件管理逻辑时判断状态
encoding 文本模式下返回编码( 如 'utf-8') , 二进制模式返回 None 文本处理时确认编码, 避免乱码( 如 if f.encoding != 'utf-8': 转换编码)
newlines 文本模式下返回读取到的换行符类型( \n/\r\n/ 元组, 混合换行时) 处理跨平台文件时, 识别换行符并统一转换( 如 Windows → Linux 换行符)
tell() 不是属性! 是方法, 返回指针位置( 字节数) 补充: 新手易混淆, tell() 是方法( 需调用) , 返回当前指针位置

3.1 读取

方法名 作用 & 参数 返回值 核心应用场景
read(size=-1) 从当前指针位置读取内容; size: 文本模式 = 字符数, 二进制模式 = 字节数; size=-1( 默认) 读取全部内容 文本→str, 二进制→bytes; 无内容返回空 1. 逐块读大文件( 你的代码场景) : read(8192)( 8KB) , 避免 GB 级文件一次性加载到内存( OOM) ; 2. 小文件读取: read() 一次性读全部, 简洁高效
readline(size=-1) 读取一行内容( 直到 \n) ; size 指定则最多读取 size 个字符 / 字节 文本→str( 含换行符) , 二进制→bytes; 行尾无内容返回空 按行读取文本文件( 日志 / CSV) , 逐行处理( 内存友好) : while line := f.readline(): 处理line
readlines(hint=-1) 读取所有行, 返回列表( 每行是字符串 /bytes, 含换行符) ; hint 指定则读取到总字符数≥hint 时停止 列表( 元素为每行内容) 小文件按行处理( 可遍历列表) ; hint 限制读取行数( 避免加载全部大文件)
迭代器协议( __next__()) 文件对象是迭代器, for line in f 底层调用该方法, 逐行返回内容 每行内容( str/bytes) 最简洁的按行读取方式( 推荐) : for line in f: print(line.strip())

3.2 写入

方法名 作用 & 参数 返回值 核心应用场景
write(s) 将内容写入当前指针位置; s: 文本→str, 二进制→bytes 写入的字符数 / 字节数 基础写入操作( 单行 / 小块内容) : f.write('hello\n')
writelines(lines) 写入多行内容( 可迭代对象, 如列表) ; 不会自动加换行符 批量写入多行( 如处理后的日志) : f.writelines(['a\n', 'b\n', 'c\n'])
flush() 强制将缓冲区内容刷入磁盘( 跳过系统缓冲)

writelines()需要稍微注意, 不要被单词带偏, 这个函数只是方便直接写入序列内容, 而不需要转为字符串, 并不是写入后自动换行.

    def writelines(self, lines):
        """
        Write lines to the file from an interable.

        NOTE: writelines() does NOT add line separators.
        """
        self._checkClosed()
        for line in lines:
            self.write(line)

(源码)

3.3 指针

方法名 作用 & 参数 返回值 核心应用场景
seek(offset, whence=0) 移动指针到指定位置; offset: 偏移量( 字节数) ; whence: 基准位置( 0 = 开头, 1 = 当前, 2 = 末尾) 新的指针位置( 字节数) 1. a+ 模式读取内容: f.seek(0) 移到开头, 否则 read() 从末尾开始读不到内容; 2. 随机读取文件中间内容: f.seek(1000) 跳到第 1000 字节位置
tell() 返回当前指针位置( 字节数, 从文件开头算起) 整数( 字节数)

需要注意的是a+模式打开的文件, 需要写入和读取内容.

with open('test.txt', mode='a+', encoding='utf-8') as f:
    # f.seek(0) # 将指针移动到文件头
    for l in f:
        print(l)

由于指针指向文件末尾, 所以打印不出任何内容, 需要手动将指针指向文件头.

四. 大文件处理

对于巨大的文件, 不可能都一次性装进内存.

with open('test.txt', mode='r', encoding='utf-8') as f:
    for l in f:
        yield l

with open(r"test.txt", mode='rb') as f:
    while True:
        chunk = f.read(8192)
        if not chunk:
            break

常见的逐行/逐块按需读取.

4.1 内存映射

Memory Mapping:

内存映射( Memory Mapping) 是操作系统提供的底层机制, 通过 mmap 系统调用将文件的部分 / 全部内容直接映射到进程的虚拟内存空间. 操作这块内存就等同于操作文件本身, 无需显式调用 read()/write(), 由操作系统负责内存与磁盘的同步( 按需加载, 脏页回写) .

对文本处理而言, mmap 最大的价值是突破传统文件读写的性能瓶颈, 尤其适合 GB 级超大文本文件的处理.

Memory mapping is a technique that maps a file or device into a process's virtual address space, allowing the file's content to be accessed as if it were part of the program's memory. This approach eliminates the need for traditional file I/O operations, significantly improving efficiency and simplifying data access.

一文搞懂内存映射(Memory Map)原理 - 知乎

深入理解内存映射: 加速文件访问的神奇原理 - 知乎

找个文本文件测试一下, 号称网络最长文: 宇宙巨校闪级生_百度百科

import time

t = time.time()
s = 'ERROR'
with open(r"D:\Downloads\1951\宇宙巨校闪级生.txt", mode='r', encoding='gb18030') as f:
    for line in f:
        if s in line:
            print(line)

print(time.time() - t)

# 0.6s左右
import mmap
import time

def optimized_mmap_search(file_path, keyword, encoding='gb18030'):
    keyword_bytes = keyword.encode(encoding)  # 这是二进制的匹配, 先将目标关键词编码处理
    keyword_len = len(keyword_bytes)
    line_c = b'\n'  # 换行符, 用于行数据的判断
    s = time.time()

    with open(file_path, 'rb') as f:
        # fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型), 可用于底层操作系统的 I/O 操作. 
        with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
            pos = 0
            # mm.size(), mm.size(), Return the length of the file, which can be larger than the size of the memory-mapped area.
            file_size = mm.size()
            while pos < file_size:
                # mmap.find做二进制子串匹配( C级实现, 比正则快) 
                # find(关键词, 开始位置, 结束位置)
                pos = mm.find(keyword_bytes, pos)
                # 假如没有匹配到内容,遍历整个文件
                if pos == -1:
                    break
                # 查找到关键词
                # 下一步判断出关键所在的一整行
                # 定位行首( 找前一个换行符) 
                line_start = mm.rfind(line_c, 0, pos) + 1  # 假如数据为第一行, 没有换行符, 所以是 -1 + 1 = 0
                # 定位行尾( 找后一个换行符) , pos + keyword_len, 防止关键词词出现在最末端
                line_end = mm.find(line_c, pos + keyword_len)
                # 假如查找到最后
                if line_end == -1:
                    line_end = file_size
                # 解码整行
                line_bytes = mm[line_start:line_end]
                line = line_bytes.decode(encoding, errors='ignore')
                print(line)
                # 跳过当前行, 避免重复匹配
                pos = line_end + 1

    print(f"优化后mmap耗时: {time.time() - s:.2f}秒")

optimized_mmap_search(r"D:\Downloads\1951\宇宙巨校闪级生.txt", 'ERROR', encoding='gb18030')
# 0.07s

在大小约100M, 采用gb18030编码的文本, 判断文本是否包含某个关键词, 内存映射的速度差不多是逐行读取速度的十倍, 理论上, 更大的文件, 例如GB或更大, 这种差异会更明显.

但需要注意, 这种方式的实现代码更为繁琐, 同时也需要考虑处理较小文件时, 冷启动所消耗的时间.

one more thing, 假如需要将匹配到的数据, 更改/删除, 然后保存.

五. 拓展

为了应对当下越来越巨型化的数据处理, 为更快, 更好的读取/存储, 各种格式文件应运而生: pandas 各种存储格式速度对比[CSV, hdf5,SQL, pickle, feather, parquet] - 知乎

格式名称 核心特性 核心优势 核心劣势 典型应用场景
Parquet 列存, 跨语言, 嵌套数据支持 1. 列存 I/O 效率极高( 仅读取需查询列) ; 2. 内置多压缩算法( Snappy/Gzip/LZ4) , 压缩率高; 3. 跨语言 / 跨平台( Spark/Pandas/Java/Go) ; 4. 云原生友好( 适配 S3/OSS) 1. 行写入 / 更新效率低; 2. 小文件读写开销大; 3. 单文件解析速度略低于 Feather 大数据分析( Spark/Flink) , 数据仓库( Hive/BigQuery) , 云原生数据湖, Pandas 批量数据存储
HDF5 分层结构, 异构数据, 多维支持 1. 支持海量异构数据( 数值 / 文本 / 图像 / 高维数组) ; 2. 类文件系统的分层目录结构, 元数据丰富; 3. 支持部分读取( 无需加载全文件) ; 4. 科学计算场景标准化 1. 列查询效率低于 Parquet/ORC; 2. 跨语言支持弱( 仅 Python/Java 友好) ; 3. 元数据开销大, 小文件不友好; 4. 不支持并发写入 科学计算( 气象 / 天文 / 生物信息学) , 数值模拟数据, 实验室传感器数据, 高维矩阵存储
ORC( Optimized Row Columnar) 列存, 轻量级索引, ACID 支持 1. 压缩率比 Parquet 更高( 内置 LZ4/Zlib) ; 2. 内置行索引 / 布隆过滤器, 查询速度更快; 3. 支持 ACID 事务( 适配 Hive) ; 4. 低内存占用 1. 生态比 Parquet 窄( 主要适配 Hadoop 生态) ; 2. 嵌套数据支持弱于 Parquet; 3. Python 生态适配稍差 Hadoop 生态数据仓库( Hive) , 大数据批处理( MapReduce/Spark) , 企业级数据湖, 高压缩率批处理场景
Feather( Apache Arrow) 列存, 内存映射, Arrow 兼容 1. 基于 Apache Arrow 内存格式, 读写速度极快( 接近内存级) ; 2. 支持内存映射( mmap) , 大文件读取无 OOM; 3. 跨语言( Python/R/Julia) ; 4. 小文件开销极低 1. 压缩率低于 Parquet/ORC; 2. 不支持分片存储; 3. 长期存储兼容性稍差( 版本迭代快) 交互式数据分析( Jupyter/Pandas) , Python/R 数据交换, 临时中间数据存储, 高频读写的场景
Avro 行存为主, Schema 化, 序列化 1. 行存设计, 写入 / 更新 / 流式处理效率高; 2. 内置 JSON 格式 Schema, 数据自描述; 3. 支持流式数据( Kafka) ; 4. 跨语言序列化能力强 1. 列查询效率远低于 Parquet/ORC; 2. 压缩率中等; 3. 大文件随机读取效率低 流式数据处理( Kafka/Flume) , 微服务数据序列化, Hadoop 生态行式存储, 实时数据传输
NetCDF( Network Common Data Form) 科学数据专用, 维度化存储 1. 专为多维科学数据设计( 时间 / 空间 / 高度) ; 2. 自描述元数据( 维度 / 单位 / 坐标系) ; 3. 领域标准化( 气象 / 海洋) ; 4. 支持部分读取 1. 通用数据场景适配差; 2. 压缩率低; 3. 跨语言支持弱( 仅 Python/Matlab) 气象预报, 海洋观测, 地理信息( GIS) , 大气科学, 水文监测数据
Apache Arrow IPC( Feather V2) 列存, 分片, 长期存储 1. Feather 升级版, 兼容 Arrow 内存格式; 2. 支持分片存储( 大文件拆分) ; 3. 压缩率提升; 4. 长期存储兼容性更好 1. 生态成熟度稍低于 Parquet; 2. 对旧版本工具兼容差 大规模 Arrow 生态数据分析, 跨语言大数据交换, 长期列存数据存储, 高性能中间数据
Pickle( Python 专属) 序列化, Python 对象原生支持 1. 支持任意 Python 对象( 类 / 函数 / NumPy / 模型) ; 2. Python 原生读写, 速度快; 3. 无需 Schema 定义 1. 非跨语言( 仅 Python 可用) ; 2. 安全性低( 易注入恶意代码) ; 3. 版本兼容性差( Python2/3 不互通) ; 4. 无默认压缩 Python 内部数据序列化, 机器学习模型中间结果, 临时对象存储, 纯 Python 生态场景
JSON Lines( JSONL) 行式 JSON, 文本存储 1. 纯文本格式, 易阅读 / 编辑; 2. 自描述 Schema; 3. 跨语言全兼容; 4. 逐行读写, 小文件友好 1. 读写速度极慢; 2. 压缩率极低; 3. 数值类型解析开销大; 4. 大文件查询效率差 日志存储, 轻量级结构化数据, 跨语言简单数据交换, 小规模 API 数据导出
CSV 纯文本, 极简通用 1. 无学习成本, 所有工具支持( Excel / 数据库) ; 2. 纯文本易编辑 / 查看; 3. 跨平台无兼容问题 1. 无类型约束( 全部字符串) ; 2. 压缩率最低; 3. 大文件读写极慢; 4. 不支持嵌套数据 小规模数据交换, Excel 适配, 非结构化文本数据导出, 入门级数据存储
场景类型 优先选择格式 核心原因
大数据列查询 / 分析 Parquet 跨生态, 高压缩, 列存高效
Hadoop 数据仓库 ORC 更高压缩率, ACID 事务, Hive 适配
科学计算 / 多维数据 HDF5/NetCDF 异构 / 维度化存储, 领域标准化
Python/R 交互式分析 Feather 极致读写速度, 内存映射, 跨语言交换
流式数据 / 序列化 Avro 行存高效, Schema 自描述, 流式适配
Python 内部数据存储 Pickle 原生支持 Python 对象, 无需适配
轻量级文本数据交换 JSON Lines/CSV 易阅读, 全工具兼容, 无需特殊解析库

列存 vs 行存核心差异

  • 列存格式( Parquet/ORC/Feather/HDF5) : 适合「读多写少, 列维度查询」( 如统计某几列的均值 / 求和) , 压缩率高, 大数据分析场景首选;
  • 行存格式( Avro/JSONL/CSV) : 适合「写多读少, 整行操作」( 如流式数据写入, 单条数据更新) , 灵活性高, 实时场景首选.