如何优雅高效地从文本文件读取数值并构建numpy数组字典?
优化文本数据集解析为字典的实现方案
首先,你的核心思路没问题,但现有代码存在几个小语法问题(比如变量名带空格、with语句缺少open()、取正则匹配结果时未索引到具体元素),而且可以通过状态管理简化逻辑、利用numpy的批量加载提升效率,让代码更易读且高效。下面分享几种优化方案:
先看你的原始问题与代码
我有一个如下结构的文本文件(开头有意留空):
- 一些无关文本行
- Data set # 1
Time Data1 Data2
[s] [kg/m2] [C]1.0 2.45 465.5E-5 2.0 3.76 876.6E-6 ... // 表格末尾到下一行之间有空行 // Data set # 2 ... ... 我希望创建一个字典,键为数据集编号(如'1'),值为对应后续的浮点矩阵。目前我使用以下代码实现: ```python results = [] num_of points = [] # 语法错误:变量名不能包含空格 dict_results = {} regex_pattern = r'-?\ *[0-9]+\.?[0-9]*(?:[Ee]\ *-?\ *[0-9]+)?' # 匹配所有数值 with ('file.txt') as f: # 语法错误:缺少open()函数 for line in f: test_line = re.findall (regex_pattern, line) if len(test_line) == 1: # 判断是否是数据集编号行 if not num_of points: # 处理第一个数据集 current point = int (test_line) # 语法错误+逻辑错误:变量名空格,且test_line是列表需取[0] num_of_points.append(current_point) else: # 将数据存入字典 results_array = np.array(results , dtype= float) dict_results.update({num_of_points[-1] : results_array}) # 重置结果列表 results = [] # 更新数据集编号 current_point = int(test_line) num_of_points.append(current_point) elif len(test_line) == 3: # 判断是否是数据行 results.append(test_line) ``` 该代码似乎可以正常运行,但我想知道是否存在更优雅且高效的实现方式。非常感谢!
方案1:简化状态管理,用生成器拆分数据集
这个方案把解析逻辑拆分为生成器函数,代码模块化,逻辑清晰,适合大多数场景:
import re import numpy as np def parse_data_sets(file_path): # 预编译正则,提升匹配效率 regex_dataset = re.compile(r'Data set # (\d+)') regex_data_line = re.compile( r'(-?\s*\d+\.?\d*(?:[Ee]\s*-?\s*\d+)?)\s+' r'(-?\s*\d+\.?\d*(?:[Ee]\s*-?\s*\d+)?)\s+' r'(-?\s*\d+\.?\d*(?:[Ee]\s*-?\s*\d+)?)' ) current_dataset = None current_data = [] with open(file_path, 'r') as f: for line in f: line = line.strip() if not line: continue # 跳过空行 # 匹配数据集编号行 dataset_match = regex_dataset.match(line) if dataset_match: # 保存上一个数据集(如果存在) if current_dataset is not None: yield current_dataset, np.array(current_data, dtype=np.float64) current_data = [] current_dataset = dataset_match.group(1) continue # 匹配数据行(必须有三个数值) data_match = regex_data_line.match(line) if data_match and current_dataset is not None: # 提取并转换为浮点数 values = [float(val.strip()) for val in data_match.groups()] current_data.append(values) # 处理文件末尾的最后一个数据集 if current_dataset is not None and current_data: yield current_dataset, np.array(current_data, dtype=np.float64) # 生成最终字典 dict_results = dict(parse_data_sets('file.txt'))
优化亮点:
- 用生成器函数分离解析逻辑与字典构建,代码更易维护
- 预编译正则表达式,避免循环中重复编译,提升匹配速度
- 显式跳过空行与无关文本,减少无效判断
- 修复了原代码中的所有语法与逻辑错误
方案2:用numpy批量加载,提升大文件处理效率
如果你的数据集很大,用numpy的genfromtxt批量读取数据,比逐行解析效率高很多:
import numpy as np import re def parse_data_sets_fast(file_path): dict_results = {} regex_dataset = re.compile(r'Data set # (\d+)') with open(file_path, 'r') as f: while True: line = f.readline() if not line: break # 文件读取完毕 line = line.strip() dataset_match = regex_dataset.match(line) if dataset_match: dataset_id = dataset_match.group(1) # 跳过表头、分隔线,定位到第一个数据行 while True: skip_line = f.readline() if not skip_line: break # 尝试转换第一个值,判断是否是数据行 try: float(skip_line.strip().split()[0]) # 把文件指针移回该行开头 f.seek(f.tell() - len(skip_line)) break except (ValueError, IndexError): continue # 批量加载数据,自动跳过注释行(//开头) data = np.genfromtxt(f, dtype=np.float64, comments='//', invalid_raise=False) # 过滤无效的NaN行,保证数据干净 data = data[~np.isnan(data).any(axis=1)] if len(data) > 0: dict_results[dataset_id] = data return dict_results dict_results = parse_data_sets_fast('file.txt')
优化亮点:
- 利用numpy的底层优化,批量读取数据,大幅提升大文件处理速度
- 用文件指针精准定位数据起始位置,跳过无关内容
- 自动识别并跳过
//开头的注释行 - 过滤无效行,确保存储的矩阵都是有效数值
方案3:极简版(适合结构固定的小文件)
如果你的文件结构非常固定,一次性读取整个文件再解析,代码会非常简洁:
import re import numpy as np with open('file.txt', 'r') as f: content = f.read() # 匹配所有数据集:编号 + 后续的所有内容(直到下一个数据集或文件结束) dataset_pattern = re.compile(r'Data set # (\d+).*?\n(.*?)(?=\nData set #|\Z)', re.DOTALL) dict_results = {} for match in dataset_pattern.finditer(content): dataset_id = match.group(1) data_lines = match.group(2).strip().split('\n') # 过滤非数据行,转换为浮点数矩阵 data = [] for line in data_lines: line = line.strip() # 跳过空行、注释、表头、分隔线 if not line or line.startswith('//') or '[' in line or '~' in line: continue try: values = list(map(float, line.split())) if len(values) == 3: data.append(values) except ValueError: continue if data: dict_results[dataset_id] = np.array(data, dtype=np.float64)
优化亮点:
- 一次性读取文件,减少IO操作次数
- 用
re.DOTALL模式匹配多行内容,一次性提取整个数据集 - 代码极简,适合结构固定的小文件快速解析
内容的提问来源于stack exchange,提问作者PathFinder
相关产品推荐
相关产品推荐

