- 后端
【免费下载链接】libphonenumber
Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers.
libphonenumber 的metadata目录是一套面向CSV 打包格式元数据的辅助 Java 库,用于读取、校验与操作 libphonenumber 各客户端库(Java/C++/JavaScript)背后的事实来源数据。本文以 metadata/README.md 为主体,结合 metadata.zip 中的真实数据与 metadata/src/main/java 源码,完整讲解该库的定位、CSV 表族结构、构建方式、类型/证据模型,以及从 CSV 表生成NumberingScheme与正则表达式的底层原理,帮助读者理解 libphonenumber 元数据从"结构化表格"走向"客户端可消费格式"的必经之路。
一、目录定位:为"读取与操纵 CSV 元数据"而生的辅助库
README 明确给出了这个目录的角色:它包含辅助库(auxiliary libraries),专门支撑对 libphonenumber 客户端库使用的CSV 打包元数据的读取与操纵。
几个关键事实需要先厘清:
- 首个发布版本的边界:这一版库"纯粹关注 CSV 文件的处理",尚未包含将 CSV 数据转换为 libphonenumber 所用的 XML 及其他文本文件的类。也就是说,当前仓库里你能直接使用的能力是"吃进 CSV、校验并操作";生成 XML 的完整工具链是文档中描述的未来计划。
- 演进方向:README 预期,将来"操纵 CSV 元数据并生成 XML 文件"的全部工具都会发布在这里;到那时,CSV 文件将成为 libphonenumber 的事实来源(source of truth),而基于 XML 的元数据及其他映射文件(carrier/geocode/timezone 等)将自动从 CSV 派生。当前
metadata/src/main/java/com/google/i18n/phonenumbers/metadata/model/NumberingScheme.java的类注释印证了这一点——它描述为"单一国家区号下所有电话号码元数据的抽象",且"期望 CSV 表与其他主数据源在业务逻辑的单一点上构建 numbering schemes"。 - 支持与 API 稳定性声明:README 用加粗强调——这些库当前不受官方支持,不提供稳定 API,使用风险自负;API 虽不会剧烈变化,但调整与 bug 修复不可避免。
- 问题与贡献渠道:该代码库不接受直接提交的补丁/Pull Request;发现问题请开 issue;当前阶段不接受功能请求,也不提供本目录相关的答疑或技术支持。
从源码目录看,这套库的组织非常清晰,metadata/src/main/java/com/google/i18n/phonenumbers/metadata 下按职责分为五个子包:
| 子包 | 职责 | 代表类 |
|---|---|---|
table | CSV 表格基础设施:解析、schema、行列模型、RangeTable | CsvParser.java、CsvSchema.java、CsvTable.java、RangeTable.java |
model | 各类 CSV 表的 schema 定义与领域模型 | RangesTableSchema.java、MetadataTableSchema.java、NumberingScheme.java |
regex | 从 RangeTree 生成(部分优化的)正则表达式 | RegexGenerator.java、NfaFlattener.java |
finitestatematcher | 将号码范围编译为有限状态匹配器 | DigitSequenceMatcher.java、MatcherCompiler.java |
i18n | 区域码与语言标签的值类型 | PhoneRegion.java、SimpleLanguageTag.java |
根包下还直接放了一批核心值类型:DigitSequence.java、RangeSpecification.java、RangeTree.java、RangeTreeFactorizer.java、PrefixTree.java、LengthsParser.java、MetadataKey.java 等——它们是号码范围建模的最小积木。
二、metadata.zip:规范元数据的载体与目录结构
README 对 metadata.zip 的定义是:它包含 libphonenumber 项目的规范元数据(canonical metadata),供 libphonenumber 工具使用;CSV 的 schema 不被承诺保持稳定。
用 zip 工具解开该包可以看到(本仓库共 1531 个文件),其布局与 FileBasedCsvLoader.java 的读取逻辑完全对应:
metadata/ ├── metadata.csv # 顶层元数据表,一行一个国际区号 ├── 1/ # 按国际区号命名的目录(如 1、55、65、961…) │ ├── ranges.csv # 号码范围表 │ ├── shortcodes.csv # 短号码表 │ ├── examples.csv # 示例号码表 │ ├── formats.csv # 格式表 │ ├── altformats.csv # 备用格式表(可选) │ ├── operators.csv # 运营商表(可选) │ └── comments.csv # 注释表(可选) └── ...FileBasedCsvLoader正是按这套约定实现的:构造时读取根目录下的metadata.csv(MetadataTableSchema.SCHEMA.load(root.resolve("metadata.csv"))),loadData(cc)时再进入root.resolve(cc.toString())目录,依次加载ranges、shortcodes、examples、formats、altformats、operators、comments七个*.csv文件。值得注意的是:没有对应文件的表会被静默视为空表(CsvSchema.load 在Files.exists(file)为 false 时直接返回空表)。
顶层 metadata.csv 的真实样例
从metadata.zip中解出的metadata.csv,表头与数据行如下(分号分隔,值可带引号):
Calling Code ; Main Region ; Extra Regions ; National Prefix ; IDD Prefix ; Timezone ; Mobile Portable Regions ; Extension Prefix 1 ; "US" ; "AG,AI,AS,BB,BM,BS,CA,DM,DO,GD,GU,JM,KN,KY,LC,MP,MS,PR,SX,TC,TT,VC,VG,VI" ; "1" ; "011" ; ... ; "AG,AI,BB,..." 20 ; "EG" ; ; "0" ; "00" ; "Africa/Cairo" ; "EG" 211 ; "SS" ; ; "0" ; "00" ; "Africa/Nairobi"这一行的语义由 MetadataTableSchema.java 定义,其中Calling Code是行键列,其余为非键列:
| 列名 | 含义 | 说明 |
|---|---|---|
Calling Code | 国际区号(行键) | 如1(NANPA)、20(埃及) |
Main Region | 区号对应的主区域 | 如 NANPA 的主区域是US |
Extra Regions | 共享该区号的其他区域(逗号分隔) | 如1号段下还覆盖AG,AI,AS,...等加勒比/太平洋区域 |
National Prefix | 拨打国内号码时的前缀 | 可多个,第一个为 preferred;US为1,多数国家为0 |
IDD Prefix | 默认国际直拨前缀 | 可含单个~表示拨号停顿(如俄罗斯的8~10),该符号仅在生成 XML 的preferredInternationalPrefix时保留 |
Timezone | 默认时区(可多个,&分隔) | 如Africa/Cairo |
Mobile Portable Regions | 移动号码可在运营商间携号转网的区域列表 | 逗号分隔 |
Extension Prefix | 分机号首选前缀 | 如ext |
ranges.csv 的真实样例
美国区号目录1/ranges.csv的头部与几行真实数据:
Prefix ; Length ; Type ; Tariff ; Area Code Length ; Operator ; Format ; Timezone ; Regions ; Geocode:en ; Provenance ; Comment 201200 ; 10 ; FIXED_LINE_OR_MOBILE ; STANDARD_RATE ; 3 ; ; "fmt_3/3/4" ; "America/New_York" ; "US" ; "Jersey City, NJ" 20120[1-9] ; 10 ; FIXED_LINE_OR_MOBILE ; STANDARD_RATE ; 3 ; ; "fmt_3/3/4" ; "America/New_York" ; "US" ; "New Jersey"可以看出:行键由Prefix(可用[1-9]这种范围说明语法)与Length(支持8,9、5,7-9等长度集合表达)两列构成;而Geocode:en这类以语言标签命名的列是列组(ColumnGroup)——详见下文第三节。
三、CSV 表族:七个表格的 schema 全解
CsvData(CsvData.java)把"单一国际区号下的所有 CSV 表 + 遗留 XML"聚合为一个对象,注释明确指出:"这是能重建全部遗留数据(metadata XML、carrier/geocode/timezone 映射)的数据来源"。它一次加载全部表,因为转换到遗留格式往往需要多个数据结构协同。其create()静态工厂会执行三类一致性校验:
- 区号必须存在于顶层元数据表中;
- 区域一致性:ranges 表与 shortcodes 表声明的区域必须与 metadata 表的
Main Region/Extra Regions对齐(checkRegions); - 行不得重叠:ranges 表行之间、shortcodes 表行之间(按区域分别计算)的号码范围两两不能相交(
checkNoOverlappingRows)。
下面逐表介绍 schema(全部定义于 metadata/src/main/java/com/google/i18n/phonenumbers/metadata/model)。
1. Ranges 表(号码范围表)
RangesTableSchema.java 定义行键列Prefix+Length,以及一组丰富的非键列。其中两个枚举很值得注意:
- ExtType(外部号码类型):
UNKNOWN、FIXED_LINE、MOBILE、FIXED_LINE_OR_MOBILE、VOIP、PAGER、PERSONAL_NUMBER、UAN、VOICEMAIL,以及两个"未来预留"类型M2M(机器对机器)与ISP(拨号上网)。注释说明这个外部类型"从技术上比 ValidNumberType 更好,因为它把类型与资费正确拆开",但 phonenumber 库内部逻辑无法直接消化,因此最终仍要通过XmlRangesSchema映射回旧的ValidNumberType。 - ExtTariff(外部资费):
STANDARD_RATE、TOLL_FREE、SHARED_COST、PREMIUM_RATE。将 ExtType 与 ExtTariff 组合后,可映射回ValidNumberType(如TOLL_FREE资费 →TOLL_FREE,STANDARD_RATE不改变类型映射)。
Ranges 表完整列清单(含默认值):
| 列 | 类型/取值 | 说明 |
|---|---|---|
Type | ExtType,默认UNKNOWN | 号码范围的语义类型,所有行都应赋值 |
Tariff | ExtTariff,默认STANDARD_RATE | 期望资费 |
Area Code Length | 无符号整数 | 本地拨号时可移除的前缀长度;若区号非可选则不填 |
National Only | 布尔 | 不能从区外拨入,派生noInternationalDialling范围 |
Sms | 布尔 | 是否预期支持 SMS |
Operator | 字符串 | 期望运营商(carrier)ID,未知可为空 |
Format | 字符串 | 期望格式 ID,无需格式化可为空 |
Timezone | 时区列表,&分隔 | 空则隐含默认时区 |
Regions(CSV 列) | 区域列表,逗号分隔 | 导入内部表时被"规范化"为一组布尔列Region:XX |
Geocode:XXX(列组) | 字符串 | 按语言代码组织的 geocode 文本 |
Provenance | Provenance 枚举 | 该范围为何有效的最重要依据 |
Comment | 自由文本 | 通常存放与 Provenance 对应的证据链接 |
注意Regions列在 CSV 形态与内部RangeTable形态之间有一个有趣的"胖瘦转换":RangesTableSchema.toCsv 把一组布尔列Region:XX合并成单个逗号分隔的多值列(便于在电子表格中查看),toRangeTable 则反向展开成布尔列组(便于程序处理)。
2. Shortcodes 表(短号码表)
ShortcodesTableSchema.java 的行键是Region+Prefix+Length三列——注释解释了原因:区域必须进入行键,因为同一短号码在不同区域可能类型不同(NANPA 尤其如此,大量区域只有极少量短号码,合并到单表最省事)。非键列:Type(必须赋值)、Tariff(必须赋值)、Sms、Carrier Specific(是否仅限某运营商,源码注释为 Subregion/指定运营商的语义)、Provenance、Comment。
真实数据(1/shortcodes.csv)示例:
Region ; Prefix ; Length ; Type ; Tariff ; Sms ; Carrier Specific AG ; 911 ; 3 ; EMERGENCY ; TOLL_FREE AG ; 988 ; 3 ; EXPANDED_EMERGENCY ; TOLL_FREE BB ; [2359]11 ; 3 ; EMERGENCY ; TOLL_FREE AS ; 40404 ; 5 ; COMMERCIAL ; ; true3. Examples 表(示例号码表)
ExamplesTableSchema.java 行键为Region+Type(ValidNumberType),非键列Number(国内号码)与Comment(选择该示例的依据)。真实数据(1/examples.csv):
Region ; Type ; Number AG ; FIXED_LINE ; "2684601234" AG ; MOBILE ; "2684641234" AG ; TOLL_FREE ; "8002123456" AI ; FIXED_LINE ; "2644612345"4. Formats 表(格式表)
FormatsTableSchema.java 行键为Id,非键列:
| 列 | 约束 |
|---|---|
National | 必填;可含#表示国内前缀占位 |
Carrier | 可选;可含#与@(运营商说明符),后缀必须与 National 兼容 |
International | 不得含#或@ |
Local | 不得含#或@,若有 Area Code Length 则必须与之对应 |
National Prefix Optional | 布尔 |
Comment | 自由文本 |
真实数据(1/formats.csv):
Id ; National ; International ; Local ; National Prefix Optional ; Comment fmt_3/3/4 ; "(XXX) XXX-XXXX" ; "XXX-XXX-XXXX" ; "XXX-XXXX" ; true ; "A different pattern is used when formatting internationally..." fmt_3/4 ; "XXX-XXXX" ; "XXX-XXXX" ; ; true ; "310-xxxx (7 digit) UAN numbers ."5. Operators 表(运营商表)
OperatorsTableSchema.java 行键为Id,非键列Domestic Selection Codes(国内拨号选择码,逗号分隔的范围说明)、IDD Prefixes(国际直拨码)、Names:XX(按语言的分组名称列)。两个使用约定值得注意:
- 默认 IDD 前缀不放在本表,而是放在顶层 metadata 表的
IDD Prefix列; - 若某个选择码/IDD 码不归属任何有号码范围的运营商(如通用可用码),运营商 ID 必须以
__(双下划线)开头,以绕过"未赋值运营商"的一致性检查。
6. AltFormats 与 Comments
AltFormatsSchema.java 定义备用格式表,行由"备用格式说明符"标识,含PARENT(所对应的主格式 ID)等列;CommentsSchema负责装载注释。二者在FileBasedCsvLoader中分别经loadAltFormats、loadComments读取,且当前CsvData.diff的 TODO 注释表明:diff 比较暂未覆盖 comments 与 altformats。
四、类型系统与证据体系:两个 proto 文件
metadata模块把枚举类型放在 proto 文件中,构建时由 protoc 生成 Java 类。
enums.proto:Provenance(证据来源)
enums.proto 定义Provenance枚举,注释强调其不稳定,且只能存储于基于文本的 protocol buffer 中。取值按可信度递增排列:
| 值 | 数值 | 含义 |
|---|---|---|
UNKNOWN | 0 | proto3 的默认值,真实数据不应出现 |
ITU | 10 | 官方 ITU 文档中定义的范围,注释应含文档链接,最可信 |
IR21 | 20 | 官方 IR21 文档中定义的范围,注释应含文档链接 |
GOVERNMENT | 30 | 官方/政府背书实体网站(如国家电信运营商)中的证据,注释含 URL |
TELECOMS | 40 | 电信运营商网站(移动运营商、MVNO 等)中的证据,注释含 URL |
WEB | 50 | 非官方网站(如 Facebook 或公司主页)中的证据,注释含 URL |
INTERNAL | 100 | 无法引用外部证据的特殊接受情形;注释应说明 bug 报告或内部理由,只在极特殊情况下使用,且注释可能在对外发布时被清除 |
这一枚举直接对应 ranges/shortcodes 表中的Provenance列,是"每个号码范围为什么有效"的审计线索。
types.proto:号码类型三枚举
types.proto 定义了三个枚举:
- XmlNumberType:
XML_UNKNOWN、XML_NO_INTERNATIONAL_DIALLING、XML_FIXED_LINE、XML_MOBILE、XML_PAGER、XML_TOLL_FREE、XML_PREMIUM_RATE、XML_SHARED_COST、XML_PERSONAL_NUMBER、XML_VOIP、XML_UAN、XML_VOICEMAIL。注释要求:枚举名必须与 XML 元数据中的元素名(忽略大小写)一致——这保证了将来 CSV→XML 生成时名称可直接对上。 - ValidNumberType:每个有效号码范围被归类为恰好一种类型;不含
NO_INTERNATIONAL_DIALLING(它是范围的属性而非基本类型)。取值与 XmlNumberType 一一对应。 - XmlShortcodeType:
SC_SHORT_CODE、资费互斥子集SC_TOLL_FREE/SC_STANDARD_RATE/SC_PREMIUM_RATE、以及用途类SC_CARRIER_SPECIFIC/SC_EMERGENCY/SC_EXPANDED_EMERGENCY/SC_SMS_SERVICES。与主元数据不同,短号码类型不要求互斥。
五、构建方式:Maven、protoc 与 AutoValue
metadata/pom.xml 揭示了模块的技术栈:
- Java 11编译目标;
maven-compiler-plugin3.8.1 配置了两个 execution:process-annotations在generate-sources阶段以-proc:only运行注解处理器(AutoValue),default-compile在compile阶段以-proc:none编译(避免重复处理)。 - protoc-jar-maven-plugin3.11.4(内嵌 protoc 3.1.0)在
generate-sources阶段扫描src/main/proto生成 proto Java 类并加入源码目录。 - 依赖清单(均为编译期或测试期依赖):
| 依赖 | 版本 | 用途 |
|---|---|---|
| guava | 32.1.2-jre | 集合、不可变结构、CharMatcher 等基础设施 |
| icu4j | 73.2 | Unicode/语言标签处理(SimpleLanguageTag等) |
| protobuf-java | 3.24.0 | proto3 运行时 |
| auto-value / auto-value-annotations | 1.10.2 | 不可变值类型的样板代码生成 |
| protoc-jar-maven-plugin | 3.11.4 | 构建期生成 proto 类 |
| jsr305 | 3.0.2 | @Nullable等注解 |
| truth / truth-java8-extension | 1.1.5 / 1.0.1(test) | 测试断言 |
测试资源位于 metadata/src/test/java/com/google/i18n/phonenumbers/metadata,覆盖CsvParserTest、CsvTableTest、RangeTableTest、RegexGeneratorTest、MatcherCompilerTest、DigitSequenceMatcherTest等,另有regression_test_data.textpb用于编译器回归测试。
六、核心处理链路:从 CSV 到 NumberingScheme 与正则
1. CSV ↔ RangeTable 的双向转换
Ranges 表在内存中以RangeTable形式工作:RangesTableSchema.toRangeTable把 CSV 行还原成带类型化列的范围表,toCsv反向导出。这一转换在CsvData.getRangesAsTable()(标注@Memoized,只算一次)中被封装,canonicalizeRangeTables()则通过"转表再转回 CSV"来规范化范围表(注释提醒:大区域可能较慢)。
2. NumberingScheme:单一区号的知识抽象
NumberingScheme.java 是"单一国家区号已知的所有电话号码元数据"的抽象。它的 Javadoc 特别说明:不存在 NumberingScheme 的 builder——期望在业务逻辑单一点用 CSV 表等主数据源直接构建;测试中可用TestNumberingScheme。而 XmlRangesSchema.java 定义了生成 NumberingScheme 所需的精简列集:Type、Area Code Length、National Only、Region:XX布尔列组——它没有配套的CsvKeyMarshaller,因为它不是数据导入格式,而是内部转换目标。
3. RegexGenerator:从 RangeTree 到正则
正则生成是全链路中最精妙的部分。RegexGenerator.java 从RangeTree产出部分优化的正则表达式,核心 API:
basic():不启用任何可选优化,结构更简单但输出通常更长;defaultXmlGenerator():即BASIC.withDfaFactorization().withSubgroupOptimization(),注释明确这是生成与遗留 XML 数据相同正则的默认生成器,任何工具想获得与遗留 XML 一致的正则都应使用它;- 另有
withDotMatch(用.匹配任意数字)等开关。
生成过程依赖 RangeTreeFactorizer.java 的合并策略(ALLOW_EDGE_SPLITTING/REQUIRE_EQUAL_EDGES)与 NfaFlattener.java 的 NFA 展平。注意一个工程细节:尾部优化(tail optimization)被有意禁用,源码注释说它似乎抵消了子组优化(subgroup optimization)的收益。
4. 有限状态匹配器
finitestatematcher子包提供了与正则等价但更高效的匹配方案:MatcherCompiler.java 将范围编译为字节码形式的匹配器(涉及OpCode.java、Operation.java、Statistics.java),运行期由 DigitSequenceMatcher.java 执行;CompilerRegressionTest配合regression_test_data.textpb保证编译器输出的稳定性。
七、CSV 基础设施:解析器与 schema 机制
table 子包是整条工具链的地基,理解它才能读懂所有 schema:
- CsvParser.java:一个高效、fluent 风格的流式 CSV 解析器。特点包括:完整支持引号转义与多行引号值(
allowMultiline())、可选的空白修剪(trimWhitespace())、基于头部行的列映射(RowMapper.mapTo,且校验表头不能有重复列名)、逗号/制表符分隔(commaSeparated()/tabSeparated())。源码注释直言"这个类之所以必要,是因为 Guava 的 CSV 实现不支持忽略空白"。 - CsvSchema.java:schema = 键的 marshaller + 非键列集合。
parseHeader校验前几列是否与键列完全一致,parseRow把一行拆成键(CsvKeyMarshaller.deserialize)与列赋值列表;load(Path)对不存在的文件返回空表。 - CsvTable / RangeTable / CsvKeyMarshaller:
CsvTable提供导入导出与 diff 能力(CsvData.Diff利用CsvTable.diff(..., DiffMode.CHANGES)输出新增/修改/删除的上下文化差异);RangeTable用不可变RangeTree组织范围数据并支持OverwriteMode覆盖语义。
MetadataException(MetadataException.java)是贯穿全部校验的异常类型,checkMetadata的 Javadoc 强调:MetadataException 只应对应"可通过修改 CSV 数据修复"的问题——这正是"CSV 将作为事实来源"这一设计意图的直接体现:所有错误都应当能在源头数据层被修正。
八、结语:当前边界与使用建议
综合 README 与源码,这套元数据处理库的当前边界可以精确概括为:
- 能做:读取 CSV 元数据(
FileBasedCsvLoader)、按 schema 解析与校验(CsvParser/CsvSchema/MetadataException)、聚合单一区号数据(CsvData)、比较快照差异(CsvData.diff)、构建NumberingScheme、生成正则与有限状态匹配器; - 尚未做:把 CSV 转换为 libphonenumber 客户端使用的 XML 元数据及其他映射文件的完整工具(README 明示,属于未来计划);
- 使用注意:库不受官方支持、API 不稳定、不接受功能请求——把它当作"能读、能验、能操作"的实验性工具链,而非有兼容性承诺的公共 API;schema 本身也可能演进。
对于想深入理解 libphonenumber 元数据结构的读者,推荐的阅读路径是:先看 metadata/README.md 把握定位,再解开 metadata.zip 对照 RangesTableSchema.java 与 MetadataTableSchema.java 理解列语义,最后沿着FileBasedCsvLoader → CsvData → NumberingScheme → RegexGenerator这条链路,即可完整还原"从 CSV 表格到可消费元数据"的转换管线。
- 后端
【免费下载链接】libphonenumber
Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers.
相关推荐
libphonenumber 元数据工具链(Metadata Tools)指南:CSV 驱动的号码规划数据模型与 metadata.zip
libphonenumber 元数据工具链(Metadata Tools)指南:CSV 驱动的号码规划数据模型与 metadata.zip 导读 libphon
后端移动开发北京昇腾GPT-2安全与偏见分析:如何规避AI生成内容风险
北京昇腾GPT 2安全与偏见分析:如何规避AI生成内容风险 北京昇腾GPT 2作为一款先进的语言生成模型,在自然语言处理领域展现了强大的能力。然而,与所有大型语
从混乱到精准:libphonenumber元数据工具链全攻略
从混乱到精准:libphonenumber元数据工具链全攻略 libphonenumber是Google开发的一套强大的国际电话号码处理库,支持Java、C++
后端移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考