Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2104|回复: 0
打印 上一主题 下一主题

字符集和编码 字节序

[复制链接]

388

主题

602

帖子

2218

积分

金牌会员

Rank: 6Rank: 6

积分
2218
跳转到指定楼层
楼主
发表于 2016-6-14 09:34:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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不是同一个数,不是么?


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|firemail ( 粤ICP备15085507号-1 )

GMT+8, 2024-4-19 16:28 , Processed in 0.056864 second(s), 19 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表