Python检测生僻字的实现方法
解决思路
首先想到的就是利用python的正则表达式来匹配非法字符,然后找出非法记录。然而理想总是丰满的,现实却是残酷的。在实现的过程中,才发现自己对于字符编码、以及python内部字符串表示的相关知识的缺乏。在这期间,踩过了不少坑,到最后虽然还有些模糊的地方,但总算有一个总体清晰的了解。在此记录下心得,避免以后在同一个地方跌倒。
以下的测试环境是ArcGIS10.3自带的python2.7.8环境,不保证其他python环境也适用。
python正则表达式
python中的正则功能由内嵌的re函数库提供,主要用到3个函数。re.compile()提供可重用的正则表达式,match()和search()函数返回匹配结果,两者之间的区别在于:match()从指定位置开始匹配,search()会从指定位置向后搜索直到找到匹配字符串。例如下面的代码中,match_result从第一个字符f开始匹配,匹配失败返回空值;search_result从f开始向后搜索,直到找到第一个匹配的字符a,然后通过group()函数输出匹配结果为字符a。
importre pattern=re.compile('[abc]') match_result=pattern.match('fabc') ifmatch_result: printmatch_result.group() search_result=pattern.search('fabc') ifsearch_result: printsearch_result.group()
以上的实现方式需要先编译一个pattern,然后再进行匹配。实际上,我们可以直接利用re.match(pattern,string)函数来实现相同的功能。但是直接匹配的方式没有先编译再匹配的方式灵活,首先是正则表达式没办法重用,如果大量数据进行同一模式匹配,意味着每次都需要内部编译,造成性能损失;另外,re.match()函数没有pattern.match()功能强大,后者可以指定从哪个位置开始匹配。
编码问题
了解python正则的基本功能后,剩下的事情就是找到一个合适的正则表达式来匹配生僻字和非法字符。非法字符很简单,采用以下pattern就可以实现匹配:
pattern=re.compile(r'[~!@#$%^&*]')
然而对于生僻字的匹配,着实难倒了我。首先是对于生僻字的定义,什么样的字算生僻字?经过咨询项目经理,规定非GB2312的字符属于生僻字。接下来的问题是,如何匹配GB2312字符?
经过查询,GB2312的范围是[\xA1-\xF7][\xA1-\xFE],其中汉字区的范围是[\xB0-\xF7][\xA1-\xFE]。因此,添加生僻字匹配后的表达式为:
pattern=re.compile(r'[~!@#$%^&*]|[^\xA1-\xF7][^\xA1-\xFE]')
问题似乎是顺理得当地解决了,然而我还是toosimpletoonaive。由于要判断的字符串都是从图层文件读取的,arcpy贴心地将读取的字符编码为unicode格式。因此,我需要找出GB2312字符集在unicode中的编码范围。但现实是,GB2312字符集在unicode中的分布并不是连续的,使用正则表示这个范围必定是非常复杂的。使用正则表达式匹配生僻字的构想,似乎陷入了死胡同。
解决方案
既然提供的字符串是unicode格式,那么我可不可以将其转换为GB2312再进行匹配呢?实际上是不行,因为unicode字符集要远大于GB2312字符集,因此GB2312=>unicode总是可以实现的,而反过来unicode=>GB2312不一定能成功。
这突然为我提供了另外一种思路,假设一个字符串的unicode=>GB2312转换会失败,那么是不是恰恰说明了它不属于GB2312字符集?所以,我使用unicode_string.encode('GB2312')函数尝试转换字符串,捕获UnicodeEncodeError异常来识别生僻字。
最终的代码如下:
importre defis_rare_name(string): pattern=re.compile(u"[~!@#$%^&*]") match=pattern.search(string) ifmatch: returnTrue try: string.encode("gb2312") exceptUnicodeEncodeError: returnTrue returnFalse
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。