Python编程使用有限状态机识别地址有效性
作者:somenzz 发布时间:2023-09-03 00:14:56
在收发快递填写地址的时候,我们会经常手动输入地址让程序智能识别,标准的地址比如,xx省xx市xx县/区xx路xx号,不过有时候也可以简单写:xx市xx县/区xx路xx号,或者xx省xx县/区xx路xx号,或者xx市xx路xx号。
但是有些就不是合法的地址了,比如 xx省xx街道xx号,或者 xx市xx省xx区xx号。
那么问题来了,如何识别一个地址是否有效,确切的讲,如何编程识别一个中国地址是否有效?
虽然我们大脑可以一眼识别,但是让计算器去识别,可以不是一件容易的事,根本原因在于地址的描述虽然看上去简单,但是它依然是比较复杂的上下文有关的文法。
比如 “上海市北京东路 xx 号,南京市北京东路 xx 号”,扫描到北京东路时,它后面的门牌号是否构成正确的地址要看上下文,即城市名。
所幸的是,地址的上下文比较简单,是有限的,虽然我们可以暴力穷举所有省、市、区、街道。但有效的方法还是有限状态机。
每一个有限状态机都有一个开始状态和一个终止状态,以及若干中间状态,每一条弧上带着一个状态进入下一个状态的条件,比如在上图中当前的状态如果是省,如果遇到下一个词组和区有关就进入区,如果遇到下一个词组和城市有关那么就进入市。
如果一条地址能从状态机的开始状态,经过状态机的若干中间状态,最终走到终止状态,则这条地址有效,否则无效。
比如 xx市xx省xx区xx号 就是无效地址,无法从市走到省。
现在我们通过一个简单的优先状态机来实现,代码有注释,很容易看懂
from enum import Enum
def isAddress(address: str) -> bool:
#定义状态
State = Enum("State", [
"STATE_INITIAL", #开始
"STATE_PROVINCE", # 省
"STATE_CITY", # 市
"STATE_AREA", # 区 / 县
"STATE_STREET", # 街道
"STATE_NUM", #号
"STATE_END", #结束
"STATE_ILLEGAL", #错误状态
])
def toAddressType(addr_slice : str) -> State:
if "省" in addr_slice:
return State.STATE_PROVINCE
elif "市" in addr_slice:
return State.STATE_CITY
elif "区" in addr_slice or "县" in addr_slice:
return State.STATE_AREA
elif "路" in addr_slice or "街道" in addr_slice:
return State.STATE_STREET
elif "号" in addr_slice:
return State.STATE_NUM
else:
return State.STATE_ILLEGAL
#定义状态转移
transfer = {
#开始可以转为 省或市
State.STATE_INITIAL: {
State.STATE_PROVINCE,
State.STATE_CITY,
},
#省可以转 市或区县
State.STATE_PROVINCE:{
State.STATE_CITY,
State.STATE_AREA,
},
#市可以转区或街道
State.STATE_CITY: {
State.STATE_AREA,
State.STATE_STREET,
},
#区县可以转街道
State.STATE_AREA: {
State.STATE_STREET,
},
#街道可以转号或终止
State.STATE_STREET: {
State.STATE_NUM,
State.STATE_END,
},
#号只能转终止
State.STATE_NUM: {
State.STATE_END,
},
}
st = State.STATE_INITIAL
for ch in address:
current_state = toAddressType(ch)
if current_state not in transfer[st]:
return False
st = current_state
return st in [State.STATE_STREET, State.STATE_NUM,State.STATE_END]
if __name__ == '__main__':
address1 = ["江苏省","苏州市", "吴中区", "中山北路", "208号"]
address2 = ["苏州市","吴中区", "中山北路", "208号"]
address3 = ["苏州市","吴江区", "中山北路", "208号"]
address4 = ["苏州市","吴江区","208号"]
address5 = ["苏州市","中山北路"]
assert isAddress(address1)
assert isAddress(address2)
assert isAddress(address3)
assert isAddress(address5)
assert isAddress(address4) == False
这里没有对整个地址字符串进行分词,而是直接将地址写成了列表的形式,主要为了说明状态机的实现和应用,上述代码仅能从格式上保证地址是有效的,并不能确保地址真实有效,如果要判断是真实有效的,那就需要将全国所有的省、市、区县、街道建立一个 hash 表,门牌号可以用范围表示,再进行状态转移判断。
上述代码的 transfer 就是一个 hash 表,相当于把所有正确转移的情况都穷举了一遍,它穷尽了在任何一种情况下,对应任何的输入,需要转义的状态。
最后的话
本文分享了如何实现一个简单的有限状态机
附有限状态机的开源实现:
django-fsm[1]
python-state-machine[2]
参考资料
[1]
django-fsm:
https://github.com/viewflow/django-fsm
[2]
python-state-machine:
https://github.com/jtushman/state_machine
来源:https://blog.csdn.net/somenzz/article/details/120170320


猜你喜欢
- 1.理解mask()和setmask()一般是在pyqt绘图时常见,而且在显示不规则图形时更是常见。参考书籍上说:setMask()函数的作
- 1. 池的概念主线程:相当于生产者,只管向线程池提交任务。 并不关心线程池是如何执行任务的。线程池:相当于消费者,负责接收任务,并将任务分配
- 今天开发时,使用axios返回的response中data有多个数据:如果是获取cn里的数据的,可以用:response.data.cn但是
- scikit-learn 是基于 Python 语言的机器学习工具简单高效的数据挖掘和数据分析工具可供大家在各种环境中重复使用建立在 Num
- 组建一个关于书籍、作者、出版社的例子:from django.db import modelsclass Publisher(models.
- 启发式评估法(Heuristic Evaluation)是一种用来发现用户界面设计中的可用性问题从而使这些问题作为再设计过程中的一部分被重视
- 作为一个.net后台开发的程序猿,博客里既然大多都是前端相关的博文。是不是该考虑换方向了,转前端开发得了 ...小小吐槽一下,近期受该不该跳
- 很多朋友说JavaScript的decodeURI函数也可以实现,但有bug所有呢,下面看下下面的函数,经过测试使用暂时没什么问题,我在之前
- 对于使用Django框架开发的系统,当部署时设置settings.py文件中Debug=False时xadmin后台管理系统样式会丢失。【问
- 写在前面之前的文章中已经讲过了遗传算法的基本流程,并且用MATLAB实现过一遍了。这一篇文章主要面对的人群是看过了我之前的文章,因此我就不再
- 1.创建带有缺失值的数据库:import pandas as pdimport numpy as npdf = pd.DataFrame(n
- 前言mysql模块(项目地址为https://github.com/mysqljs/mysql)是一个开源的、JavaScript编写的My
- 本文给大家分享的是查看MySQL连接的root密码的方法,下面话不多说来来看正文:1.首先我们进到MySQL的bin目录下➜ cd /usr
- 本文实例为大家分享了python图书管理系统的具体代码,供大家参考,具体内容如下实现语言:python图形框架:DTK+2.0数据库框架:S
- 在python中可以通过内置函数int()函数进行二进制转十进制;int()函数可以将一个指定进制的数字型字符串或者十进制数字转化为整型。P
- 目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,
- 本文实例讲述了python通过索引遍历列表的方法。分享给大家供大家参考。具体如下:python中我们可以通过for循环来遍历列表:colou
- 在使用数据库的时候,难免要在使用过程中进行删除的操作,如果是使用int类型的字段,令其自增长,这是个最简单的办法,但是后果会有些不是你想要的
- 背景:使用python脚本传递参数在实际工作过程中还是比较常用,以下提供了好几种的实现方式:一、使用sys.argv的数组传入说明:使用sy
- 首先给大家介绍ThinkPHP函数详解:M方法M方法用于实例化一个基础模型类,和D方法的区别在于:1、不需要自定义模型类,减少IO加载,性能