缓存时间:
2026/08/29 09:47
# 解析臭名昭著的日本邮政CSV文件
来源:https://www.dampfkraft.com/posuto.html
去年年底,我发布了 **posuto**(https://github.com/polm/posuto),这是一个以易于使用的格式呈现日本邮政编码数据的软件包。它基于 **日本邮政发布的数据**(https://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html),该数据以使用广泛但难以解析而闻名。
 这个由 **Irasutoya**(https://www.irasutoya.com/2019/08/blog-post_590.html)创作的可爱角色很讨喜,但原始的邮政CSV数据却并非如此。
我最初注意到邮政数据,是因为在一次在线表单中输入邮政编码后,地址自动补全为 "XXX-borough(不包括以下建筑)"。我完全不明白这个括号里的内容指什么,于是开始寻找常用的邮政数据来源,发现了CSV文件,也发现了问题所在。原来CSV文件中包含面向阅读者的括号注释,并且引用了行的顺序。
这带来了问题。这些数据主要按行逐行使用,而括号注释在此毫无意义。既然CSV是字段分隔格式,也根本不需要括号注释——你完全可以添加一个专门的备注字段。
这仅仅是 **`ken_all.csv`** 的众多问题之一。你可以在 **Twitter**(https://twitter.com/search?q=ken_all.csv&src=typed_query&f=live)上看到人们不断抱怨它,甚至曾短暂存在过一个专门收集网上各种吐槽的 **博客**(http://ken-all.hatenadiary.com/)。一条 **特别有趣的推文**(https://twitter.com/bulkneets/status/1259457777862184966)描述道,那些期望计算机屈从于人类意志的人,在地狱里会受到永恒解析 **`ken_all.csv`** 的惩罚。
该文件的 **README**(https://www.post.japanpost.jp/zipcode/dl/readme.html)解释说,字段过长的行会被拆分成多行。具体而言,如果町名超过38个字符,或者半角片假名(**半角片假名**)读音字段超过76个字符,该行就会被拆分为两行。过长的町名字段会延续,而所有其他字段都会被重复。下面是这种格式的示例缩略:
```
12345,Tokyo,Minato,This place name is really
12345,Tokyo,Minato,very long it didn't fit in
12345,Tokyo,Minato,a single line so we had to
12345,Tokyo,Minato,split it
```
其动机未作解释。也许三十年前某处有个用于存储一行的固定宽度缓冲区。我在前一份工作中曾处理过来自数百个不同来源的CSV和其他文件,见过许多可怕的情况,但从未在其他地方见过这种特定的格式选择。还应该指出,虽然长度限制如上所述,但长行中插入换行符的位置似乎很随意,既不在字符限制处,也不在正常的单词边界处。
值得注意的是,并非所有CSV的问题都是纯技术性的;邮政编码本身就很复杂。CSV中行数最多的邮政编码——惊人的66行——是〒452-0961,它指的是爱知县清须市的 **春日町地区**(https://ja.wikipedia.org/wiki/%E6%98%A5%E6%97%A5%E7%94%BA_(%E6%84%9B%E7%9F%A5%E7%9C%8C))。之所以有这么多行,是因为每个町名都占单独一行。(这个特定案例可能与春日町在2006年至2009年并入清须市之前,一直是日本面积最小的町有关。)
相比之下,根据上述换行规则,最长的*连续*行属于〒602-8368或〒602-8374的条目,两者各有八行。它们都位于京都几个采用独特、怪异的基于交叉点的地址系统的地区之一(https://en.wikipedia.org/wiki/Japanese_addressing_system#Kyoto)。该条目大致如下:
```
12345,Kyoto,Kyoto,"North Town (Up Lower Godsroad from"
12345,Kyoto,Kyoto,"the West, Down Turtle Street from the"
12345,Kyoto,Kyoto,"East, Up Old Temple Road from the"
12345,Kyoto,Kyoto,"West)"
```
这里我使用了引号字段,但实际的CSV并不给字段加引号,而是使用了一种不同的逗号。
还有其他问题。许多地区有兜底邮政编码,其中町名显示为 "不包括以下",唯一能做的就是查找该确切字符串并将其排除。有各种类似的字符串,很难确保我已全部捕获。
另一个注释的例子是“一円”。通常这意味着“一日元”,但它也表示“周边地区”,是CSV中应从町名中移除的备注,*除了*在滋贺县恰好有一个町名就是“一円”(〒522-0317)。
日本邮政还提供一个单独的 **罗马字文件**(https://www.post.japanpost.jp/zipcode/dl/roman-zip.html)。它的更新频率低于主文件,经常不同步,并且提供的罗马字质量极低。目前为了保持一致性,我仍在posuto中提供这些数据,但老实说你直接使用 **cutlet**(https://www.dampfkraft.com/nlp/cutlet-python-romaji-converter.html)就好了。举个罗马字转换糟糕的例子:
```
大手町 JAビル
OTEMACHI JIEIEIBIRU
```
这里发生的情况是,"JA" 被转换成了日语的音读 “ジェイエイ”。然后 ジェ(写作“大字ji小字e”,发音为“je”)被当作大字处理,转换成了 “jie”,其他字符则按原样转换,把一个已经是拉丁字母的东西变成了字母汤。相比之下,cutlet 能很好地将 “JAビル” 转换为 “JA building”(大小写处理确实还需要一些改进)。类似的问题会把 “Roppongi Hills” 变成 “Roppongihiruzu”,把 “Sweden Hills” 变成 “Suedenhiruzu”。
总之,处理这个文件是我在一个地方可能蕴含多少复杂性方面学到的谦卑一课。我略过了许多细节,但你可以在posuto的README中找到它们。
你可以将 **posuto**(https://github.com/polm/posuto)作为库使用,或者如果你不使用Python,只需下载预处理的JSON文件并使用它。如果你找到了好的用途,我很乐意听你分享。
哦,如果你需要一个Win3.1或DOS程序将数据复制到IBM H软盘上,只需查看 **日本邮政页面的底部**(https://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html)——他们已经为你准备好了。 Ψ