航班号解析技术:正则表达式与字符串处理在航空系统的实践
在航空运输和票务系统开发中航班号解析是一个看似简单但实际涉及多种业务规则的技术点。以“空白航空1145”为例这个字符串可能来源于用户输入、第三方数据接口或内部系统生成开发人员需要从中准确提取出航空公司标识如IATA二字码、ICAO三字码或自定义航司代码和航班序号并处理可能存在的空格、特殊字符或命名不规范情况。这类解析逻辑直接关系到航班信息查询、票务预订、航变通知等核心业务流程的准确性。本文将基于一个虚构的“空白航空”案例从零构建一个健壮的航班号解析工具。我们会先定义航班号的标准格式和常见变异然后使用正则表达式和字符串处理技术实现解析核心接着封装成可复用的函数或类并编写单元测试验证边界情况。最后我们会讨论在实际项目中如何集成此类工具以及遇到解析失败时的排查思路和日志记录策略。1. 理解航班号的结构与解析挑战航班号通常由航空公司代码和航班序号两部分组成但实际数据来源中可能存在多种格式变体。如果解析规则设计不严谨很容易导致业务逻辑错误或数据不一致。1.1 标准航班号的组成国际航空运输协会IATA定义的航班号一般遵循以下规则航空公司代码2位字母如CA代表国航或3位数字如724代表青岛航空。航班序号1到4位数字用于区分同一航司的不同航班。完整航班号示例CA1234、MU511、CZ3101。但在实际业务数据流中你可能会遇到带有空格或连字符的格式“CA 1234”或“CA-1234”。航空公司名称与航班号混合出现“空白航空1145”。航班序号包含字母或特殊字符较少见但某些系统会产生。字符串前后有多余空格或不可见字符。1.2 解析失败的业务影响如果航班号解析错误可能会导致查询不到真实的航班动态信息。票务系统无法正确匹配运价和舱位。旅客收到的航变通知或登机口信息错误。数据统计时航班量计算不准。因此解析逻辑必须兼顾标准格式处理和常见变异兼容。2. 环境准备与依赖配置我们使用Python来实现航班号解析器因为Python在数据处理和字符串操作方面非常高效且易于集成到各种业务系统中。以下环境适用于大多数开发场景。2.1 基础环境要求Python 3.7或更高版本本文示例使用Python 3.8.5验证。操作系统Windows 10、macOS Big Sur或主流Linux发行版均可。代码编辑器或IDEVS Code、PyCharm或任何你熟悉的工具。检查Python环境是否就绪python --version # 预期输出Python 3.x.x2.2 项目结构规划创建一个名为flight_parser的目录结构如下flight_parser/ ├── src/ │ └── flight_parser.py # 核心解析逻辑 ├── tests/ │ └── test_parser.py # 单元测试 ├── requirements.txt # 项目依赖 └── README.md # 项目说明2.3 依赖包管理本项目仅使用Python标准库无需额外安装第三方包。但为了代码质量和测试便利建议在requirements.txt中记录开发工具# 以下为可选开发工具非运行时必需 pytest6.0.0 # 单元测试框架 black20.8b0 # 代码格式化工具安装开发依赖pip install -r requirements.txt3. 实现航班号解析核心逻辑我们将采用正则表达式匹配为主、字符串清理为辅的方案确保能够处理多种航班号格式。3.1 定义航班号解析规则首先分析“空白航空1145”这类字符串的模式航空公司部分中英文名称、IATA代码、ICAO代码或自定义标识。数字部分1-4位航班序号。分隔符空格、连字符或直接连接。设计正则表达式模式import re # 基础模式匹配航空公司和航班序号 flight_pattern re.compile( r^(?:[a-zA-Z]{2}\s?|\d{3}\s?|[a-zA-Z]{3}\s?|[\u4e00-\u9fa5]\s?)? # 航空公司代码或名称可选 r(\d{1,4})$ # 航班序号必需 )但这个模式过于简单我们需要更全面的解决方案。3.2 完整的航班号解析类创建src/flight_parser.py文件实现完整的解析逻辑import re import logging from typing import Optional, Dict, Tuple class FlightNumberParser: 航班号解析器支持多种格式的航班号提取 def __init__(self): # 配置日志 self.logger logging.getLogger(__name__) # 常见航空公司代码映射示例数据 self.airline_codes { CA: 中国国际航空, MU: 中国东方航空, CZ: 中国南方航空, 空白航空: BK, # 虚构航司映射 北京航空: BJ, } # 主正则表达式模式匹配航空名称 数字或代码数字格式 self.patterns [ # 模式1中文航空名称 空格 数字如空白航空 1145 re.compile(r^([\u4e00-\u9fa5]航空)\s*(\d{1,4})$), # 模式2IATA代码 空格/连字符/无分隔 数字如CA 1145、CA-1145、CA1145 re.compile(r^([A-Z]{2})[\s\-]*(\d{1,4})$), # 模式3纯数字航班号如1145 re.compile(r^(\d{1,4})$), ] def parse(self, flight_input: str) - Optional[Dict]: 解析航班号字符串返回结构化数据 Args: flight_input: 航班号输入字符串如空白航空1145 Returns: Dict: 包含航空公司代码、航班序号等信息的字典解析失败返回None if not flight_input or not isinstance(flight_input, str): self.logger.warning(输入为空或非字符串类型) return None # 清理输入字符串去除前后空格、统一空格处理 cleaned_input flight_input.strip().replace( , ) # 替换全角空格 self.logger.debug(f清理后输入: {cleaned_input}) # 依次尝试不同模式进行匹配 for i, pattern in enumerate(self.patterns): match pattern.match(cleaned_input) if match: self.logger.debug(f模式{i1}匹配成功: {match.groups()}) return self._process_match(match.groups(), pattern_idxi) self.logger.warning(f无法解析的航班号格式: {flight_input}) return None def _process_match(self, match_groups: Tuple, pattern_idx: int) - Dict: 处理正则匹配结果转换为结构化数据 result {} if pattern_idx 0: # 中文航空名称 数字 airline_name, flight_num match_groups result[airline_name] airline_name result[flight_number] flight_num result[airline_code] self.airline_codes.get(airline_name, UNKNOWN) elif pattern_idx 1: # IATA代码 数字 airline_code, flight_num match_groups result[airline_code] airline_code result[flight_number] flight_num result[airline_name] self._get_airline_name(airline_code) elif pattern_idx 2: # 纯数字 flight_num match_groups[0] result[flight_number] flight_num result[airline_code] UNKNOWN result[airline_name] 未知航空公司 result[full_flight_number] f{result[airline_code]}{result[flight_number]} return result def _get_airline_name(self, code: str) - str: 根据航空公司代码获取名称 for code_key, name in self.airline_codes.items(): if code code_key or (len(code_key) 2 and code code_key): return name return 未知航空公司 # 便捷函数 def parse_flight_number(flight_input: str) - Optional[Dict]: 解析航班号的便捷函数 parser FlightNumberParser() return parser.parse(flight_input)3.3 关键代码解释正则表达式设计模式1专门处理中文航空名称格式如空白航空1145。模式2处理标准IATA代码格式兼容多种分隔符。模式3处理只有航班序号的情况。输入清理使用strip()去除前后空格。替换全角空格为半角空格避免编码问题。多模式匹配按优先级依次尝试不同模式提高匹配成功率。每个模式匹配成功后立即返回避免不必要的后续匹配。结果标准化统一返回包含航空公司代码、名称、航班序号和完整航班号的字典。使用UNKNOWN标识无法识别的航空公司避免返回空值导致调用方异常。4. 编写单元测试验证解析效果全面的测试用例是确保解析器健壮性的关键。创建tests/test_parser.pyimport unittest import sys import os # 添加src目录到Python路径 sys.path.append(os.path.join(os.path.dirname(__file__), .., src)) from flight_parser import FlightNumberParser class TestFlightNumberParser(unittest.TestCase): def setUp(self): self.parser FlightNumberParser() def test_chinese_airline_with_space(self): 测试中文航空名称带空格格式 result self.parser.parse(空白航空 1145) self.assertIsNotNone(result) self.assertEqual(result[airline_name], 空白航空) self.assertEqual(result[flight_number], 1145) self.assertEqual(result[airline_code], BK) def test_chinese_airline_no_space(self): 测试中文航空名称无空格格式 result self.parser.parse(空白航空1145) self.assertIsNotNone(result) self.assertEqual(result[flight_number], 1145) def test_iata_code_with_space(self): 测试IATA代码带空格格式 result self.parser.parse(CA 1145) self.assertIsNotNone(result) self.assertEqual(result[airline_code], CA) self.assertEqual(result[flight_number], 1145) def test_iata_code_with_hyphen(self): 测试IATA代码带连字符格式 result self.parser.parse(CA-1145) self.assertIsNotNone(result) self.assertEqual(result[airline_code], CA) def test_iata_code_no_separator(self): 测试IATA代码无分隔符格式 result self.parser.parse(CA1145) self.assertIsNotNone(result) self.assertEqual(result[airline_code], CA) self.assertEqual(result[flight_number], 1145) def test_numeric_only(self): 测试纯数字航班号 result self.parser.parse(1145) self.assertIsNotNone(result) self.assertEqual(result[flight_number], 1145) self.assertEqual(result[airline_code], UNKNOWN) def test_with_extra_spaces(self): 测试前后带多余空格的输入 result self.parser.parse( 空白航空1145 ) self.assertIsNotNone(result) self.assertEqual(result[flight_number], 1145) def test_invalid_formats(self): 测试无效格式 # 航班号过长 self.assertIsNone(self.parser.parse(空白航空12345)) # 包含非法字符 self.assertIsNone(self.parser.parse(空白航空11A5)) # 空输入 self.assertIsNone(self.parser.parse()) # None输入 self.assertIsNone(self.parser.parse(None)) def test_unknown_airline(self): 测试未知航空公司处理 result self.parser.parse(XX1234) self.assertIsNotNone(result) self.assertEqual(result[airline_code], XX) self.assertEqual(result[airline_name], 未知航空公司) if __name__ __main__: unittest.main()运行测试python -m pytest tests/test_parser.py -v预期看到所有测试用例通过确保解析器在各种边界情况下都能正确工作。5. 集成到实际项目中的实践建议航班号解析器开发完成后需要合理集成到业务系统中才能发挥价值。以下是不同场景下的集成方案。5.1 Web API服务集成在RESTful API中处理航班号查询请求from flask import Flask, request, jsonify from src.flight_parser import parse_flight_number app Flask(__name__) app.route(/api/flight/parse, methods[POST]) def parse_flight(): 航班号解析API接口 data request.get_json() flight_input data.get(flight_number, ).upper() # 统一大写处理 result parse_flight_number(flight_input) if result: return jsonify({ success: True, data: result }) else: return jsonify({ success: False, error: 无法解析航班号格式 }), 400 if __name__ __main__: app.run(debugTrue)5.2 数据库查询优化解析后的标准化航班号可以用于高效数据库查询import sqlite3 def query_flight_info(flight_input: str): 根据输入的航班号查询航班信息 parsed parse_flight_number(flight_input) if not parsed: return None conn sqlite3.connect(flights.db) cursor conn.cursor() # 使用解析后的标准格式查询 query SELECT * FROM flights WHERE airline_code ? AND flight_number ? cursor.execute(query, (parsed[airline_code], parsed[flight_number])) result cursor.fetchone() conn.close() return result5.3 批处理数据清洗对于历史数据迁移或批量处理场景def batch_process_flight_data(flight_list: list) - list: 批量处理航班号数据 processed [] parser FlightNumberParser() for raw_flight in flight_list: parsed parser.parse(raw_flight) if parsed: processed.append(parsed) else: # 记录解析失败的数据用于后续处理 processed.append({ raw_input: raw_flight, error: 解析失败, standardized: None }) return processed6. 常见问题排查与调试策略即使有完善的解析逻辑在实际运行中仍可能遇到各种问题。以下是典型的排查路径。6.1 解析失败问题排查当航班号解析返回None或异常结果时按以下顺序检查问题现象可能原因检查方式解决方案返回None无错误日志输入格式不在预设模式中检查输入字符串实际内容添加新的正则表达式模式中文航空名称解析失败编码问题或空格处理异常打印字符串长度和字符编码加强输入清理逻辑航班序号提取错误数字部分匹配不准确测试边界值如1位、4位数字调整正则表达式数字范围航空公司代码映射错误映射表中缺少对应条目检查映射表内容和大小写完善航空公司代码库6.2 日志配置与调试为解析器配置详细的日志记录便于生产环境问题追踪import logging def setup_logging(): 配置日志记录 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(flight_parser.log), logging.StreamHandler() ] ) # 在解析器初始化时调用 setup_logging()6.3 性能优化考虑对于高频调用场景可以考虑以下优化措施from functools import lru_cache class OptimizedFlightParser(FlightNumberParser): 带缓存优化的航班号解析器 lru_cache(maxsize1000) def parse(self, flight_input: str) - Optional[Dict]: 带缓存的解析方法避免重复解析相同输入 if not flight_input or not isinstance(flight_input, str): return None return super().parse(flight_input)7. 生产环境最佳实践将航班号解析器部署到生产环境时需要额外考虑可靠性、可维护性和扩展性。7.1 航空公司代码库管理不要将航空公司代码硬编码在程序中建议使用外部配置import json import os class ConfigurableFlightParser(FlightNumberParser): 支持外部配置的解析器 def __init__(self, config_fileairline_codes.json): super().__init__() self.airline_codes self._load_airline_codes(config_file) def _load_airline_codes(self, config_file: str) - Dict: 从JSON文件加载航空公司代码映射 if os.path.exists(config_file): with open(config_file, r, encodingutf-8) as f: return json.load(f) else: # 退回默认映射 return self.airline_codes创建airline_codes.json配置文件{ CA: 中国国际航空, MU: 中国东方航空, CZ: 中国南方航空, 空白航空: BK, 北京航空: BJ }7.2 错误处理与降级方案确保解析失败时系统能够优雅降级def safe_parse_flight_number(flight_input: str, default_airline: str UNKNOWN) - Dict: 安全的航班号解析确保始终返回有效结果 try: result parse_flight_number(flight_input) if result: return result else: # 降级方案尝试提取纯数字航班号 numbers re.findall(r\d, flight_input) if numbers: return { airline_code: default_airline, flight_number: numbers[0], airline_name: 默认航空公司, full_flight_number: f{default_airline}{numbers[0]} } except Exception as e: logging.error(f航班号解析异常: {e}) # 最终降级方案 return { airline_code: default_airline, flight_number: 0000, airline_name: 解析失败, full_flight_number: f{default_airline}0000 }7.3 监控与告警在生产环境中监控解析器的运行状态class MonitoredFlightParser(FlightNumberParser): 带监控指标的解析器 def __init__(self): super().__init__() self.parse_success 0 self.parse_failure 0 def parse(self, flight_input: str) - Optional[Dict]: result super().parse(flight_input) if result: self.parse_success 1 else: self.parse_failure 1 # 定期打印统计信息实际项目中可推送到监控系统 if (self.parse_success self.parse_failure) % 100 0: self._report_metrics() return result def _report_metrics(self): total self.parse_success self.parse_failure success_rate (self.parse_success / total) * 100 if total 0 else 0 self.logger.info(f解析统计: 成功{self.parse_success}次, 失败{self.parse_failure}次, 成功率{success_rate:.2f}%)7.4 版本兼容与更新策略当航空公司代码或解析规则需要更新时向后兼容确保新版本能够正确处理旧数据格式。渐进式更新先在小范围环境验证再全量部署。数据迁移工具提供历史数据重新解析的工具。版本标记在解析结果中包含解析器版本信息。航班号解析虽然是一个相对独立的技术模块但在航空业务系统中起着承上启下的关键作用。良好的解析器设计应该兼顾准确率、性能和可维护性同时为未来的业务扩展预留空间。实际项目中还需要根据具体的业务需求和数据特点不断调整和优化解析规则。

相关新闻

生物医学博士的5个科研效率工具,最后一个我导师也在用

生物医学博士的5个科研效率工具,最后一个我导师也在用

读博五年,从研一疯狂找文献、熬夜画通路图,到如今平稳推进论文与国自然申报,我试过几十款科研软件,淘汰了大量功能单一、操作繁琐的工具。整理出5款贯穿文献、统计、绘图、翻译、全流程的刚需利器,覆盖生物医学科研全部…

2026/10/16 8:28:11 阅读更多 →
Prompt-费曼学习法

Prompt-费曼学习法

Prompt-费曼学习法浏览网页时看到的,不知道原地址,在此进行标注浏览地址:https://www.scnet.cn/ui/aihub/agent/JumpingBean/FeynmanExplainer🧠 FeynmanLens - 费曼概念拆解机“如果你不能简单地解释它,你就没有真正理…

2026/10/17 4:25:39 阅读更多 →
Simulink数据字典实战:从零构建MBD工程数据管理核心

Simulink数据字典实战:从零构建MBD工程数据管理核心

1. 项目概述:为什么数据字典是模型开发的基石 在Matlab/Simulink的模型开发世界里,尤其是涉及汽车电子、航空航天、工业控制这类对一致性、可追溯性要求极高的领域,我们经常会遇到一个头疼的问题:模型里散落着成百上千个信号、参数…

2026/10/15 7:27:20 阅读更多 →

最新新闻

Docker容器隔离与网络通信原理:从namespace到iptables一网打尽

Docker容器隔离与网络通信原理:从namespace到iptables一网打尽

先说个现象:很多人本机用Docker跑一两个容器,感觉一切都很美好;可一旦项目多起来,要同时起十几个服务,最让人头疼的一定是网络。容器之间互相不通、端口映射诡异失效、跨主机通信直接懵圈,这些问题几乎每个…

2026/10/19 6:00:36 阅读更多 →
从零搭建 artcraft 创意工具:本地优先架构与静态导出实践

从零搭建 artcraft 创意工具:本地优先架构与静态导出实践

1. 从零搭建一个叫 artcraft 的创意工具:我踩过的坑和最终跑通的方案第一次看到 artcraft 这个词,脑子里蹦出来的是“艺术”加“手艺”的组合,直觉告诉我这应该是一个跟创意生产、手工制作或者视觉表达沾边的项目。后来陆续在一些独立开发者的…

2026/10/19 6:00:36 阅读更多 →
diagram-design:从草图到系统,掌握技术图表设计核心方法

diagram-design:从草图到系统,掌握技术图表设计核心方法

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么画流程图、时序图或者架构图。但真正做过技术方案、写过设计文档、给团队做过评审的人都知道,画图这件事从来…

2026/10/19 6:00:36 阅读更多 →
IAR Embedded Workbench for AVR:IDE工具链、C-SPY调试与工程实战指南

IAR Embedded Workbench for AVR:IDE工具链、C-SPY调试与工程实战指南

简介:嵌入式开发中,IDE和调试器的选择直接影响固件开发效率。IAR Embedded Workbench作为支持AVR微控制器的集成开发环境,将编译器、汇编器、连接器与C-SPY调试器统一在同一个工程管理框架内,为8位、16位和32位不同架构芯片提供一…

2026/10/19 6:00:36 阅读更多 →
数学建模国赛A题回焊炉炉温曲线:从牛顿冷却定律到粒子群优化实战

数学建模国赛A题回焊炉炉温曲线:从牛顿冷却定律到粒子群优化实战

简介:面向数学建模竞赛参赛者和指导教师,这份资源为2020年国赛A题论文成品,聚焦集成电路回焊炉温度曲线优化,完整呈现从问题重述、模型假设到求解与检验的全过程。论文以牛顿冷却定律为核心建立温度控制模型,通过最小二…

2026/10/19 6:00:36 阅读更多 →
AI春节档深度观察:拜年玩法、创作工具与行业真相

AI春节档深度观察:拜年玩法、创作工具与行业真相

1. 春节档的主角,从电影院跑到了AI里我这两年有个很直观的感受:一进腊月,朋友圈里的AI产品广告比年货广告还密集。今天这家上线"拜年大模型",明天那家推出"国风写真生成器",后天又有人发布"数…

2026/10/19 5:59:36 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →