把脏地址解析成区划码(三):层级、时间机器和一处数据缺口

0 阅读4分钟

前两篇讲了 10 关,都是"答案看着像、其实不对"。这一篇的 5 关,问题往资料层和层级逻辑里钻了一层:市名把自己下辖的区名吃掉、历史回放认错全名、同一个地名在不同年份指两个不同的地方,还有一处根因不在引擎、而在事件库本身。

第 11 关:黄石港区 里的"黄石"

输入 湖北省黄石港区,输出 湖北省-黄石市。区没了。

因为市名"黄石市"去掉后缀的短形式"黄石",正好是"黄石港区"的开头,被当成市名消费掉了。同一批还有 本溪满族自治县,"本溪"既是市名又是县名的开头。

改法:市名短形式匹配之前,先看本市有没有更长的县名以它开头。有就不按"市"消费,交给县级解析。

第 12 关:时间机器查"芜湖县"

我这个引擎有个时间机器的功能,可以查某年某地叫什么。查 芜湖县(1992 年),返回的是 安徽省-芜湖市

根因是收集当年命中的时候没区分"全名命中"和"短形式命中",排序只看位置和级别,于是短形式的地级先胜出。

改法是记录是否全名命中,同名多命中时全名优先。第 2 关那个"1987 年的海南省",语义照样保住。

第 13 关:巢湖市 的两种身份

"巢湖市"既是 2011 年被撤销的地级市,也是现在仍然存在的县级市,隶属合肥市。所以两个输入,答案必须不一样:

  • 安徽省合肥市巢湖市中垾镇,这里的"巢湖市"必须按现行县级市理解;
  • 巢湖市庐江县,这里的"巢湖市"必须按已撤销的地级市理解,因为庐江县当年属巢湖市,现在属合肥市。

原来两种情况一律做历史替换,于是第一句被替换成"合肥市",县级市和乡镇一起消失。

改法是让别名替换变成上下文敏感。别名本身是现行区划名时,如果整串就是它,或者它前面已经出现了省名或市名,就不做历史替换。

第 14 关:(山东省)莒县 只解析到省级

根因是特别小的一个实现细节。判断"是否在词边界"时,我用"字符码小于 U+4E00"来区分汉字,而全角括号的码位比"一"还大,被当成了汉字,于是"莒县"这两个字不认可前边界。

改成按 CJK 区间判断就对了。

第 15 关:事件库里少写了两个字段

万县区昌平县顺义县 这些旧名一直解析不出来。查到根上,是事件库里有 128 条记录的新路径只写了末段。比如 万县区 → 万州区,缺了"重庆市",被引擎的完整性检查整条丢掉了。

补齐之后,自动生成的历史别名从 888 条涨到 915 条。一个数据缺口,换回一批能用的旧名映射,包括北京昌平、顺义、大兴、房山、平谷、怀柔、密云、延庆这些撤县设区的旧名。

同一批还顺手改了一个小的:开源版不随包分发逐年快照,时间机器原来会直接抛异常。改成返回"本发行版不提供该能力"的诚实拒绝。宁可说没有,不要崩。


这一批的账

数(截至本篇)
本篇这 5 关各补 3 条以上回归,共 18 条
累计修掉的解析缺陷15 个
累计新增回归用例59 条,回归集从 110 条扩到 169 条,全绿
自动生成的历史别名915 条

引擎和数据在 GitHub:github.com/nbsetsail/r… 。代码 MIT,数据 CC-BY-4.0,署名即可使用。变更事件库的每条都带出处,可以用它做时间机器式的回放。

最后一篇讲第四轮:把"交付件"本身也拉去考试之后,又发现了什么。