Python正则捕获航班排班数据异常及替代实现方案咨询
航班排班PDF解析的正则问题解决思路
我正在开发一个航空通勤追踪项目,用pypdf解析PDF排班表并存入SQLite3。现在遇到正则问题:能匹配包含多航班的整段排班内容,但只能提取最后一个航班的信息,无法完整捕获每日所有航班数据。
附相关数据与代码
排班文本片段
...[xFDP 00:00][DTwSB 08:00] Thu22 C/I ABC 0530 XX 1260 ABC 0630 !1135 DEF S7M8XX 1261 DEF 1220 !1540 ABC S7M8 C/O!1600 ABC [FT 08:25] [DT 10:30][FDT 10:10]...
原正则代码
flight_duty_regex = re.compile(r'''( (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) # Weekday (\d{2})\s # Date C/I\s # Check-in (\w{3})\s # Check-in station (!?\d{4})\s # Check-in time (optional ! for different timezone) (?:(XX|YY)\s # Prefix (\d{3,4})\s # Flight number (\w{3})\s # Departure station (!?\d{4})\s # Departure time (optional ! for different timezone) (!?\d{4})\s # Arrival time (optional ! for different timezone) (\w{3})\s # Arrival station (?:S7M8|S738)\s?)+ # Aircraft type C/O # Check-out (!?\d{4})\s # Check-out time (optional ! for different timezone) (\w{3}) # Check-out station )''', re.VERBOSE)
原输出结果
[('Thu22 C/I ABC 0530\nXX 1260 ABC 0630 !1135 DEF S7M8XX 1261 DEF 1220 !1540 ABC S7M8\nC/O!1600 ABC', '22', 'ABC', '0530', 'XX', '1261', 'DEF', '1220', '!1540', 'ABC', '!1600', 'ABC')]
问题根源
你写的正则里,航班相关的捕获组被放在重复的非捕获组(?:...)+中。Python正则引擎的规则是:重复的捕获组只会保留最后一次匹配的结果,前面匹配的航班数据会被后续匹配覆盖,所以最终只能拿到最后一个航班的信息。
优化实现方案
1. 拆分正则,分两步处理
先匹配完整的每日排班块,再从块中提取所有航班条目,彻底避免重复组覆盖的问题:
- 第一步:匹配包含日期、签到、所有航班、签出的完整排班段
- 第二步:用单独的正则从该段中提取每个航班的详细信息
示例代码:
import re # 第一步:匹配完整的每日排班块 duty_block_regex = re.compile(r''' ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\d{2}\sC/I\s\w{3}\s!?\d{4}) \n (.+?) \n (C/O!?\d{4}\s\w{3}) ''', re.VERBOSE | re.DOTALL) # 第二步:匹配单个航班信息 flight_regex = re.compile(r''' (XX|YY)\s (\d{3,4})\s (\w{3})\s (!?\d{4})\s (!?\d{4})\s (\w{3})\s (S7M8|S738) ''', re.VERBOSE) # 测试文本 roster_text = '''...[xFDP 00:00][DTwSB 08:00] Thu22 C/I ABC 0530 XX 1260 ABC 0630 !1135 DEF S7M8XX 1261 DEF 1220 !1540 ABC S7M8 C/O!1600 ABC [FT 08:25] [DT 10:30][FDT 10:10]...''' # 提取排班块并解析 for match in duty_block_regex.finditer(roster_text): check_in_line, flights_line, check_out_line = match.groups() # 解析签到信息 ci_match = re.match(r'(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)(\d{2})\sC/I\s(\w{3})\s(!?\d{4})', check_in_line) weekday_date, ci_station, ci_time = ci_match.groups() # 解析签出信息 co_match = re.match(r'C/O(!?\d{4})\s(\w{3})', check_out_line) co_time, co_station = co_match.groups() # 提取所有航班 flights = [] for flight_match in flight_regex.finditer(flights_line): prefix, flight_num, dep_station, dep_time, arr_time, arr_station, aircraft = flight_match.groups() flights.append({ 'prefix': prefix, 'flight_num': flight_num, 'dep_station': dep_station, 'dep_time': dep_time, 'arr_time': arr_time, 'arr_station': arr_station, 'aircraft': aircraft }) # 输出结果 print(f"日期: {weekday_date}") print(f"签到: {ci_station} {ci_time}") print(f"签出: {co_station} {co_time}") print("航班列表:") for idx, flight in enumerate(flights, 1): print(f" 航班{idx}: {flight['prefix']}{flight['flight_num']} | {flight['dep_station']}{flight['dep_time']} -> {flight['arr_station']}{flight['arr_time']} | 机型:{flight['aircraft']}")
2. 使用命名组提升可读性
给正则捕获组命名,能让后续处理代码更清晰,避免靠索引取值出错:
flight_regex = re.compile(r''' (?P<prefix>XX|YY)\s (?P<flight_num>\d{3,4})\s (?P<dep_station>\w{3})\s (?P<dep_time>!?\d{4})\s (?P<arr_time>!?\d{4})\s (?P<arr_station>\w{3})\s (?P<aircraft>S7M8|S738) ''', re.VERBOSE) # 提取时直接用组名 flight_data = flight_match.groupdict()
3. 逐行搜索的补充思路
如果排班表格式固定,可先按行分割文本,再针对不同行类型分别处理:
- 签到行:匹配
(Mon|Tue...)XX C/I ...格式 - 航班行:匹配
XX 1234 ...格式 - 签出行:匹配
C/O ...格式
这种方式逻辑更直观,适合格式稳定的排班表,也更容易调试。
内容的提问来源于stack exchange,提问作者bergand
相关产品推荐
相关产品推荐

