喵の窝

UTF8编码的处理(下)

‘你可知回字有几种写法?’本是孔乙己中的一个讽刺段子。但是到了utf8编码中。这就变成了一个非常麻烦的问题。

在utf8编码中,同一个字符可以有多种表示方法。当然,并不是每一个字符都有多种表示方法。而是一些特殊的字符,比如‘å’就有两种表示方法:”\u00e5”和”\u0061\u030a”。前一种表示方法是‘å’字符的码点。而后一种表示方法则是字符’a’加上字符’ ̊’。

当然,这两种写法在显示上是完全没有区别的。目前支持utf8的系统都能正确把两个字符拼成一个字符然后显示。但是,如果需要搜索,查找,替换这种字符的话就稍微麻烦一些。

在有些语言比如swift中,字符串的相关api已经考虑过这种情况了。因此,在做字符串相关操作时不用考虑这种情况。

还有一些语言比如GoLang并没有在语言层面考虑这种问题。取而代之的是提供了unicode标准化的库。这样一来我们可以先把需要处理的字符串统一成一个标准。然后在进行比较等相关操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
var str string = "\u00E5"
var str1 string = "\u0061\u030A"
fmt.Printf("str : %q bytes in str % X\n", str, []byte(str))
fmt.Printf("str1 : %q bytes in str1 % X\n", str1, []byte(str1))

fmt.Printf("str == str1 : %t\n", str == str1)

fmt.Printf("strCompare(str, str1): %t\n", strCompare(str, str1))

}

func strCompare(str1, str2 string) bool {
nfcBytes1 := norm.NFC.Bytes([]byte(str1))
nfcBytes2 := norm.NFC.Bytes([]byte(str2))

return string(nfcBytes1) == string(nfcBytes2)
}

上面的方法申明了两个字符串,然后打印他们的字符和储存字符所用的字节。
然后用‘==’号比较这两个字符串是否相等。最后再用strCompare来比较两个字符串是否相等。
在strCompare函数中,把两个utf8字符串被统一成了NFC格式,然后再用‘==’号来比较。
程序的输出如下。

1
2
3
4
str	: "å"	bytes in str	C3 A5
str1 : "å" bytes in str1 61 CC 8A
str == str1 : false
strCompare(str, str1): true

从输出中可以看出,虽然两个字符串都表示的同一个字符,但是由于他们的表示方法不一样,‘==’运算符判断两个字符是不相等的。而strCompare函数中,由于两个字符串已经被统一成一个标准,因此两个字符串就相等了。


Fin .