本文详解如何使用 Python 高效解析含分隔符的纯文本文件,通过字符串分割、字符过滤和类型转换,从不规则格式(如“课程 | 97.0% | 3.0 Credits |”)中稳健提取 GPA 计算所需的成绩与学分数值,并封装为 (float, float) 元组列表。
本文详解如何使用 python 高效解析含分隔符的纯文本文件,通过字符串分割、字符过滤和类型转换,从不规则格式(如“课程 | 97.0% | 3.0 credits |”)中稳健提取 gpa 计算所需的成绩与学分数值,并封装为 `(float, float)` 元组列表。
在实际数据处理中,我们常需从非标准格式的 .txt 文件中提取结构化信息——例如本例中的学生成绩单。原始文件虽有人类可读性(如 Government | 97.0% | 3.0 Credits |),但直接用 isdecimal() 或逐字符判断易引入噪声(如误捕课程名中的数字 1、2,或残留分隔符 |)。关键在于分层抽象:先按行切分,再按字段分隔,最后在目标字段内精准提纯数字。
✅ 推荐方案:基于字段定位 + 字符白名单过滤
核心思路是:
- 每行以 | 分割为字段(类似简易 CSV);
- 明确指定成绩字段(索引 1)和学分字段(索引 2);
- 对目标字段仅保留数字和小数点,丢弃 %、Credits、空格等干扰字符;
- 安全转换为浮点数,构建 (grade, credits) 元组。
以下是完整、健壮且可复用的实现:
# 定义常量提升可维护性
DECIMAL_CHARS = set('0123456789.') # 白名单:仅允许数字和小数点
GRADE_FIELD_IDX = 1 # 成绩位于分割后的第2个字段(索引1)
CREDIT_FIELD_IDX = 2 # 学分位于分割后的第3个字段(索引2)
def extract_decimal(s: str) -> str:
"""从字符串中提取所有数字和小数点,拼接为连续数字字符串"""
return ''.join(c for c in s if c in DECIMAL_CHARS)
def parse_grade_line(line: str) -> tuple[float, float]:
"""解析单行,返回 (成绩, 学分) 元组"""
fields = [field.strip() for field in line.split('|')] # 分割并去首尾空格
if len(fields) < 3:
raise ValueError(f"行格式异常,字段不足3个: {line.strip()}")
grade_str = extract_decimal(fields[GRADE_FIELD_IDX])
credit_str = extract_decimal(fields[CREDIT_FIELD_IDX])
try:
return float(grade_str), float(credit_str)
except ValueError as e:
raise ValueError(f"无法转换为浮点数 — 成绩'{grade_str}' 或 学分'{credit_str}' 格式错误") from e
# 主流程:读取文件并批量解析
records = []
with open("Grades.txt", "r", encoding="utf-8") as f:
for line_num, line in enumerate(f, start=1):
line = line.strip()
if not line: # 跳过空行
continue
try:
records.append(parse_grade_line(line))
except ValueError as e:
print(f"警告(第{line_num}行): {e}")
print("解析结果(格式:(成绩, 学分)):")
print(records)输出示例:
立即学习“Python免费学习笔记(深入)”;
解析结果(格式:(成绩, 学分)): [(97.0, 3.0), (87.0, 3.0), (76.0, 4.0), (93.0, 3.0), (83.0, 4.0), (75.0, 3.0), (56.6, 4.0), (89.4, 3.0)]
⚠️ 关键注意事项
- 编码安全:显式指定 encoding="utf-8" 避免 Windows 系统下中文或特殊符号乱码;
- 空行/异常行防护:strip() 和 if not line 过滤空白行,len(fields)
- 错误定位友好:捕获异常时附带行号,便于调试;
- 避免过度正则:对本例中规律性强的格式,白名单过滤比正则更直观、高效且不易出错;
- 扩展性提示:若后续需支持更多字段(如课程名、学期),可将 parse_grade_line 改为返回命名元组或字典。
? 总结
文本解析的本质不是“匹配所有可能”,而是“在已知模式下最小化假设”。本方案放弃全局字符扫描,转而依赖字段位置与内容语义(成绩字段必含数字+小数点+%),大幅提升鲁棒性。掌握这种“分层切割 + 目标提纯”的思维,你就能从容应对各类现实世界中的半结构化文本——无论是日志、报表还是配置文件。下一步,可将 records 列表直接用于 GPA 计算(加权平均:sum(g*c for g,c in records) / sum(c for _,c in records))。










