拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

libphonenumber 元数据处理库解析:CSV 元数据规范、metadata.zip 数据包与从 CSV 到 NumberingScheme 的工具链

libphonenumber 元数据处理库解析:CSV 元数据规范、metadata.zip 数据包与从 CSV 到 NumberingScheme 的工具链
  • 后端

【免费下载链接】libphonenumber

Google's common Java, C++ and JavaScript library for parsing, formatting, and validating international phone numbers.

项目地址:https://gitcode.com/gh_mirrors/libp/libphonenumber
点击查看免费下载

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 下按职责分为五个子包:

子包职责代表类
tableCSV 表格基础设施:解析、schema、行列模型、RangeTableCsvParser.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()静态工厂会执行三类一致性校验:

  1. 区号必须存在于顶层元数据表中;
  2. 区域一致性:ranges 表与 shortcodes 表声明的区域必须与 metadata 表的Main Region/Extra Regions对齐(checkRegions);
  3. 行不得重叠: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 表完整列清单(含默认值):

列类型/取值说明
TypeExtType,默认UNKNOWN号码范围的语义类型,所有行都应赋值
TariffExtTariff,默认STANDARD_RATE期望资费
Area Code Length无符号整数本地拨号时可移除的前缀长度;若区号非可选则不填
National Only布尔不能从区外拨入,派生noInternationalDialling范围
Sms布尔是否预期支持 SMS
Operator字符串期望运营商(carrier)ID,未知可为空
Format字符串期望格式 ID,无需格式化可为空
Timezone时区列表,&分隔空则隐含默认时区
Regions(CSV 列)区域列表,逗号分隔导入内部表时被"规范化"为一组布尔列Region:XX
Geocode:XXX(列组)字符串按语言代码组织的 geocode 文本
ProvenanceProvenance 枚举该范围为何有效的最重要依据
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 ; ; true

3. 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 中。取值按可信度递增排列:

值数值含义
UNKNOWN0proto3 的默认值,真实数据不应出现
ITU10官方 ITU 文档中定义的范围,注释应含文档链接,最可信
IR2120官方 IR21 文档中定义的范围,注释应含文档链接
GOVERNMENT30官方/政府背书实体网站(如国家电信运营商)中的证据,注释含 URL
TELECOMS40电信运营商网站(移动运营商、MVNO 等)中的证据,注释含 URL
WEB50非官方网站(如 Facebook 或公司主页)中的证据,注释含 URL
INTERNAL100无法引用外部证据的特殊接受情形;注释应说明 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 类并加入源码目录。
  • 依赖清单(均为编译期或测试期依赖):
依赖版本用途
guava32.1.2-jre集合、不可变结构、CharMatcher 等基础设施
icu4j73.2Unicode/语言标签处理(SimpleLanguageTag等)
protobuf-java3.24.0proto3 运行时
auto-value / auto-value-annotations1.10.2不可变值类型的样板代码生成
protoc-jar-maven-plugin3.11.4构建期生成 proto 类
jsr3053.0.2@Nullable等注解
truth / truth-java8-extension1.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 与源码,这套元数据处理库的当前边界可以精确概括为:

  1. 能做:读取 CSV 元数据(FileBasedCsvLoader)、按 schema 解析与校验(CsvParser/CsvSchema/MetadataException)、聚合单一区号数据(CsvData)、比较快照差异(CsvData.diff)、构建NumberingScheme、生成正则与有限状态匹配器;
  2. 尚未做:把 CSV 转换为 libphonenumber 客户端使用的 XML 元数据及其他映射文件的完整工具(README 明示,属于未来计划);
  3. 使用注意:库不受官方支持、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.

项目地址:https://gitcode.com/gh_mirrors/libp/libphonenumber
点击查看免费下载
上一篇:TradingAgents-CN 配置桥接机制验证与修复实录:从 MongoDB 统一配置到环境变量的端到端测试
下一篇:Langfuse 惰性 JSON 查看器设计剖析:字节索引引擎、异步数据源与按需物化的三层架构

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表