Python中的UCS编码与Unicode字符转换


引子

写这篇文章的起因是有人问询问,我公开的JCLdic能不能识别下面的例子:

  • 漢字の「口」とカタカナの「ロ」
  • 漢字の「一」と長音記号の「―」  

虽然JCLdic没有考虑这种情况,但是我试着提出一个方案,来解决这个问题。

方法很简单,就是设定一个标准,把所有的容易混淆的字符,全部都统一。比如汉字的「一」和カタカナ的「―」全部统一为汉字的「一」。

拿「一」来距离,构建一个和「一」类似的所有字符集,如果判断字符集中的字符在文本中出现了,就把这些字符全部统一为汉字的「一」。

那么如何判断呢?

最简单的是在Python里用==进行判断,因为都是Unicode编码,可以识别各种不同的字符。

不过在看到这篇文章(见下面解释)里关于NFKD/NFKC正规化的内容后,我猜测使用正规化的话,这些字符会不会自动统一为同一个字符「一」。

文章中提到了几个不同的横线,经过NFKD/NFKC的正规化后,全部变为002D。

所以我打算使用Python进行NFKD/NFKC正规化,看看能不能得到满意的效果。

关于Unicode的预备知识

在大部分关于Unicode的介绍中,基本都是用U+4E00这样的表示方法(比如CJK統合漢字-全漢字一覧)。但这种表示方法更正确的称呼应该是UCS(Universal Character Set)。只不过该字符集与Unicode字符集作了整合。而UCS-2作为一种编码方案,和我们常见的UTF-8一样,都可以用来表示Unicode。而在Python中,输入UCS-2格式的字符串后,可以直接打印出对应的汉字。

>>> '\u4e00'
'一'

比如下面是关于汉字“一”的相关介绍:

关于这部分,推荐阅读一下相关的资料:

  • 彻底理解字符编码:主要看Unicode,UCS,UTF-8

    • Unicode编码方案主要有两条主线:UCS和UTF。UTF主线由Unicode Consortium进行维护管理,UCS主线由ISO/IEC进行维护管理。
  • 细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4

    • 除了UCS-4,ISO 10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),其编码固定占用2个字节,它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)。例:“汉”的UCS-2编码为6C49。
  • Python3中如何得到Unicode码对应的中文:使用encode('unicode-escape')可以转换成UCS-2编码。

    • 先看一下 text 的 type。如果是 str,应该是不需要转码如果是 bytes,则转之。ucps.py
>>> '\u8bf7'
'请'
>>> b'\u8bf7'
b'\\u8bf7'
>>> b'\u8bf7'.decode('unicode-escape')
'请'
>>> b'\u8bf7'.decode('unicode_escape')
'请'
>>> r'\u8bf7' # 脚本ucps.py是处理这种情况的
'\\u8bf7'
>>> ucps_to_str(r'\u8bf7')
'请'
>>> '请'.encode('unicode-escape')
b'\\u8bf7'
>>> _.decode('unicode-escape')
'请'

对所有类横线字符「一」进行正规化

收集到的类似横线字符「一」的所有字符:

文字 UTF-8 Unicode 説明
- 2D U+002D ASCIIのハイフン
E383BC U+30FC 全角の長音
E28090 U+2010 別のハイフン
E28091 U+2011 改行しないハイフン
E28093 U+2013 ENダッシュ
E28094 U+2014 EMダッシュ
E28095 U+2015 全角のダッシュ
E28892 U+2212 全角のマイナス
EFBDB0 U+FF70 半角カナの長音
E4B880 U+4E00 漢字

试验结果

我把上面的所受hyphen全部正则化一下。

s = '-ー‐‑–—―−ー一'

for i in s: 
    print(i.encode('unicode-escape')) 

b'-'
b'\\u30fc'
b'\\u2010'
b'\\u2011'
b'\\u2013'
b'\\u2014'
b'\\u2015'
b'\\u2212'
b'\\uff70'
b'\\u4e00'

s_norm = unicodedata.normalize('NFKC', s)

for i in s_norm: 
    print(i.encode('unicode-escape')) 

b'-'
b'\\u30fc'
b'\\u2010'
b'\\u2010'
b'\\u2013'
b'\\u2014'
b'\\u2015'
b'\\u2212'
b'\\u30fc'
b'\\u4e00'

可以看到,只有第4个字符为了2010。

看来要想构建统一的字符集,还是要一个一个字符进行判断。

欢迎订阅我的博客:RSS feed
知乎: 赤乐君
Blog: BrambleXu
GitHub: BrambleXu
Medium: BrambleXu

参考资料


文章作者: BrambleXu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 BrambleXu !
评论
  目录