4. 写入CSV文件:使用csv.writer写入行、写入表头、处理特殊字符与换行
前面我们一直在讲怎么读CSV,但实际工作中,写CSV才是真正的家常便饭。你想想看,数据清洗完了要导出、处理结果要保存、给下游系统投喂数据……这些都得靠写CSV来完成。
我个人习惯把写CSV分成三个层次:先学会基本写入,再搞定表头,最后处理那些让人头疼的特殊字符。咱们一层层来。
4.1 基础写入:csv.writer 的两种打开方式
Python的csv.writer用起来很直接。但有个细节我刚开始也踩过坑——文件打开模式。
核心原则:写CSV必须用 'w' 模式,并且加上 newline='' 参数。
为什么非要加 newline=''?因为Windows系统下,如果不指定这个参数,写入的每一行后面都会多一个空行。我在项目中第一次导出数据时,客户反馈说“文件里全是空行”,排查了半天才发现是这个问题。
import csv
# 正确写法
with open('output.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['张三', '28', '北京'])
writer.writerow(['李四', '32', '上海'])
这段代码会生成两行数据。注意看,writerow()接收的是一个列表,每个元素对应一列。
4.2 写入多行:writerows 批量操作
如果你有几十上百行数据要写,一条条writerow()太慢了。这时候用writerows(),一次性搞定。
data = [
['产品A', '2024-01-15', '98.5'],
['产品B', '2024-01-16', '87.2'],
['产品C', '2024-01-17', '92.1'],
]
with open('products.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(data)
writerows()接收一个二维列表,内部自动遍历每一行并写入。效率比循环调用writerow()高不少,尤其是数据量大的时候。
4.3 写入表头:让数据有“身份证”
没有表头的CSV文件,说白了就是一堆裸数据。别人拿到文件根本不知道第一列是什么、第二列代表什么。我建议你养成习惯——任何CSV文件,第一行都写表头。
with open('employees.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# 先写表头
writer.writerow(['姓名', '年龄', '部门', '入职日期'])
# 再写数据行
writer.writerow(['王五', '30', '技术部', '2023-06-01'])
writer.writerow(['赵六', '27', '市场部', '2023-08-15'])
表头和数据行用同样的writerow()方法写入,只是位置不同——表头永远在第一行。
小技巧:如果你用pandas处理数据,导出CSV时默认会带表头。但用原生csv模块时,表头需要手动写。我一般把表头定义成一个单独的列表,放在代码开头,方便维护。
4.4 处理特殊字符:引号、逗号、换行符
这才是写CSV的真正难点。你想想看,如果数据里本身就包含逗号,或者包含换行符,直接写进去不就乱套了吗?
举个例子,某个字段值是 "北京,朝阳区",里面有个逗号。如果不做处理,解析方会把它当成两列。
csv.writer 默认会帮我们处理大部分情况——它会在必要时自动给字段加引号。但具体怎么加,可以通过 quoting 参数控制。
| quoting 参数 | 含义 | 适用场景 |
|---|---|---|
csv.QUOTE_ALL |
所有字段都加引号 | 数据格式不确定,求稳 |
csv.QUOTE_MINIMAL |
只在必要时加引号(默认) | 常规数据,追求可读性 |
csv.QUOTE_NONNUMERIC |
非数字字段加引号 | 数字和文本混合的数据 |
csv.QUOTE_NONE |
从不加引号 | 确定数据不含特殊字符 |
我曾经遇到过一个坑:配方数据里有个字段叫“备注”,里面写了多行文本,比如:
备注: 该批次需要
在恒温环境下保存
避免阳光直射
这个字段里包含了换行符。如果用默认的QUOTE_MINIMAL,写入时会把换行符原样写进去,导致CSV文件里出现真正的换行,破坏文件结构。
正确的做法是:
import csv
data = [
['配方001', '25°C', '该批次需要\n在恒温环境下保存'],
['配方002', '18°C', '避免阳光直射\n密封保存'],
]
with open('recipes.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
writer.writerow(['配方编号', '温度', '备注'])
writer.writerows(data)
用QUOTE_ALL后,所有字段都会被双引号包裹。包含换行符的字段会被正确转义,解析方读取时能还原出多行文本。
注意:如果数据里本身包含双引号,比如 他说"你好",csv.writer 会自动把它转义成 他说""你好""。这是CSV标准规定的转义方式,读取时也能正确还原。
4.5 自定义分隔符与换行符
虽然逗号是CSV的默认分隔符,但有些场景下需要换别的。比如欧洲一些地区用逗号做小数点,CSV文件就会改用分号做分隔符。
with open('europe_data.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, delimiter=';') # 用分号做分隔符
writer.writerow(['温度', '湿度', '压力'])
writer.writerow(['23,5', '65,2', '1013,2'])
这里温度 23,5 表示23.5摄氏度。如果用逗号做分隔符,解析方就会把 23 和 5 当成两列,数据全乱套。
换行符方面,默认是 \r\n(Windows风格)。如果你需要输出给Linux系统用,可以指定 lineterminator='\n'。
with open('linux_data.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerow(['A', 'B', 'C'])
经验之谈:除非有明确的跨平台需求,否则我建议保持默认的 \r\n。因为大多数CSV解析器都能兼容两种换行符,没必要自己折腾。
4.6 写入流程总览
下面这张图总结了写CSV文件的完整流程和关键决策点:
4.7 实战:配方数据写入完整示例
最后,咱们把前面学的串起来,写一个完整的配方数据导出函数:
import csv
def export_recipes(recipes, filename='配方数据.csv'):
"""
导出配方数据到CSV文件
recipes: 列表,每个元素是字典,包含配方信息
"""
fieldnames = ['配方编号', '配方名称', '温度(°C)', '湿度(%)', '备注']
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f, quoting=csv.QUOTE_ALL)
# 写入表头
writer.writerow(fieldnames)
# 写入数据
for recipe in recipes:
row = [
recipe.get('id', ''),
recipe.get('name', ''),
recipe.get('temp', ''),
recipe.get('humidity', ''),
recipe.get('note', '')
]
writer.writerow(row)
print(f'已导出 {len(recipes)} 条配方数据到 {filename}')
# 使用示例
data = [
{'id': 'R001', 'name': '标准发酵', 'temp': '28.5', 'humidity': '75', 'note': '恒温发酵'},
{'id': 'R002', 'name': '低温熟成', 'temp': '4.0', 'humidity': '85', 'note': '需要\n避光保存'},
]
export_recipes(data)
这里我用了 utf-8-sig 编码,而不是普通的 utf-8。为什么?因为Excel打开UTF-8编码的CSV文件时,经常会出现中文乱码。加上BOM(字节顺序标记)后,Excel就能正确识别编码了。这个坑我踩过不止一次,后来干脆所有导出都用 utf-8-sig。
总结一下写CSV的要点:
- 文件打开必须加
newline='',否则多空行 - 表头和数据用同样的
writerow()写入 - 数据含特殊字符时,用
quoting=csv.QUOTE_ALL保底 - 跨平台场景注意分隔符和换行符的选择
- 给Excel用的文件,编码用
utf-8-sig