起因是微信群里有人问go如何判断字符串是不是utf8编码,这个问题很简单,在go的内置库unicode/utf8
里本身就提供了utf8.ValidString
方法。
但是如何获取一个字符串的编码呢?
关于unicode符号表和utf8编码可以参考这篇文章《字符编码笔记:ASCII,Unicode 和 UTF-8》
简单地说,就是给世界上所有的字符编上号,这套编号系统就是Unicode
,目前最新的 Unicode 12.1 已经收录了137,929
个字符。我们知道2^16才65536,显然是不够,而且考虑到将来的扩展,所以可以用一个int32的数字来表示来表示一个字符。那么按照常理,存储一个字符需要4个byte共32个bit。实际上UTF-32就是这么做的,utf32是定长编码, 表示任何字符都用4 byte
,这对于我们程序员来说就非常方便了(也不是绝对,参见https://www.zhihu.com/question/24572900/answer/140924633),但是缺点就是费资源了,无论是存储还是传输。
UTF8是变长编码,具体可以参考上面阮一峰的文章。我们这里主要是讲字符串的编码判断。
字符串是一个字符数组,本质是就是一连串的01
,那么如果给你一个字符串比如0110 0001
,你能判断它是什么编码吗?
答案是不能直接获取,但可以用穷举法或排除法来试探出编码.
原因两点:
二进制串具有多义性
同一个二进制串根据不同的解析规则可以解出不同的字符组成的字符串字符串并不一定有一个已知的编码
比如我随手打的一个字符串010101010101010101001010101001001
,它并不属于目前已知的任何编码,自然也无法获取它的编码了,你用任何编码展示都会显示乱码或空白。
各语言是怎么做的?
php
作为世界最好的语言,提供了mb_detect_encoding函数来判断字符串编码.函数签名为mb_detect_order ([ mixed $encoding_list = mb_detect_order() ] )
其中第二个参数是个备选集,如果你不提供,就会使用默认的mb_detect_order,它实质上是依次判断字符串是否符合每个备选编码的特征,如果是则返回该编码,不是则进行下一个编码判断.
Python
python有一个包 chardet 可以检测一个字符串编码,它的原理其实跟php差不多,也是一个一个编码去判断,而且它返回的结果除了包含编码名称,还有一个正确性值,如{'confidence': 0.99, 'encoding': 'GB2312'}
它表示运有99%的概率认为这段代码是GB2312编码方式。另外,为了提高效率,它还支持只判断字符串的一部分,以加快判断速度。
所以说,实际上一个字符串是没办法直接获取它的编码的,除非有bom头,否则只能一个个去试,就像我说一段话,你并不能直接获取它是什么语言,你只能根据你已知的语言如汉语,英语,德语的特征一个个去判断。