firemail
标题: 字符集和编码 字节序 [打印本页]
作者: hechengjin 时间: 2016-6-14 09:34
标题: 字符集和编码 字节序
http://www.cnblogs.com/foreach-b ... e_into_account.html
字符集和编码为什么要考虑文件的编码?
当你将文件从阿拉伯传到中国,告诉你的中国朋友要进行一个外部排序,你的中国朋友也许会傻:
上面是什么?乱码.
你也可以这样体验乱码:
echo "数" > t.txticonv -f UTF-8 -t UNICODE t.txt��pe好了,你知道了如果不知道文件的编码,你可能会解析到乱码.
字符集是什么?charset - > char-set,字符的集合.比如UNICODE、ASCII
编码是什么?encoding,字符的表示.比如UTF-8、ASCII
字符集和编码的关系你晕了,我也晕了,ASCII码怎么既是字符集又是编码?
历史上,字符集和编码是同义词,实际却又不尽相同,没有一个规范地定义,那怎么理解呢?
字符集,往往强调其所“支持”的字符范围,集外的字符它不支持.集合就有一个边界,边界内的我给个表示,边界外的我不知道怎么表示。
编码,往往强调针对某个字符集的字符,我这么去转换表达为机器可理解的方式-二进制,如果对某个字符集的字符,我的转换方式和其一致,那么我既是编码也是字符集,否则我就只是一种字符集的转换格式。
那么,UNICODE是字符集,它所支持的字符,在它内部,也有个表示,这种表示是不是一种编码?毫无疑问,是一种编码。
UTF-8是一种编码,是对UNICODE的一个变长实现,这种编码和UNICODE编码是什么关系?转换关系。
所以看编码还是字符集,往往要看“语境”。
举个简单的例子:
汉字:“数”
编码/字符集进制值
UNICODE166570
UNICODE2110010101110000
UTF-816E6 95 B0
UTF-82111001101001010110110000
这个例子,你可以这样体验:
/// 看utf-8echo "数" > t.txthexdump -xv -C t.txt0000000 95e6 0ab0 00000000 e6 95 b0 0a |....|00000004/// 看unicodeiconv -f utf-8 -t unicode t.txt | hexdump -xv -C0000000 feff 6570 000a 00000000 ff fe 70 65 0a 00 |..pe..|00000006
字节序如果你注意到汉字“数”经过UTF-8编码后的16进制是E6 95 B0,再给它后面加个换行是E6 95 B0 0A,你用下面的方法会看到:
hexdump -xv -C t.txt0000000 95e6 0ab0 00000000 e6 95 b0 0a |....|00000004按双字节(16位)的方式解析,你得到是95 E6 0A B0,你一定很费解,然并卵,这种费解的根源就是字节序造成的。
字节序就是字节的顺序,如果按照双字节(16位)的方式解析E6 95 B0 0A,那么读16位,得到E6 95,字节序反一下,得到95 E6,再读16位,得到B0 0A,字节序反一下,得到0A B0,合起来就是95 E6 0A B0。
这种“反字节序”就叫Little-Endian,小端或者小尾。
大端(Big-Endian | BE)
注:图片来自wiki从上图可以看出,一个数的最高字节被放在内存的低地址处:
0A -> a + 0,这种方式就是大端(Big-Endian),数的最高字节叫作Most Significant Byte,这个你可以称作最高有效字节或者最重要的字节,为什么最重要呢?
对于1个数来讲,它的最高字节能反映这个数的符号(正负),也比低位字节更能代表这个数的大小。
大端存储的特点也决定了它的优点,就是近似估计一个数的大小和符号,只需要取最高字节即可。
小端(Little-Endian | LE)反之,如果一个数的最低字节被放在内存的低地址处:
0D -> a + 0,这种方式就是小端(Little-Endian),数的最低字节叫作Least Significant Byte,可以称作最低有效字节或者最不重要的字节。
32位/64位地址下:
10进制16进制位数(bits)/字节数(bytes)BELE
1684961410A 0B 0C 0D32/40A 0B 0C 0D0D 0C 0B 0A
30850C 0D16/20C 0D0D 0C很显然,大端存储符合我们人类从左往右书写或阅读的习惯,为什么又要出一个如此麻烦的小端存储呢?
小端存储,取数的时候可以这样:
a + 0 -> 第一个字节
a + 1 -> 第二个字节
a + 2 -> 第三个字节
a + 3 -> 第四个字节
一个基址a,+不同的偏移即可完成不同精度的取数操作,high么?
如何区分大端还是小端?如果你注意到:
/// 看unicodeiconv -f utf-8 -t unicode t.txt | hexdump -xv -C0000000 feff 6570 000a 00000000 ff fe 70 65 0a 00 |..pe..|00000006那么前2个字节feff就表示了小端存储,很简单,fe < ff,这2个字节叫作Byte Order Mark,称字节序标记,不仅可以用作区分文件是大端/小端存储,还可以表示编码。
至于编程的方式区分大端小端,这个如果你谨记字节序的含义相信不难写出代码:
/// java获取字节序BE/LE Field us = Unsafe.class.getDeclaredField("theUnsafe"); us.setAccessible(true); Unsafe unsafe = (Unsafe)us.get(null); long a = unsafe.allocateMemory(8); ByteOrder byteOrder = null; try { unsafe.putLong(a, 0x0102030405060708L); byte b = unsafe.getByte(a); switch (b) { case 0x01: byteOrder = ByteOrder.BIG_ENDIAN; break; case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN; break; default: assert false; byteOrder = null; } } finally { unsafe.freeMemory(a); } System.out.println(byteOrder.toString());
用C/C++就更简单了,一个union就搞定了.
字节序影响了什么?字符集和编码如果搞错,会导致乱码,字节序搞错,会导致错码,很显然0C0D和0D0C不是同一个数,不是么?
欢迎光临 firemail (http://firemail.wang:8088/) |
Powered by Discuz! X3 |