如何解析含带引号字符串的CSV——进阶场景
解决带引号字符串和引号内分隔符的CSV解析需求
这个问题确实有点棘手,因为Python标准库的csv模块是按照RFC 4180规范设计的——默认会把引号当作字段的边界标记,解析后自动剥离引号,而引号内的分隔符虽然会被当作字段内容,但引号本身留不下来。不过要满足你保留引号+引号内未转义分隔符作为普通字符的需求,完全是可行的,下面给你两种实用方案:
方案一:自定义正则解析(简单直接)
如果你的CSV格式没有太复杂的转义场景(比如字段内没有转义的引号\"),用正则表达式直接匹配字段是最省心的方式。核心思路是:匹配两种字段类型——带引号的完整字段(包括引号本身)、不带引号的普通字段,这样既能保留引号,又不会拆分引号内的分隔符。
示例代码:
import re def parse_custom_csv(line, delimiter=','): # 正则规则:匹配带引号的字段(含引号) 或 不带引号的字段(直到遇到分隔符) pattern = re.compile(rf'(?:"[^"]*"|[^{delimiter}]+)') # 去除每个字段首尾可能的空白(如果不需要可以去掉这步) return [field.strip() for field in pattern.findall(line)] # 测试用例 test_line = '"user_id","alice, smith",email,"alice@example.com, work"' result = parse_custom_csv(test_line) print(result) # 输出:['"user_id"', '"alice, smith"', 'email', '"alice@example.com, work"']
如果需要支持字段内的转义引号(比如"he said \"hello\""),只需要调整正则规则,让它能识别转义的引号:
def parse_custom_csv_with_escape(line, delimiter=','): # 匹配带转义引号的字段:允许字段内出现 \" pattern = re.compile(rf'(?:"(?:\\.|[^"])*"|[^{delimiter}]+)') return [field.strip() for field in pattern.findall(line)] # 测试转义引号场景 test_line = '"message","he said \"hello, world\"",status,success' result = parse_custom_csv_with_escape(test_line) print(result) # 输出:['"message"', '"he said \"hello, world\""', 'status', 'success']
方案二:基于标准csv模块改造(兼容复杂场景)
如果你依赖csv模块的其他特性(比如处理多行字段),可以先让csv模块正常解析,再给原本带引号的字段重新添加引号。不过这种方法需要额外判断字段原本是否带引号,这里提供一种思路:
import csv from io import StringIO def parse_csv_with_quote_preservation(line, delimiter=','): # 先让csv模块解析(此时会剥离引号) reader = csv.reader(StringIO(line), delimiter=delimiter) parsed_fields = next(reader) # 重新匹配原行的带引号字段,给对应位置的字段加回引号 original_fields = re.findall(rf'(?:"[^"]*"|[^{delimiter}]+)', line) preserved_fields = [] for orig, parsed in zip(original_fields, parsed_fields): # 如果原字段是带引号的,就保留原引号内容;否则用解析后的内容 preserved_fields.append(orig if orig.startswith('"') and orig.endswith('"') else parsed) return preserved_fields # 测试 test_line = '"product","laptop, 15-inch",price,"999.99"' result = parse_csv_with_quote_preservation(test_line) print(result) # 输出:['"product"', '"laptop, 15-inch"', 'price', '"999.99"']
总结
- 如果你不需要处理多行字段、转义引号之外的复杂场景,方案一的正则解析是最直接高效的选择,完全满足你的两个核心需求。
- 如果需要兼容
csv模块的高级特性,再考虑方案二的改造方式。
内容的提问来源于stack exchange,提问作者ardabro
相关产品推荐
相关产品推荐

