字符、字节及其编码

从计算机对多国语言的支持角度看,大体能够分为三个阶段:程序员

系统内码 说明 系统
阶段一 ASCII 计算机刚开始只支持英语,其它语言不可以在计算机上存储和显示。 英文 DOS
阶段二 ANSI编码
(本地化)
为使计算机支持更多语言,一般使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。好比:汉字 '中' 在中文操做系统中,使用 [0xD6,0xD0] 这两个字节存储。

不一样的国家和地区制定了不一样的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来表明一个字符的各类汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码表明 GB2312 编码,在日文操做系统下,ANSI 编码表明 JIS 编码。

不一样 ANSI 编码之间互不兼容,当信息在国际间交流时,没法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
中文 DOS,中文 Windows 95/98,日文 Windows 95/98
阶段三 UNICODE
(国际化)
为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各类语言中的每个字符设定了统一而且惟一的数字编号,以知足跨语言、跨平台进行文本转换、处理的要求。 Windows NT/2000/XP,Linux,Java

 

字符串在内存中的存放方法:网络

在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。好比,"Bob123" 在内存中为:ide

42 6F 62 31 32 33 00
B o b 1 2 3 \0

在使用 ANSI 编码支持多种语言阶段,每一个字符使用一个字节或多个字节来表示(MBCS),所以,这种方式存放的字符也被称做多字节字符。好比,"中文123" 在中文 Windows 95 内存中为7个字节,每一个汉字占2个字节,每一个英文和数字字符占1个字节:函数

D6 D0 CE C4 31 32 33 00
1 2 3 \0

在 UNICODE 被采用以后,计算机存放字符串时,改成存放每一个字符在 UNICODE 字符集中的序号。目前计算机通常使用 2 个字节(16 位)来存放一个序号(DBCS),所以,这种方式存放的字符也被称做宽字节字符。好比,字符串 "中文123" 在 Windows 2000 下,内存中实际存放的是 5 个序号:工具

2D 4E 87 65 31 00 32 00 33 00 00 00      ← 在 x86 CPU 中,低字节在前
1 2 3 \0

一共占 10 个字节。编码

2: 字符,字节,字符串spa

字节:8位byte为一字节,它是计算机中存储数据的单元,占8位二进制数。操作系统

字符:根据编码方式的不一样,字符所占的字节个数也不一样,如上例所述。code

            ASSCII:一个字符占用一个字节内存

            ANSI:一个字符能够占一个或多个字节,好比,a占一个字节,而中文字符通常占二个字节

            UNICODE:一个字符占二个字节,对于英文字符也是,可经过低字节填充来实现

字符串:它由字符组成

3:字符集和编码

使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫作“字符集”。好比:GB2312, GBK, JIS 等

规定每一个“字符”分别用一个字节仍是多个字节存储,用哪些字节来存储,这个规定就叫作“编码”。

各国因语言不一样,包含的字符也不一样, 那么在编码的时候各自的字符集也不尽相同,例如:汉字标准(GB2312)

注:“UNICODE 字符集”包含了各类语言中使用到的全部“字符”。用来给 UNICODE 字符集编码的标准有不少种,好比:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig、

4:经常使用的编码

简单介绍一下经常使用的编码规则,为后边的章节作一个准备。在这里,咱们根据编码规则的特色,把全部的编码分红三类:

分类 编码标准 说明
单字节字符编码 ISO-8859-1 最简单的编码规则,每个字节直接做为一个 UNICODE 字符。好比,[0xD6, 0xD0] 这两个字节,经过 iso-8859-1 转化为字符串时,将直接获得 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 "ÖÐ"。

反之,将 UNICODE 字符串经过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。
ANSI 编码 GB2312,
BIG5,
Shift_JIS,
ISO-8859-2 ……
把 UNICODE 字符串经过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。

反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。好比,[0xD6, 0xD0] 这两个字节,经过 GB2312 转化为字符串时,将获得 [0x4E2D] 一个字符,即 '中' 字。

“ANSI 编码”的特色:
1. 这些“ANSI 编码标准”都只能处理各自语言范围以内的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。
UNICODE 编码 UTF-8,
UTF-16, UnicodeBig ……
与“ANSI 编码”相似的,把字符串经过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。

与“ANSI 编码”不一样的是:
1. 这些“UNICODE 编码”可以处理全部的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间是能够经过计算获得的。

5:字符与编码在代码中的实现

5.1 程序中的字符与字节

在 C++ 和 Java 中,用来表明“字符”和“字节”的数据类型,以及进行编码的方法:

类型或操做 C++ Java
字符 wchar_t char
字节 char byte
ANSI 字符串 char[] byte[]
UNICODE 字符串 wchar_t[] String
字节串→字符串 mbstowcs(), MultiByteToWideChar() string = new String(bytes, "encoding")
字符串→字节串 wcstombs(), WideCharToMultiByte() bytes = string.getBytes("encoding")

以上须要注意几点:

  1. Java 中的 char 表明一个“UNICODE 字符(宽字节字符)”,而 C++ 中的 char 表明一个字节。
  2. MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。
5.2 C++ 中相关实现方法

声明一段字符串常量:

// ANSI 字符串,内容长度 7 字节
char
     sz[20] = "中文123";

// UNICODE 字符串,内容长度 5 个 wchar_t(10 字节)
wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";

UNICODE 字符串的 I/O 操做,字符与字节的转换操做:

// 运行时设定当前 ANSI 编码,VC 格式
setlocale(LC_ALL, ".936");

// GCC 中格式
setlocale(LC_ALL, "zh_CN.GBK");

// Visual C++ 中使用小写 %s,按照 setlocale 指定编码输出到文件
// GCC 中使用大写 %S

fwprintf(fp, L"%s\n", wsz);

// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节
wcstombs(sz, wsz, 20);
// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串
mbstowcs(wsz, sz, 20);

在 Visual C++ 中,UNICODE 字符串常量有更简单的表示方法。若是源程序的编码与当前默认 ANSI 编码不符,则须要使用 #pragma setlocale,告诉编译器源程序使用的编码:

// 若是源程序的编码与当前默认 ANSI 编码不一致,
// 则须要此行,编译时用来指明当前源程序使用的编码

#pragma setlocale
(".936")

// UNICODE 字符串常量,内容长度 10 字节
wchar_t wsz[20] = L"中文123";

以上须要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的做用是不一样的,#pragma setlocale 在编译时起做用,setlocale() 在运行时起做用。

对编码的误解
误解一 在将“字节串”转化成“UNICODE 字符串”时,好比在读取文本文件时,或者经过网络传输文本时,容易将“字节串”简单地做为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。

而实际上,在非英文的环境中,应该将“字节串”做为 ANSI 字符串,采用适当的编码来获得 UNICODE 字符串,有可能“多个字节”才能获得“一个字符”。

一般,一直在英文环境下作开发的程序员们,容易有这种误解。
误解二 在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪一种编码才能被正确地使用。这使咱们造成了一个惯性思惟:“字符串的编码”。

当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,所以已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时,或 者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。

很多的人都有这个误解。

第一种误解,每每是致使乱码产生的缘由。第二种误解,每每致使原本容易纠正的乱码问题变得更复杂。

在这里,咱们能够看到,其中所讲的“误解一”,即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。所以,咱们经常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操做,获得原始的“字节串”。而后再使用正确的 ANSI 编码,好比 string = new String(bytes, "GB2312"),来获得正确的“UNICODE 字符串”。

非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。若是程序运行时的语言环境与开发时的语言环境不一样,将会致使 ANSI 字符串的显示失败。

好比,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。若是这个日文程序界面改成采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将能够显示正常的日文。

因为客观缘由,有时候咱们必须在中文操做系统下运行非 UNICODE 的日文软件,这时咱们能够采用一些工具,好比,南极星,AppLocale 等,暂时的模拟不一样的语言环境。

相关文章
相关标签/搜索