(图: 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
)
错误处理, 针对某些系统生成的不规范文件.
二. 参数详解
默认为只读模式, 避免文件损坏, +号表示读写皆可, 默认为文本模式, 二进制需要加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, 需进一步推断编码")
对于文本的编码检测一般常用的库:
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 withmode='r+') would have another buffering. To disable buffering inTextIOWrapper, consider using thewrite_throughflag forio.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()returnsTrue) 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原样写入;
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.
找个文本文件测试一下, 号称网络最长文: 宇宙巨校闪级生_百度百科
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) : 适合「写多读少, 整行操作」( 如流式数据写入, 单条数据更新) , 灵活性高, 实时场景首选.