ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

机场三字代码表:IATA代码解析、清洗与业务应用指南

机场三字代码表:IATA代码解析、清洗与业务应用指南

简介:这份机场三字代码表面向航空从业者、票务代理、物流调度人员及航空爱好者,用于快速查询IATA三字代码与对应城市、机场名称的映射关系,解决日常工作中代码识别与核对的需求。资源包共1个docx文件,约22KB,以表格形式按代码字母顺序排列,涵盖AAT阿勒泰、PEK北京首都、PVG上海浦东、CAN广州、SZX深圳等大量国内机场条目,便于按代码或城市双向检索。文档同时梳理了三字代码的组成逻辑、分类方式、命名规则与标准化进程,并说明其在机票、行李标签、航班调度和机场管理中的实际应用,读者可借此理解代码背后的编码规律而非单纯记忆。目前已有934人学习下载,适合需要常备速查工具或系统了解三字代码体系的读者参考使用。

1. 机场三字代码表:一份能直接塞进项目的 IATA 代码字典

做航班数据清洗的同行大概都遇到过这种场面:上游给过来一张表,起降地写的是“PEK”“PVG”“CAN”,你要把它翻译成城市名做报表,或者反过来把用户输入的中文城市名映射成三字码去调接口。临时去搜,搜出来的页面要么只列了北上广,要么把 PEK 和 SHA 混着写,要么干脆把 IATA 代码和 ICAO 四字码搅在一起。这份机场三字代码表就是冲着这个痛点来的——它把国内主要机场的中文城市名和三字码成对列出来,从 AAT 阿勒泰到 ZYI 遵义,覆盖了干线机场也覆盖了不少支线机场,格式是纯文本的“代码+城市名”四列并排,复制粘贴就能用。适合谁?做航空物流、OTA 订单、差旅系统、航班动态看板的后端和数据分析同学,以及需要给测试库造机场维表的开发。它不解决航司二字码、也不解决 ICAO 四字码,边界先说清楚,后面才不会翻车。

2. 把纯文本表变成可用维表:解析、清洗与字段设计

拿到手的原料是一段四列并排的文本,每行其实是“代码 城市名 代码 城市名 代码 城市名 代码 城市名”的重复结构。直接按空格 split 会得到一个长度不固定的列表,因为城市名里带“/”的(比如“长沙/黄花”)、带空格的(比如“西安 ”后面跟了全角空格)都会把切分搞乱。所以第一步不是写业务代码,而是先把它规整成一张两列的表:iata_code和city_name。

2.1 先看清原料的结构再动手

原料里有两个细节值得单独拎出来。第一,城市名存在别名写法,比如“长沙/黄花”和“长沙”同时出现(CSX 和 HHA),前者是机场名带城市,后者是城市名本身;第二,存在全角空格和制表符混排,像“SIA西安 ”这种尾部带全角空格的,肉眼看不出来,程序一跑就多出一个空字段。常见做法是先把全角空格、制表符、连续空格统一替换成单个半角空格,再做切分。另一个容易忽略的点是:这份表里既有“北京首都机场”这种带“机场”后缀的,也有“上海浦东”这种不带的,字段设计上建议把“城市名”和“机场名”分开存,别指望一个字段同时承担两种语义。

2.2 解析脚本:从一段文本到两列 CSV

下面这段 Python 是我一般会先跑的清洗脚本,逻辑不复杂,但把上面说的坑都堵上了。它假设原始文本已经存成raw.txt,每行是四组“代码+城市名”的重复。

import re import csv def normalize_line(line: str) -> str: # 全角空格、制表符统一成半角空格,压缩连续空格 line = line.replace('\u3000', ' ').replace('\t', ' ') line = re.sub(r' +', ' ', line).strip() return line def parse_raw(path: str): rows = [] with open(path, encoding='utf-8') as f: for lineno, line in enumerate(f, 1): line = normalize_line(line) if not line: continue tokens = line.split(' ') # 每行应为 8 个 token:4 组 (code, city) if len(tokens) % 2 != 0: print(f'[skip] line {lineno} token 数为奇数: {tokens}') continue for i in range(0, len(tokens), 2): code, city = tokens[i], tokens[i + 1] # 三字码必须是 3 位大写字母,否则视为脏数据 if not re.fullmatch(r'[A-Z]{3}', code): print(f'[skip] line {lineno} 非法代码: {code}') continue rows.append({'iata_code': code, 'city_name': city}) return rows if __name__ == '__main__': data = parse_raw('raw.txt') # 去重:同一代码只保留第一条 seen, dedup = set(), [] for r in data: if r['iata_code'] in seen: continue seen.add(r['iata_code']) dedup.append(r) with open('airports.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.DictWriter(f, fieldnames=['iata_code', 'city_name']) writer.writeheader() writer.writerows(dedup) print(f'解析完成,共 {len(dedup)} 条')

这段脚本的关键点有三个。normalize_line负责把全角空格和连续空格压平,这是后面能稳定按单空格切分的前提;len(tokens) % 2 != 0的判断是防御性的,原料里只要有一行少了个城市名,整行的分组就会错位,宁可跳过并打日志,也不要让脏数据静默进库;re.fullmatch(r'[A-Z]{3}', code)用来兜底,防止把城市名误当成代码。参数上唯一需要你改的是输入文件名,输出固定为airports.csv,字段名按你下游表的习惯改也行,但建议保留iata_code这个命名,后面做 join 时语义清楚。

2.3 字段设计与索引:别让三字码变成字符串黑洞

解析出来的两列只是起点。真要用起来,我建议在维表里再加三列:country_code(固定填 CN,方便以后扩国际表)、city_pinyin(用拼音库生成,做模糊搜索)、is_active(标记是否在用,支线机场有停航的)。索引方面,iata_code建唯一索引是必须的,因为它是业务主键;city_name建普通索引,因为用户输入中文城市名反查代码是高频操作。这里有个血泪经验:别在city_name上做LIKE '%xx%'的前导通配查询,数据量小的时候没感觉,一旦并发上来就是全表扫,正确做法是单独维护一张拼音首字母映射表,或者用全文索引。字段类型上,三字码用CHAR(3)而不是VARCHAR,定长在存储和比较上都更省。

3. 三字码在业务里的三种用法:映射、校验与模糊匹配

表建好了,接下来是它到底能干什么。这一章按使用频率从高到低排:城市名到代码的正向映射、代码到城市名的反向翻译、以及用户输入不规范时的模糊匹配。三种用法对应三种不同的实现策略,混着用就会出问题。

3.1 正向映射:中文城市名转三字码

正向映射的坑在于“一名多码”。原料里“长沙”对应 CSX,“长沙/黄花”对应 HHA,如果你只按城市名精确匹配,用户输入“长沙”可能命中两条。常见做法是维护一张别名表,把“长沙/黄花”拆成主名“长沙”和别名“黄花”,查询时先匹配主名,再匹配别名,最后按is_active过滤。下面是一个简化的查询逻辑,用 SQL 表达更直观:

-- 假设有 airports(iata_code, city_name, alias_name, is_active) SELECT iata_code FROM airports WHERE is_active = 1 AND (city_name = :input OR alias_name = :input) ORDER BY (city_name = :input) DESC -- 主名命中优先 LIMIT 1;

ORDER BY里那个布尔表达式是让主名精确命中的记录排在别名前面,避免“长沙”被解析成 HHA。参数:input就是用户传进来的城市名,记得在应用层先做去空格和全半角归一,否则“上海 ”和“上海”会被当成两个值。如果业务允许一个城市多机场(比如上海有 SHA 和 PVG),那就别LIMIT 1,改成返回列表让上层选,或者按“是否国际航班”这类业务字段再筛。

3.2 反向翻译:代码转城市名与展示层处理

反向翻译看起来最简单,WHERE iata_code = 'PEK'就完事,但展示层有个细节:原料里“北京首都机场”和“北京”是两条不同记录(PEK 和 BJS),前者是具体机场,后者是城市代码。做航班列表时,起降地通常要展示“北京首都”,而做城市维度统计时又要归到“北京”。我的处理方式是在维表里加一列metro_code,把 PEK 的metro_code填成 BJS,这样统计时按metro_code聚合,展示时按iata_code取机场名,两套口径互不干扰。这一步不做,后面做城市吞吐量报表时就会把 PEK 和 BJS 当成两个城市,数字直接翻倍,属于典型的黑匣子问题。

3.3 模糊匹配:用户输入“bei jing”也能命中

面向 C 端的搜索框不会老老实实输入标准城市名,拼音、首字母、错别字都可能有。我的做法是在维表里预生成三列:city_pinyin(全拼)、city_initials(首字母)、city_name,查询时按优先级依次匹配。下面这段是匹配函数的骨架:

def match_airport(user_input: str, airports: list) -> list: q = user_input.strip().lower().replace(' ', '') # 优先级:三字码 > 城市名 > 全拼 > 首字母 for key in ('iata_code', 'city_name', 'city_pinyin', 'city_initials'): hits = [a for a in airports if a.get(key, '').lower() == q] if hits: return hits # 兜底:前缀匹配,限制返回条数 return [a for a in airports if a['city_pinyin'].startswith(q)][:10]

这里q做了去空格和小写归一,因为用户可能输入“PEK”也可能输入“pek”。四级优先级的顺序是有讲究的:三字码精确命中直接返回,避免“CAN”被拼音匹配成别的;城市名其次;拼音和首字母放最后,因为它们误命中率最高。兜底的前缀匹配一定要加[:10]限制,否则用户输入一个“a”能把半张表捞出来,接口直接超时。这套逻辑在数据量几千条时纯内存跑完全够,上了十万级再考虑倒排索引。

4. 避坑与排查:三字码落地时最容易翻车的五件事

这一章是我踩过的坑里挑出来最值得说的五条,每条按“现象、原因、解决”写,照着排查能省不少时间。

4.1 现象:统计报表里北京出现两次,数字对不上

原因:把 PEK(首都机场)和 BJS(北京城市代码)当成两个独立城市做了 group by,原料里这两条都在,直接聚合就会重复计数。解决:维表加metro_code列做归并,统计口径统一走metro_code,展示口径走iata_code,两套字段分开维护,别在一个字段上既要又要。

4.2 现象:解析脚本跑完条数比预期少了一截

原因:原料里存在全角空格和制表符,split(' ')切出来的 token 数不是偶数,被% 2 != 0的判断整行跳过了。解决:切分前先做replace('\u3000', ' ')和re.sub(r' +', ' ', line),把空白字符归一。排查时把跳过的行号打出来,对着原文件看几行就能定位。

4.3 现象:用户搜“上海”返回了浦东,但业务要的是虹桥

原因:SHA 和 PVG 都归在上海名下,精确匹配时按数据库默认顺序返回,结果不稳定。解决:查询不要LIMIT 1,返回列表由上层按业务规则选;或者在维表里加is_primary标记主机场,排序时优先。这个坑在差旅系统里特别常见,默认机场选错,用户订到浦东去赶虹桥的飞机,投诉就来了。

4.4 现象:三字码字段存进了小写,join 时匹配不上

原因:上游接口返回的是小写pek,维表里存的是大写PEK,MySQL 默认排序规则下大小写不敏感还能过,换到 PostgreSQL 或做内存 join 就直接 miss。解决:入库前统一upper(),字段用CHAR(3)并在应用层做一次归一,别指望数据库排序规则兜底,跨库迁移时这就是定时炸弹。

4.5 现象:模糊搜索输入单个字母,接口响应从 50ms 涨到 3s

原因:前缀匹配没有限制返回条数,startswith在几千条数据上做全量过滤,再叠加没加索引的LIKE,直接拖垮。解决:兜底分支强制[:10]截断,拼音列建索引,高频查询走缓存。另外提醒一句,别在city_name上做LIKE '%x%',前导通配用不上索引,这是很多人后悔药都来不及吃的地方。

5. 进阶:把静态表变成可校验的数据资产

静态代码表最大的问题是会过期——机场新建、代码调整、支线停航,原料不会自己更新。我的习惯是给这份表加一层校验:每次导入前跑一遍完整性检查,把“代码格式不对”“城市名为空”“同一代码重复”三类问题拦在入库前。下面这段校验脚本可以直接挂到 CI 里,跑不通就不让合并:

import re def validate(rows: list) -> list: errors = [] seen = {} for idx, r in enumerate(rows): code, city = r.get('iata_code', ''), r.get('city_name', '').strip() if not re.fullmatch(r'[A-Z]{3}', code): errors.append(f'row {idx}: 非法三字码 {code!r}') if not city: errors.append(f'row {idx}: 城市名为空,代码 {code}') if code in seen: errors.append(f'row {idx}: 代码 {code} 与 row {seen[code]} 重复') else: seen[code] = idx return errors

validate返回的是错误列表而不是布尔值,这样 CI 日志里能直接看到哪一行出了什么问题,比一句“校验失败”有用得多。三个检查项对应三类真实故障:格式错多半是解析切分错位,城市名为空多半是全角空格没清干净,重复代码多半是原料里同一机场出现了两次。跑通之后,我一般还会加一条“与上一版本 diff”的检查,新增和删除的代码单独列出来人工过一眼,因为机场代码的增删往往意味着业务范围变化,值得多看一眼。

再往上一层,如果你要把这份表对外提供查询服务,建议在它之上封一个薄薄的服务层,把“精确查代码”“城市名反查”“拼音模糊搜”三个接口分开,别做成一个万能接口靠参数切换。分开之后每个接口的缓存策略、限流阈值、返回结构都能独立调,出问题也好定位。我见过把三种查询塞进一个接口的实现,参数一多文档就写不清,最后调用方全靠猜,维护成本比多写两个接口高得多。

从那以后我每次拿到这类静态代码表,都强制先跑一遍解析加校验再入库,宁可多花十分钟,也不让脏数据进生产库——毕竟三字码错了,下游的航班、订单、报表会一路错到底,排查起来就是一场灾难。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表