如何优化硬编码Python脚本,实现多文本文件特定行灵活提取?
LAS文本报告信息提取脚本优化
问题背景
作为Python新手,我借助ChatGPT及本站问答编写了一段脚本,用于处理指定目录下的多个LAS工具生成的.txt报告文件,提取LAS文件名、PROJCS投影信息、点密度等内容。但当前脚本硬编码程度高,依赖固定字符索引切片提取内容,一旦报告格式稍有变化就会失效,希望优化成自动搜索定位目标内容的版本,提升通用性。
原始代码
import os def extract_lines(input_file, output_file): with open(input_file, 'r') as file: # Reads in las filename linea = [line for line in file if line.startswith('lasinfo')] linea = str(linea) linea = linea[31:-5] if len(linea) == 0: linea ='No filename defined!' with open(input_file, 'r') as file: # Reads in las datum lineb = [line for line in file if line.startswith(' PROJCS')] lineb = str(lineb) lineb = lineb[14:41] if len(lineb) == 0: lineb ='No DATUM defined!' with open(input_file, 'r') as file: # Reads in las point density details linec = [line for line in file if line.startswith('point density')] linec = str(linec) linec = linec[2:-4] lined = linec[27:31] # strips out density (points/m) value from statement if len(linec) == 0: linec ='No point density listed' lineout = linea + "," + lineb + "," + linec + "," + lined + '\n' with open(output_file, 'a') as output: output.write(lineout) def process_txt_files(directory_path, output_file): # Scans directory for .txt files to process with open(output_file, 'w'): pass for filename in os.listdir(directory_path): if filename.endswith(".txt"): file_path = os.path.join(directory_path, filename) extract_lines(file_path, output_file) directory_path = 'C:/Users/rinicholls/Richard/Gnarabup_LiDAR/LiDAR/Gnarabup_South_AVWS/reports' output_file = 'density.txt' process_txt_files(directory_path, output_file)
待处理的.txt报告示例
lasinfo (231025) report for 'Gnarabup_South_sph_e310n6230_1AVWS.las' reporting all LAS header entries: file signature: 'LASF' file source ID: 0 global_encoding: 1 project ID GUID data 1-4: 00000000-0000-0000-0000-000000000000 version major.minor: 1.4 system identifier: 'LAStools (c) by rapidlasso GmbH' generating software: 'lasheight (210418) commercial' file creation day/year: 278/2023 header size: 375 offset to point data: 375 number var. length records: 0 point data format: 6 point data record length: 30 number of point records: 0 number of points by return: 0 0 0 0 0 scale factor x y z: 0.01 0.01 0.01 offset x y z: 0 0 0 min x y z: 311439.81 6230000.08 -38.91 max x y z: 311999.99 6231999.80 -33.17 start of waveform data packet record: 0 start of first extended variable length record: 0 number of extended_variable length records: 0 extended number of point records: 7586 extended number of points by return: 7586 0 0 0 0 0 0 0 0 0 0 0 0 0 0 reporting minimum and maximum for all LAS point record entries ... X 31143981 31199999 Y 623000008 623199980 Z -3891 -3317 intensity 0 0 return_number 1 1 number_of_returns 1 1 edge_of_flight_line 0 1 scan_direction_flag 0 1 classification 13 13 scan_angle_rank -11 18 user_data 1 1 point_source_ID 174 177 gps_time 363998052.416096 363998902.558917 extended_return_number 1 1 extended_number_of_returns 1 1 extended_classification 13 13 extended_scan_angle -1820 2924 extended_scanner_channel 0 0 number of first returns: 7586 number of intermediate returns: 0 number of last returns: 7586 number of single returns: 7586 covered area in square units/kilounits: 30136/0.03 point density: all returns 0.25 last only 0.25 (per square units) spacing: all returns 1.99 last only 1.99 (in units) overview over extended number of returns of given pulse: 7586 0 0 0 0 0 0 0 0 0 0 0 0 0 0 histogram of classification of points: 7586 wire guard (13)
优化后的代码
import os import re def extract_las_info(file_lines): # 提取LAS文件名 las_filename = "No filename defined!" lasinfo_pattern = re.compile(r"lasinfo \(.*\) report for '([^']+)'") for line in file_lines: match = lasinfo_pattern.match(line.strip()) if match: las_filename = match.group(1) break # 提取PROJCS信息 projcs_info = "No DATUM defined!" projcs_pattern = re.compile(r" PROJCS:?\s*(.*)") for line in file_lines: match = projcs_pattern.match(line.strip()) if match: projcs_info = match.group(1).strip() break # 提取点密度信息 density_info = "No point density listed" density_value = "" density_pattern = re.compile(r"point density: (.*)") value_pattern = re.compile(r"all returns (\d+\.\d+)") for line in file_lines: match = density_pattern.match(line.strip()) if match: density_info = match.group(1).strip() value_match = value_pattern.search(density_info) if value_match: density_value = value_match.group(1) break return las_filename, projcs_info, density_info, density_value def extract_lines(input_file, output_file): # 一次性读取所有行,减少IO操作 with open(input_file, 'r') as file: file_lines = file.readlines() las_filename, projcs_info, density_info, density_value = extract_las_info(file_lines) # 处理密度值为空的情况 if not density_value and density_info != "No point density listed": density_value = "N/A" lineout = f"{las_filename},{projcs_info},{density_info},{density_value}\n" with open(output_file, 'a') as output: output.write(lineout) def process_txt_files(directory_path, output_file): # 清空输出文件 with open(output_file, 'w'): pass # 遍历目录下的txt文件 for filename in os.listdir(directory_path): if filename.lower().endswith(".txt"): file_path = os.path.join(directory_path, filename) extract_lines(file_path, output_file) # 配置路径 directory_path = 'C:/Users/rinicholls/Richard/Gnarabup_LiDAR/LiDAR/Gnarabup_South_AVWS/reports' output_file = 'density.txt' process_txt_files(directory_path, output_file)
优化说明
- 减少文件IO操作:一次性读取文件所有行,避免重复打开同一文件多次,提升效率
- 正则表达式提取:不再依赖固定字符索引切片,通过正则匹配目标内容,即使报告格式有小幅度变化(比如空格数量不同)也能正常提取
- 增强容错性:对缺失信息的场景(如无PROJCS、点密度值无法提取)做了更完善的处理,避免空值或错误内容
- 代码结构更清晰:将信息提取逻辑封装成单独函数,便于维护和扩展后续需要提取的其他字段
内容的提问来源于stack exchange,提问作者Rich
相关产品推荐
相关产品推荐

