CSV本质上只是一个纯文本文件,由逗号分隔的一行行记录组成,它并不像数据库那样支持随机定位修改。所以想在Python里改CSV中的某个单元格,思路只有一条:把文件内容读进内存(通常是列表的列表),在内存里修改对应位置的值,再把整个列表覆写回文件。这个流程看似简单,但其中有不少细节值得注意,下面结合实例逐步展开。

基础流程:读入列表、定位修改、覆写回文件
先看最常见的场景:已知要修改的数据在第几行第几列,把它替换成新值。用内置的csv模块配合列表操作即可完成。关键是打开文件读取时用csv.reader把每行转成列表,所有行汇成一个二维列表,这样就能像操作数组一样用下标访问任意单元格了。
import csv
# 读取整个CSV到二维列表
with open('data.csv', 'r', encoding='utf-8', newline='') as f:
rows = list(csv.reader(f))
# 假设要修改第3行(下标从0开始)第2列的值
rows[2][1] = '新值'
# 覆写回原文件
with open('data.csv', 'w', encoding='utf-8', newline='') as f:
csv.writer(f).writerows(rows)这里有几个必须强调的细节。第一,读和写是两个独立的open操作,不能在同一个文件句柄上先读后写再指望原地更新,那样会导致数据错乱或截断。第二,newline=''参数非常重要,官方文档明确要求这样写。如果不加,在Windows平台上写入的CSV可能出现空行隔行的问题,因为csv模块自己处理换行,Python默认的universal newline机制会额外再转换一次。第三,读写时的编码必须保持一致,否则中文内容很容易变成乱码。Windows下Excel生成的CSV常常是GBK编码,读取时可以指定encoding='gbk',写入时也保持同样的编码,就能保证数据不损坏。
另外,下标定位前最好加一层边界检查。如果行号或列号超出列表范围,rows[100][5] = 'x'会直接抛出IndexError。稳妥的写法是先判断len(rows) > 2 and len(rows[2]) > 1再执行赋值,避免脚本在处理不规则CSV时崩溃。
按条件匹配:根据某列的值找到目标行再修改
实际业务中更常见的情况是不知道行号,只知道某一列的值,比如把姓名为张三的那一行的工资改成9000。这时候需要遍历列表,找到匹配的行再修改对应列。遍历过程中注意表头行的处理,通常第一行是字段名,要从下标1开始遍历,或者先单独弹出表头。
import csv
with open('staff.csv', 'r', encoding='utf-8', newline='') as f:
rows = list(csv.reader(f))
header, body = rows[0], rows[1:]
name_col = 0 # 姓名所在列
salary_col = 2 # 工资所在列
target_name = '张三'
for row in body:
if row[name_col] == target_name:
row[salary_col] = '9000'
with open('staff.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(header)
writer.writerows(body)这种写法可以顺便统计修改了几条记录,比如加一个计数器,循环结束后如果计数为零说明没有找到目标,可以打印提示而不是静默成功。还要注意CSV里读出来的所有值都是字符串类型,做数值比较时要先转换,例如float(row[salary_col]) < 5000,写回时再转成字符串。如果匹配的行可能有多条,上面这个循环会全部修改,这正是我们通常想要的行为;如果只想改第一条,可以在修改后用break跳出。
当列比较多、字段名可能变化时,建议用csv.DictReader和csv.DictWriter按字典方式操作,通过字段名而不是魔法数字访问列,代码可读性和健壮性都会好很多:
import csv
with open('staff.csv', 'r', encoding='utf-8', newline='') as f:
reader = csv.DictReader(f)
fieldnames = reader.fieldnames
rows = list(reader)
for row in rows:
if row['姓名'] == '张三':
row['工资'] = '9000'
with open('staff.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)覆写的安全性与替代方案
直接覆写原文件有一个天然的风险:写文件是先清空再写入,如果中途程序崩溃、磁盘写满或者数据量太大导致内存不足,原文件可能已经损坏且无法恢复。因此对于重要数据,推荐的工法是先写到临时文件,确认写完整后再替换原文件。标准库的os.replace可以原子性地完成替换动作,比先删除再重命名安全得多。
import csv, os
with open('staff.csv', 'r', encoding='utf-8', newline='') as f:
rows = list(csv.reader(f))
rows[2][1] = '新值'
with open('staff_tmp.csv', 'w', encoding='utf-8', newline='') as f:
csv.writer(f).writerows(rows)
os.replace('staff_tmp.csv', 'staff.csv')除了安全性,还要留意引号与特殊字符的处理。csv.writer默认使用QUOTE_MINIMAL策略,即只有字段中包含逗号、换行符或引号时才加双引号包裹,这与绝大多数工具兼容。但Excel生成的文件换行是CRLF(\r\n),如果下游系统严格依赖这种风格,写入时可以显式传lineterminator='\r\n'。含中文逗号、全角字符的CSV看起来像CSV,实际分隔符不是半角逗号,读取时要通过delimiter参数指定正确的分隔符,否则切出来的列会不对。
最后提一下更省心的替代方案。如果数据处理逻辑比较复杂,用pandas会更直观:df = pd.read_csv('data.csv')读入后可以用df.loc[df['姓名']=='张三', '工资'] = 9000一行完成条件修改,再用df.to_csv('data.csv', index=False)写回。pandas默认会把空值读成NaN,写回时空字段可能与原文件格式略有差异,纯文本场景下标准库csv模块反而更忠实于原文件。对于几百行的小文件,标准库方案足够;对于几万行以上的数据或需要复杂计算的场景,pandas的效率和维护性优势就体现出来了。
Python修改CSVcsv模块特定单元格修改时间:2026-09-10 02:51:20