
本文介绍使用python正则表达式精准识别并修复含嵌入式换行符(crlf/lf)的非标准csv数据,保留合法行结束符,仅将字段内部多余换行替换为空格或指定字符,适用于无引号包裹、结构依赖日期/id等模式识别的场景。
本文介绍使用python正则表达式精准识别并修复含嵌入式换行符(crlf/lf)的非标准csv数据,保留合法行结束符,仅将字段内部多余换行替换为空格或指定字符,适用于无引号包裹、结构依赖日期/id等模式识别的场景。
在处理来自外部系统的原始文本数据时,常遇到一种“伪CSV”格式:字段未用引号包裹,但某些字段(如description)本身包含换行符(\n 或 \r\n),导致单条逻辑记录被错误拆分为多行。而文件头又明确声明了列数(如 ID~First name~last name~description~birth date),说明每行应有且仅有5个字段——关键在于如何区分“真正的行结束符”和“字段内部的非法换行”。
核心思路是:不依赖逐行分割(f.readline()),而是基于每行的语义模式(如以数字ID开头、以MM/DD/YY格式日期结尾)进行正则匹配,将符合完整记录结构的连续文本块识别为一行,并统一清理其中的嵌入式换行。
以下提供两种生产就绪方案:
✅ 方案一:内存友好型(推荐用于大文件)
逐行读取并累积到缓冲区(buffer),当缓冲区内容匹配完整记录模式时,执行清理并写入:
import re
# 编译正则:匹配以数字ID开头、以日期结尾的5字段行(支持跨行)
pattern = re.compile(r'^\d+~[^~]*~[^~]*~[^~]*~\d{1,2}/\d{1,2}/\d{2}$', re.MULTILINE)
with open('sample.txt') as f_in, open('cleaned.csv', 'w') as f_out:
header = f_in.readline().rstrip('\r\n')
f_out.write(header.replace('~', ',') + '\n') # 转换分隔符
buffer = ''
for line in f_in:
buffer += line
# 尝试匹配:去除首尾空白后,若整段buffer符合模式,则为一条完整记录
stripped = buffer.strip()
if pattern.match(stripped):
# 替换所有内部换行为空格(保留末尾换行)
cleaned_line = stripped.replace('\n', ' ').replace('\r', ' ')
f_out.write(cleaned_line.replace('~', ',') + '\n')
buffer = '' # 清空缓冲区
# 若未匹配,继续累积(应对长description跨多行)⚠️ 注意事项:
- 正则中
^和$配合re.MULTILINE可确保匹配跨行字符串的起止;- 使用
strip()消除缓冲区首尾空白干扰;- 分隔符
~在输出前统一替换为,,便于后续CSV解析;- 此方案流式处理,内存占用恒定,适合GB级文件。
✅ 方案二:简洁一次性处理(适合中小文件)
若文件可全量载入内存,用 re.findall() 直接提取所有合法记录块:
import re
with open('sample.txt') as f_in:
content = f_in.read()
header, *body = content.split('\n', 1) # 分离首行头
header_csv = header.replace('~', ',').rstrip('\r\n')
# 匹配完整记录:ID + 3字段 + 日期,支持中间含换行
records = re.findall(
r'(\d+~[^~]*~[^~]*~[^~]*~\d{1,2}/\d{1,2}/\d{2})',
body[0] if body else '',
re.DOTALL
)
with open('cleaned.csv', 'w') as f_out:
f_out.write(header_csv + '\n')
for record in records:
# 清理记录内所有换行,替换为单空格
clean_record = record.replace('\n', ' ').replace('\r', ' ')
f_out.write(clean_record.replace('~', ',') + '\n')? 关键设计要点总结
-
模式可靠性:利用
ID(数字开头)和birth date(固定格式)作为锚点,比单纯计数~更鲁棒; -
非贪婪匹配:正则中避免
.*过度匹配,改用[^~]*精确限定字段边界; -
换行处理粒度:仅对匹配到的逻辑行内
\n/\r执行替换,绝不触碰行尾\n(由write(... + '\n')显式控制); -
扩展性:如需保留原始换行为
<br>或删除而非替换,只需修改replace()参数即可。
通过上述方法,您能稳定地将“破碎”的业务数据还原为结构清晰、可被标准CSV工具(如pandas、Excel)直接加载的合规格式。


















