Libpostal: 一个C库,用于解析/规范化世界各地的街道地址。由统计NLP和开放地理数据提供支持

libpostal:国际街道地址NLP

libposal是一个C库,用于使用统计NLP和开放数据解析/规范化世界各地的街道地址。这个项目的目标是理解每种语言中的location-based字符串。有关libposal背后的研究的更全面的概述,请务必查看(冗长的)介绍性博客文章:

  • 原文:OpenStreetMap上的统计NLP

  • Follow-up1.0版本:OpenStreetMap上的统计NLP:第2部分

🇧🇷 🇫🇮 🇳🇬 🇯🇵 🇽🇰 🇧🇩 🇵🇱 🇻🇳 🇧🇪 🇲🇦 🇺🇦 🇯🇲 🇷🇺 🇮🇳 🇱🇻 🇧🇴 🇩🇪 🇸🇳 🇦🇲 🇰🇷 🇳🇴 🇲🇽 🇨🇿 🇹🇷 🇪🇸 🇸🇸 🇪🇪 🇧🇭 🇳🇱 🇨🇳 🇵🇹 🇵🇷 🇬🇧 🇵🇸

地址和它们所代表的位置对于任何处理地图的应用程序(位置搜索、运输、on-demand/delivery服务、check-ins、评论)都是必不可少的。然而,即使是最简单的地址也包含了本地约定、缩写和上下文,这使得传统的full-text搜索引擎很难对它们进行有效的索引/查询。这个库有助于将人类使用的free-form地址转换成适合机器比较和full-text索引的干净规范化形式。尽管libposal本身并不是一个完整的地理编码程序,但它可以作为一个预处理步骤,使任何地理编码应用程序更智能、更简单、在国际上更加一致。

🇷🇴 🇬🇭 🇦🇺 🇲🇾 🇭🇷 🇭🇹 🇺🇸 🇿🇦 🇷🇸 🇨🇱 🇮🇹 🇰🇪 🇨🇭 🇨🇺 🇸🇰 🇦🇴 🇩🇰 🇹🇿 🇦🇱 🇨🇴 🇮🇱 🇬🇹 🇫🇷 🇵🇭 🇦🇹 🇱🇨 🇮🇸 🇮🇩 🇦🇪 🇸🇰 🇹🇳 🇰🇭 🇦🇷 🇭🇰

核心库是用纯C语言编写的。Python、Ruby、Go、Java、PHP和NodeJS的语言绑定都得到了官方的支持,并且很容易用其他语言编写绑定。

Sponsors

如果您的公司正在使用libpostal,请考虑让您的组织赞助该项目。解释人类所指的地理位置的含义远不是一个解决的问题,赞助有助于我们在地理空间NLP领域开拓新的领域。作为赞助商,您的公司徽标将出现在Github回购页面的显著位置,并附有指向您网站的链接。赞助信息

Backers

个人用户还可以通过每月捐款来帮助支持开放地理NLP研究:

Installation (Mac/Linux)

安装之前,请确保您具备以下先决条件:

On Ubuntu/Debian

sudo apt-get install curl autoconf automake libtool pkg-config

On CentOS/RHEL

sudo yum install curl autoconf automake libtool pkgconfig

On Mac OSX

brew install curl autoconf automake libtool pkg-config

然后安装C库:

git clone https://github.com/openvenues/libpostalcd libpostal./bootstrap.sh./configure --datadir=[...some dir with a few GB of space...]make -j4sudo make install# On Linux it's probably a good idea to runsudo ldconfig

libposal支持pkg-config,因此可以使用pkg-config打印链接程序所需的标志:

pkg-config --cflags libpostal         # print compiler flagspkg-config --libs libpostal           # print linker flagspkg-config --cflags --libs libpostal  # print both

例如,如果您编写了一个名为app.c的程序,可以这样编译它:

gcc app.c `pkg-config --cflags --libs libpostal`

Installation (Windows)

MSys2/MinGW

对于Windows,构建过程当前需要MSys2和MinGW。可以从http://msys2.org。请按照MSys2网站上的说明进行安装。

请运行以下命令确保Msys2是up-to-date:

pacman -Syu

安装以下先决条件:

pacman -S autoconf automake curl git make libtool gcc mingw-w64-x86_64-gcc

然后构建C库:

git clone https://github.com/openvenues/libpostalcd libpostalcp -rf windows/* ././bootstrap.sh./configure --datadir=[...some dir with a few GB of space...]make -j4make install

注意:设置datadir时,C:驱动器将输入为/c。libpostal构建脚本会自动在路径的末尾添加libpostal,因此在Windows上,'/c'将变成C:\libpostal\

编译后的.dll将位于src/.libs/目录中,应该名为libpostal-1.dll

如果需要.lib导入库将其链接到应用程序。可以使用Visual Studiolib.exe工具和libpostal.def定义文件生成一个:

lib.exe /def:libpostal.def /out:libpostal.lib /machine:x64

解析示例

libpostal的国际地址解析器使用机器学习(条件随机字段),并且在地球上每个有人居住的国家中对超过10亿个地址进行了培训。我们使用OpenStreetMap和OpenAddresses作为结构化地址的源,使用OpenCage地址格式模板:https://github.com/OpenCageData/address-formatting来构造训练数据,并补充包含多边形,生成sub-building组件,如公寓/楼层号和邮政信箱。我们还添加缩写、随机删除组件等,以使解析器对混乱的real-world输入尽可能健壮。

这些示例解析结果来自交互式address_parser程序,该程序在运行make时使用libposal构建。请注意,解析器可以处理逗号与无逗号,以及组件的各种外壳和排列(如果输入的是城市或城市/邮政编码)。

parser

解析器在held-out数据上实现了非常高的精度,目前99.45%正确的完全解析(这意味着在分子中使用1来获得地址中的每个标记)。

Usage (parser)

下面是一个使用Python绑定的解析器API示例:

from postal.parser import parse_address
parse_address('The Book Club 100-106 Leonard St Shoreditch London EC2A 4RH, United Kingdom')

C API示例:

#include <stdio.h>#include <stdlib.h>#include <libpostal/libpostal.h>int main(int argc, char **argv) {    // Setup (only called once at the beginning of your program)
    if (!libpostal_setup() || !libpostal_setup_parser()) {        exit(EXIT_FAILURE);
    }    libpostal_address_parser_options_t options = libpostal_get_address_parser_default_options();    libpostal_address_parser_response_t *parsed = libpostal_parse_address("781 Franklin Ave Crown Heights Brooklyn NYC NY 11216 USA", options);    for (size_t i = 0; i < parsed->num_components; i++) {        printf("%s: %s\n", parsed->labels[i], parsed->components[i]);
    }    // Free parse result
    libpostal_address_parser_response_destroy(parsed);    // Teardown (only called once at the end of your program)
    libpostal_teardown();
    libpostal_teardown_parser();
}

Parser labels

从技术上讲,地址解析器可以使用在训练数据中定义的任何字符串标签,但这些标签是当前定义的,基于OpenCage的address-formatting库中定义的字段,以及libposal为处理特定模式而添加的一些字段:

  • 房子:场地名称如“布鲁克林音乐学院”,建筑名称如“帝国大厦”

  • 类别:用于类别查询,如“餐厅”等。

  • near:类似“in”、“near”等短语,用于分类短语之后,帮助解析查询,如“Brooklyn的餐馆”

  • house_number:通常是指外部(street-facing)的建筑编号。在一些国家,这可能是一个复合的,连字符的号码,其中也包括一个公寓号码,或一个街区号码(一个la Japan),但为了简单起见,libposal只将其称为house_number。

  • 道路:街道名称

  • 单元:公寓、单元、办公室、地段或其他次要单元标志

  • 级别:表示楼层号的表达式,例如“3rd floor”、“Ground floor”等。

  • 楼梯:编号/字母楼梯

  • 入口:编号/字母入口

  • po_box:邮政信箱:通常位于non-physical(mail-only)地址中

  • 邮政编码:用于邮件分拣的邮政编码

  • 郊区:通常是一个非正式的社区名称,如“哈莱姆”、“南布朗克斯”或“皇冠高地”

  • city_district:这些通常是城市内的行政区或地区,用于某些官方目的,例如“布鲁克林”、“哈克尼”或“布拉迪斯拉发四世”

  • 城市:任何人类住区,包括城市、城镇、村庄、村庄、地方等。

  • 岛屿:命名岛屿,如“毛伊岛”

  • state_district:通常是second-level行政区划或县。

  • 国家:行政区划。在英格兰,威尔士和北爱尔兰等地都被用在了“英格兰和北爱尔兰”的地图上

  • country_region:没有任何政治地位的国家的非正式划分

  • 国家:主权国家及其附属领土,任何带有ISO-3166代码的东西。

  • world_region:目前只用于在国名后面加上“西印度群岛”,这是加勒比海地区经常使用的一种模式,例如“牙买加,西印度群岛”

规范化示例

expand_addressAPI将杂乱的real-world地址转换为适合搜索索引、哈希等的规范化等效地址。

下面是一个使用Python绑定的交互式示例:

expand

libposal包含一个OSM-trained语言分类器,用于检测给定地址中使用的语言,以便应用适当的规范化。唯一需要的输入是原始地址字符串。下面是一些不同语言中不太直接的规范化的简短列表。

InputOutput (may be multiple in libpostal)
One-hundred东96街二十号东96街120号
C/Ocho,第4页calle 8 polígono工业4
V-XX塞特姆布雷,20通过20塞特姆布雷20
英语四年级埃格利斯街92号
第四、第七条7
第四、第七条ulitsa karetnyy ryad dom 4 stroyeniye 7号
Marktstraße 14马克大街14号

libposal目前支持60多种语言的这些类型的规范化,您可以添加更多(不必编写任何C)。

要进一步阅读和一些奇怪的地址edge-cases,请参阅:程序员相信地址的错误。

Usage (normalization)

下面是一个使用Python绑定以简洁的示例(大多数higher-level语言绑定都是类似的):

from postal.expand import expand_address
expansions = expand_address('Quatre-vingt-douze Ave des Champs-Élysées')

assert '92 avenue des champs-elysees' in set(expansions)

与C API等效的代码多了几行,但仍然相当简单:

#include <stdio.h>#include <stdlib.h>#include <libpostal/libpostal.h>int main(int argc, char **argv) {    // Setup (only called once at the beginning of your program)
    if (!libpostal_setup() || !libpostal_setup_language_classifier()) {        exit(EXIT_FAILURE);
    }    size_t num_expansions;    libpostal_normalize_options_t options = libpostal_get_default_options();    char **expansions = libpostal_expand_address("Quatre-vingt-douze Ave des Champs-Élysées", options, &num_expansions);    for (size_t i = 0; i < num_expansions; i++) {        printf("%s\n", expansions[i]);
    }    // Free expansions
    libpostal_expansion_array_destroy(expansions, num_expansions);    // Teardown (only called once at the end of your program)
    libpostal_teardown();
    libpostal_teardown_language_classifier();
}

Command-line用法(展开)

建立libpostal之后:

cd src/

./libpostal "Quatre vingt douze Ave des Champs-Élysées"

如果您有一个文本文件或流,每行有一个地址,command-line接口还接受来自stdin的输入:

cat some_file | ./libpostal --json

Command-line用法(解析器)

建立libpostal之后:

cd src/

./address_parser

address_parser是一个交互式shell。只需输入地址,libposal将解析它们并打印结果。

Bindings

libposal是为higher-level语言设计的。如果您看不到您选择的语言,或者您正在编写语言绑定,请让我们知道!

官方支持的语言绑定

非官方语言绑定

Database extensions

Unofficial REST API

  • libpostalrest:Libpostal REST

Libpostal REST Docker

Libpostal ZeroMQ Docker

  • Libpostal ZeroMQ Docker image:pasupulaphani/libpostal-zeromq,来源:Github

Tests

libpostal在自动化测试中使用最多。要运行测试,请使用:

make check

{146}你的贡献很容易,如果我们加上82个案例的话。我们主要使用函数测试来检查字符串输入和字符串输出。

libposal还定期从OSM(clean)获取数百万个地址的battle-tested,以及来自生产地理编码器的匿名查询(不太干净)。在此过程中,我们使用valgrind检查内存泄漏和其他错误。

Data files

libpostal需要从S3下载一些数据文件。基本文件是执行扩展所需的数据结构的on-disk表示。对于地址解析,由于模型训练需要几天时间,所以我们将完全训练过的模型发布到S3,并在OSM、OpenAddresses等中添加新地址时自动更新它。语言分类器模型也是如此。

运行make时会自动下载数据文件。要检查和下载任何新的数据文件,您可以运行make,或者运行:

libpostal_data download all $YOUR_DATA_DIR/libpostal

并将$YOUR_DATA_DIR替换为在安装期间传递给配置的任何内容。

Language dictionaries

libposal包含许多影响扩展、语言分类器和解析器的per-language字典。要探索词典或贡献您的语言中的缩写/短语,请参阅参考资料/词典。

Training data

在机器学习中,大量的训练数据往往是取得良好效果的必要条件。许多open-source机器学习项目要么只发布模型代码(当且仅当你是Google时,结果是可复制的),要么是pre-baked模型,其中训练条件未知。

Libpostal有点不同,因为它是针对所有人都可以使用的开放数据进行培训的,所以我们已经发布了整个培训管道(本次回购中的geodata包),以及由此产生的培训数据本身在Internet存档中。它的解压缩容量超过100GB。

训练数据按创建日期存储在archive.org上。在这个repo的主目录中还存储了一个名为current_parser_training_set的文件,其中存储了最近创建的训练集的日期。要始终指向最新的数据,请尝试类似于:latest=$(cat current_parser_training_set)并使用该变量代替日期。

解析器训练集

所有文件都可以在https://archive.org/download/libpostal-parser-training-data-YYYYMMDD/$FILE找到,因为gzip是tab-separated值(TSV)文件,格式如下:language\tcountry\taddress

  • formatted_addresses_tagged.random.tsv.gz(ODBL):OSM地址。公寓、邮政信箱、分类等主要添加到这些示例中

  • formatted_places_tagged.random.tsv.gz(ODBL):OSM中的每一个地名(即使是以点表示的城市,etc.),reverse-geocoded)给其父管理员,如果它们列在点/多边形上,可能包括邮政编码。每个地方都有一个基本的代表性水平,人口较多的地方按比例得到更多。

  • formatted_ways_tagged.random.tsv.gz(ODBL):OSM中的每一条街道(ways with highway=*,有一些条件),reverse-geocoded给它的管理员

  • geoplanet_formatted_addresses_tagged.random.tsv.gz(CC-BY):Yahoo GeoPlanet中的每个邮政编码(包括英国、加拿大等国的几乎所有邮政编码)及其上级管理员。GeoPlanet管理员已被清理并映射到libpostal的标记集

  • openaddresses_formatted_addresses_tagged.random.tsv.gz(各种许可证,主要是CC-BY)):大多数地址数据集来自OpenAddresses,而OpenAddresses又直接来自政府来源

  • uk_openaddresses_formatted_addresses_tagged.random.tsv.gz(CC-BY):来自英国OpenAddresses的地址

如果解析器在特定类型的地址上的性能不如您所希望的,那么最好的方法是使用grep/awk查看训练数据,并尝试确定是否有某种模式/样式的地址没有被捕获。

Features

  • 缩写扩展:例如扩展“rd”=>“road”,但几乎适用于任何语言。libposal支持超过50种语言,很容易添加新语言或扩展当前词典。支持表意文字语言(不以空格分隔,例如中文),日耳曼语言也支持,其中通配符类型连接到字符串的末尾,并且可以选择性地分开,这样Rosenstraße和Rosen Straße是等效的。

  • “{82new-York-Street”:“{82new-York-state:123new-York-state”:“{82new-York-Street”:“解析纽约市主大街123号”,“解析纽约州”。解析器适用于各种国家和语言,而不仅仅是美国/英语。该模型在超过10亿个地址和address-like字符串上训练,使用OpenCage地址格式化repo中的模板为世界上每个有人居住的国家构造格式化、带标记的训练示例。为了使训练数据尽可能接近真实的杂乱的地理编码器输入,需要执行许多类型的规范化。

  • 语言分类:多项式logistic回归训练(使用FTRL-Proximal方法来归纳稀疏性)关于所有OpenStreetMap方式、addr:*标记、地名和格式化地址。标签是使用point-in-polygon测试派生出来的,这些测试分别针对OSM国家和官方/地区语言以及admin 1边界。例如,西班牙语是西班牙的默认语言,但在不同的地区,如加泰罗尼亚、加利西亚、巴斯克地区,各自的地区语言是默认语言。Dictionary-based在区域语言是non-default的情况下,例如威尔士语、布雷顿语、奥克西坦语,可以使用Dictionary-based消歧。字典还用于缩写规范短语,如“Calle”=>“C/”(在语言分类器和地址分析器训练集上执行)

  • 数值表达式解析(“二十一”=>21,"quatre-vingt-douze“=>92,同样使用CLDR提供的数据),支持超过30种语言。处理带有连接表达式的语言,例如milleottocento=>1800。有选择地将罗马数字规范化,而不考虑出现在许多君主、教皇等名字中的语言(IX=>9)。

  • 快速、准确的标记化/词法分析:以大于1M标记/秒的速度运行,实现了用于UTF8分词的TR-29规范,通过字符而不是空格标记东亚语言字符。

  • UTF8规范化:可选地将UTF8分解为NFD规范化形式,去掉重音符号,例如a=>a和/或应用Latin-ASCII音译。

  • 音译:例如:ulica或ulitsa。使用所有CLDR转换,与ICU使用的源数据完全相同,但是libposal不需要拉入所有的ICU(可能与您的系统版本冲突)。注意:有些语言,特别是希伯来语、阿拉伯语和泰语可能不包括元音,因此通常不会匹配人类的音译。对于这些语言中的某些语言,可能实现统计音译器。

  • 脚本检测:检测给定字符串使用的脚本(可以是多个,例如free-form香港或澳门地址,可以在同一地址中同时使用汉文和拉丁脚本)。在音译中,我们可以为给定的Unicode脚本使用所有适用的音译器(例如,希腊语可以用Greek-Latin、Greek-Latin-BGN和Greek-Latin-UNGEGN)进行音译。

Non-goals

  • 验证位置是否为有效地址

  • 实际上,地理编码地址到lat/lon(这需要数据库/搜索索引)

Raison d'être

libpostal最初是作为openvences项目的一部分创建的,目的是解决场馆重复数据消除问题。在openvences中,我们有一个由数百万个地方组成的数据集,这些数据源于来自公共爬网的tb网页。常见的爬网每月发布一次,因此即使合并两次爬网的结果也会产生显著的重复。

重复数据消除是一个相对well-studied的领域,对于文本文档,如网页、学术论文等,有相当不错的近似相似性方法,如MinHash。

然而,对于物理地址来说,频繁使用诸如Road==Rd、California==CA或newyorkcity==NYC等常规缩写会使问题变得复杂一些。即使使用MinHash这样的技术,它非常适合近似匹配,并且相当于两个集合的Jaccard相似性,我们也必须处理非常短的文本,而且通常情况下,两个等价地址(一个缩写,一个完全指定)在n-gram集合重叠方面不会非常匹配。在non-Latin脚本中,比如一个俄语地址和它的音译等价物,可以想象,两个地址指的是同一个地方,甚至一个字符都不匹配。

作为一个激励人心的例子,考虑以下两种等效的方法来编写一个特定的曼哈顿街道地址,其惯例和冗长程度各不相同:

  • 佛罗里达州第26街西30号7

  • 西30号Twenty-sixth街7号楼

很明显'30 W 26 St Fl#7!=“30 WestTwenty-sixthStreet Floor Number 7”在字符串比较的意义上,但是人类可以摸索出这两个地址指的是同一个物理位置。

libposal的目标是创建标准化的地理字符串,并将其解析为组件,这样我们就可以更有效地判断两个地址实际匹配的程度,并对dups做出自动的server-side决策。

所以不是地理编码器?

如果以上听起来很像地理编码,那是因为在某种程度上,只有在OpenVenues的情况下,我们必须在没有用户界面或用户的情况下进行地理编码,以便在自动完成下拉列表中选择正确的地址。给定一个源地址的数据库,如OpenAddresses或OpenStreetMap(或以上所有内容),libpostal可以用于在MapReduce或stream processing等设置中实现地址重复删除和server-side批处理地理编码。

现在,不再尝试使用巨大的同义词文件、脚本、自定义分析器、标记器等将address-specific约定放入传统的文档搜索引擎,比如Elasticsearch,而地理编码可以是这样的:

  1. 通过libposal的expand_address运行数据库中的地址

  2. 将规范化字符串存储在您最喜欢的搜索引擎、DB、hashtable等中。

  3. 通过libposal运行用户查询或新导入,并使用这些字符串搜索现有数据库

通过这种方式,libpostal可以根据数据集的大小在恒定的时间内执行模糊地址匹配。

Why C?

libpostal用C编写有三个原因(按重要性排序):

  1. 可移植性/普遍性:libposal的目标是higher-level人们实际使用的day-to-day语言:Python、Go、Ruby、NodeJS等等。C的优点在于几乎任何编程语言都可以绑定到它,而C编译器无处不在,所以选择你最喜欢的,编写一个绑定,而且,您可以直接在应用程序中使用libpostal,而无需独立的服务器。我们支持Mac/Linux(Windows不是优先考虑的,但很乐意接受补丁),有一个标准的autotools构建和数据文件的endianness-agnostic文件格式。Python绑定作为这个repo的一部分进行维护,因为它们是构造训练数据所必需的。

  2. Memory-efficiency:libposal被设计为在MapReduce设置下运行,根据机器配置,每个进程的RAM可能限制在小于1GB。libpostal尽可能多地使用连续数组、tries(构建在连续数组上)、bloom过滤器和压缩稀疏矩阵来保持低内存使用率。在移动设备上使用libpostal是可能的,在一个国家或少数国家培训过模型。

  3. 表现:这是最后一个原因。libposal中的大多数优化都是针对内存使用而不是针对性能。考虑到libpostal的工作量,libpostal的速度相当快。在我们测试过的平台上,它可以在一个线程/进程中每秒处理10-30k个地址(这意味着在一个多小时内处理OSM planet中的每个地址)。查看简单的基准测试程序,测试您的环境和各种类型的输入。在MapReduce设置中,per-core性能没有那么重要,因为所有事情都是并行进行的,但是Mapzen有一些流接收应用程序需要运行in-process。

C conventions

libpostal是用现代的、易读的C99编写的,并使用以下约定:

  • 大约object-oriented,尽可能多地使用C

  • 几乎没有pointer-based数据结构,数组一直向下

  • 使用动态字符数组(受sds启发)以实现更安全的字符串处理

  • 限制了几乎所有的商场名称新和所有自由名称的破坏

  • 对哈希表等简单事物的高效现有实现

  • 通用容器(通过klib)尽可能

  • 数据结构尽可能地利用稀疏性

  • 对大多数字符串字典有效的double-arraytrie实现

  • Cross-platform尽可能多,尤其是对于*nix

Preprocessing (Python)

libpostalrepo中的geodatapython包包含用于预处理各种地理数据集和为C模型构建训练数据的管道。对于大多数用户来说,这个包不应该是必需的,但是对于那些对生成新类型的地址或改进libpostal的培训数据感兴趣的用户来说,这是一个值得关注的地方。

地址分析器精度

在held-out测试数据上(这意味着模型以前从未见过标记的解析),地址解析器实现了99.45%的完全解析精度。

对于像命名实体识别这样的任务,最好使用F1分数或变体,这主要是因为存在类偏差问题(大多数单词是non-entities,而一个简单地为每个标记预测non-entity的系统实际上在准确性方面做得相当好)。地址解析不是这样。每一个标记都有一个标签,在训练数据中每一个类都有上百万个例子,所以准确度是最好的,因为它是一个干净、简单和直观的性能度量。

这里我们使用完全解析精度,这意味着如果解析器获得地址中的每个标记都正确,那么我们只在分子中给它一个“点”。这应该是一个更好的衡量标准,而不是简单地看每个标记是否正确。

地址分析器的改进

尽管当前的解析器对大多数标准地址都能很好地工作,但仍有改进的余地,特别是在确保我们使用的训练数据尽可能接近实际地址方面。有两种主要方法可以进一步改进地址解析器(按难度排序):

  1. 向OSM提供地址。任何带有地址:门牌号将在下次训练时自动合并到解析器中。

  2. 如果地址解析器不能很好地用于特定的国家、语言或地址样式,那么在创建训练数据的过程中,可能会丢失或错误标记一些名称变体或位置。有时,修复方法是更新https://github.com/OpenCageData/address-formatting上的格式,在许多其他情况下,我们可以在创建训练数据时进行相对简单的调整,以确保模型经过训练,可以处理您的用例,而不必进行任何手动数据输入。如果您看到明显错误的地址解析模式,最好的做法是将问题发布到Github。

Contributing

欢迎错误报告、问题和请求。请在提交问题、错误报告或请求前阅读贡献指南。

提交问题:https://github.com/openvences/libpostal/issues。

Shoutouts

特别感谢@BenK10最初的Windows构建,以及@AeroXuk将其无缝集成到项目中并建立了Appveyor构建。

License

根据麻省理工学院的许可证条款,该软件是开源的。



原文链接:https://www.laialex.com/post/libpostal-yi-gec-ku-yong-yu-jie-xi/-gui-fan-hua-sh-tma.html

相关文章

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

返回顶部