
本文详解如何将格式化的 sql 查询输出字符串(含表头与多行数据)高效转换为结构化字典列表,兼容 python 2 与 python 3,并支持空格分隔的字段解析及常见边界情况处理。
本文详解如何将格式化的 sql 查询输出字符串(含表头与多行数据)高效转换为结构化字典列表,兼容 python 2 与 python 3,并支持空格分隔的字段解析及常见边界情况处理。
在实际数据处理中,尤其是与遗留数据库交互或调用 CLI 工具执行 SQL 时,常会得到纯文本格式的查询结果(如 psql -t 或某些 ORM 的原始输出),其结构类似:
enable id ============= f avc-qwqwq t abd-rrtrtr f rec-yyuyu
该文本包含表头行、分隔线(============)和若干数据行。目标是将其转化为标准的结构化对象数组(即 Python 中的 list[dict]),例如:
[ {'enable': 'f', 'id': 'avc-qwqwq'}, {'enable': 't', 'id': 'abd-rrtrtr'}, {'enable': 'f', 'id': 'rec-yyuyu'} ]
✅ 核心思路:分步解析 + 动态映射
- 按行切分:使用 str.splitlines() 安全处理跨平台换行符(n, rn),避免 split(‘n’) 在末尾空行或 windows 环境下的潜在问题;
- 提取表头:跳过分隔线(第二行),取首行并用 str.split() 拆分为字段名(自动忽略多余空白);
- 逐行构建对象:对每条数据行,同样用 split() 提取非空字段,再通过 zip(header, values) 与 dict() 动态构造键值对。
? 推荐实现(Python 3 原生简洁版)
text = '''enable id ============= f avc-qwqwq t abd-rrtrtr f rec-yyuyu''' # 步骤1:安全按行分割(自动过滤空行 & 兼容换行符) lines = [line.strip() for line in text.splitlines() if line.strip()] # 步骤2:提取表头(第一行),忽略分隔线(第二行起非空行即为数据) header = lines[0].split() data_rows = lines[2:] # 跳过 header + separator # 步骤3:逐行解析 → 构建字典列表 result = [] for row in data_rows: values = row.split() # 按任意空白分割,鲁棒性强 if len(values) >= len(header): # 防止字段数不匹配 obj = dict(zip(header, values[:len(header)])) result.append(obj) print(result) # 输出: # [{'enable': 'f', 'id': 'avc-qwqwq'}, {'enable': 't', 'id': 'abd-rrtrtr'}, {'enable': 'f', 'id': 'rec-yyuyu'}]
⚙️ Python 2 兼容写法(无解包语法)
Python 2 不支持 a, b, *rest = … 解包,改用迭代器显式消费:
text = '''enable id ============= f avc-qwqwq t abd-rrtrtr f rec-yyuyu''' lines_iter = iter(text.splitlines()) header_line = next(lines_iter).strip() _ = next(lines_iter) # 跳过分隔线 data_lines = [line.strip() for line in lines_iter if line.strip()] header = header_line.split() result = [ dict(zip(header, row.split())) for row in data_lines if row.split() # 过滤空行 ] print(result)
⚠️ 注意事项与健壮性增强
-
字段对齐风险:若原始数据含空格但非分隔符(如 org 字段值为 “abc def”),split() 会错误切分。此时需改用固定列宽解析或正则提取:
import re # 假设 header 位置已知:'enable' 占前2字符,'id' 从第4列起 pattern = r'^(S{1,2})s+(S.*)$' for row in data_lines: match = re.match(pattern, row) if match: enable, org = match.groups() result.append({'enable': enable.strip(), 'id': org.strip()}) -
大小写/空格标准化:建议对 header 和 values 统一 strip(),避免 ‘enable ‘ 与 ‘enable’ 键不一致;
-
缺失字段兜底:可为 zip 补充默认值,避免 dict(zip(h, v)) 因长度不等报错:
from itertools import zip_longest obj = dict(zip_longest(header, values, fillvalue='')) -
编码兼容性:若字符串含非 ASCII 字符(如中文字段),确保源码文件声明 # -*- coding: utf-8 -*-,并在 Python 2 中显式解码 text.decode(‘utf-8’)。
✅ 总结
该方案以最小依赖、最大兼容性完成「文本表格 → 对象数组」的转换:
? 利用 splitlines() + strip() 处理换行与空白,普适性强;
? split() 默认按任意空白分割,天然适应多空格对齐场景;
? zip(header, values) 实现动态字段映射,无需硬编码键名;
? 两版本代码均通过空行过滤、长度校验提升鲁棒性。
适用于日志解析、CLI 工具集成、轻量 etl 等典型场景。