You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:50:20