字节编码方式,字节 编码

  字节编码方式,字节 编码

  字符、字节和编码

  【原创文章,转载请保留或注明出处:http://www.regexlab.com/zh/encoding.htm】

  级别:中级

  本文介绍了文字和代码的发展过程,以及对相关概念的正确理解。举例说明了编码在一些实际应用中的实现方法。然后,本文讲述了一些常见的对文字和代码的误解,这些误解导致乱码的原因,以及消除乱码的方法。本文涵盖了“中文问题”和“乱码问题”。

  掌握编码问题的关键是正确理解相关概念,编码涉及的技术其实很简单。所以,读这篇文章的时候,需要慢慢读,多思考。

  介绍

  “字符符号与编码”是一个经常讨论的话题。即便如此,频繁出现的乱码依然困扰着大家。虽然我们有很多消除乱码的方法,但不一定了解这些方法的内在原理。有些乱码其实是底层代码本身的问题造成的。所以,不仅新手对字符编码很迷茫,一些底层开发人员也缺乏对字符编码的准确理解。

  美国国家标准协会

  (本地化)

  为了使计算机支持更多的语言,通常用0x80~0xFF范围内的2个字节来表示1个字符。例如,在中文操作系统中,汉字“中”存储在[0xD6,0xD0]中。

  不同的国家和地区制定了不同的标准,产生了GB2312、BIG5、JIS等各自的编码标准。这两个字节用来表示汉字的各种扩展编码方式,称为ANSI编码。在简体中文系统下,ANSI码代表GB2312码,而在日文操作系统下,ANSI码代表JIS码。

  不同的ANSI代码互不兼容。在国际间交换信息时,不可能在同一个ANSI编码文本中存储属于两种语言的单词。

  中文DOS,中文视窗95/98,日文视窗95/98

  采用双字节对字符进行编码

  (国际化)

  为了使国际信息交换更加方便,国际组织制定了UNICODE字符集,为各种语言中的每个字符设置一个统一的唯一编号,以满足跨语言、跨平台的文本转换和处理的要求。

  Windows NT/2000/XP、Linux、Java

  字符串在内存中的存储方法:

  在ASCII中,单字节字符串使用一个字节存储一个字符(SBCS)。例如,内存中的“Bob123”是:

  在使用ANSI编码支持多种语言的阶段,每个字符由一个或多个字节(MBCS)表示,因此以这种方式存储的字符也称为多字节字符。例如,“中文123”在中文Windows 95内存中有7个字节,每个中文字符有2个字节,每个英文和数字字符有1个字节:

  采用UNICODE后,计算机在存储字符串时,将存储UNICODE字符集中每个字符的序列号。目前计算机一般用2个字节(16位)来存储一个序列号(DBCS),所以这样存储的字符也叫宽字节字符。例如,在Windows 2000下,字符串“中文123”实际上在内存中存储了五个序列号:

  1.2字符、字节和字符串

  编码的关键是准确理解字符和字节的概念。这两个概念很容易混淆,所以我们在这里做个区分:

  计算机中存储数据的单位,8位二进制数,是一个非常特定的存储空间。

  0x01、0x45、0xFA、……

  美国国家标准学会

  字符串

  在内存中,如果“字符”以ANSI编码形式存在,一个字符可能用一个字节或多个字节来表示,那么我们把这个字符串称为ANSI字符串或多字节字符串。

  中文123

  (7字节)

  采用双字节对字符进行编码

  字符串

  在内存中,如果“字符”以UNICODE中的序列号存在,那么我们称之为UNICODE字符串或宽字节字符串。

  l 中文123

  (10字节)

  因为不同ANSI码规定的标准不一样,对于给定的多字节字符串,我们必须知道它采用的是哪种编码规则,才能知道它包含哪些“字符”。至于UNICODE字符串,无论在什么环境下,它所代表的“字符”的内容总是相同的。

  1.3字符集和编码

  在各个国家和地区制定的不同ANSI编码标准中,只规定了各自语言所要求的“字符”。例如,汉字标准(GB2312)没有规定如何存储韩文字符。这些ANSI编码标准包含两层含义:

  要使用哪些字符。也就是说,标准中会包括哪些汉字、字母、符号。包含“字符”的集合称为“字符集”。

  规定了每个“字符”是用一个字节存储还是多个字节存储,用哪些字节存储。这个规则叫做“编码”。

  国家和地区在制定编码标准时,一般会同时制定“字符集”和“编码”。因此,我们通常所说的“字符集”,如GB2312、GBK、JIS等。不仅有“字符集合”的意思,还包括“编码”的意思。

  UNICODE字符集包含各种语言中使用的所有字符。编码UNICODE字符集的标准有很多,比如UTF-8、UTF-7、UTF-16、Unicode Little、Unicode Big等。

  1.4常用代码介绍

  简单介绍一下常用的编码规则,为后面的章节做一个准备。这里,根据编码规则的特点,我们将所有的编码分为三类:

  ISO-8859-1

  最简单的编码规则是每个字节直接用作一个UNICODE字符。例如,当iso-8859-1将[0xD6,0xD0]转换成字符串时,会直接得到两个UNICODE字符[0x0d 6,0x0d 0],即 。

  反之,当UNICODE字符串被iso-8859-1转换为字节字符串时,只能正常转换0~255范围内的字符。

  GB2312,

  BIG5,

  移位_JIS,

  ISO-8859-2 ……

  当通过ANSI编码将UNICODE字符串转换为“字节字符串”时,UNICODE字符可以根据各自的编码规则转换为一个或多个字节。

  相反,在将一串字节转换成一个字符串时,也可以将多个字节转换成一个字符。例如,当GB2312将[0xD6,0xD0]转换成字符串时,会得到一个字符[0x4E2D],即中间字符。

  “ANSI编码”的特征:

  1.这些“ANSI编码标准”只能处理各自语言范围内的UNICODE字符。

  2.“Unicode字符”和“转换后的字节”之间的关系是人为规定的。

  UTF八号,

  UTF-16,UnicodeBig ……

  与ANSI编码类似,当字符串通过UNICODE编码转换为“字节字符串”时,UNICODE字符可能会转换为一个字节或多个字节。

  与“ANSI编码”不同:

  1.这些“UNICODE编码”可以处理所有的UNICODE字符。

  2.可以计算出“Unicode字符”和“转换后的字节”之间的关系。

  实际上,我们不需要去探究某个字符被每个代码编码到哪些字节中。我们只需要知道“编码”的概念就是把“字符”转换成“字节”。对于“UNICODE编码”,因为可以计算,所以可以在特殊场合了解某个“UNICODE编码”的规则。

  2.程序中字符和代码的实现

  2.1程序中的字符和字节

  在C和Java中,用于表示“字符”和“字节”的数据类型,以及编码方法:

  Java中的Char代表一个UNICODE字符(宽字节字符),而C中的char代表一个字节。

  MultiByteToWideChar()和WideCharToMultiByte()是Windows API函数。

  //ANSI字符串,内容长度为7个字节

  Char [20]=中文123 ;

  //内容长度为5 wchar_t(10字节)的UNICODE字符串

  wchar _ t wsz[20]=L /x4E2D/x 6587/x 0031/x 0032/x 0033 ;

  //在运行时设置当前的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);

  //根据setlocale指定的编码将UNICODE字符串转换为字节

  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()在运行时工作。

  //Java代码,直接用中文写的

  String=中文123 ;

  //获取长度5,因为它是5个字符

  system . out . println(string . length());

  字符串输入输出操作,字符和字节转换操作。在Java包java.io.*中,以“Stream”结尾的类一般用来操作“字节串”,以“Reader”和“Writer”结尾的类一般用来操作“串”。

  //将字符串转换为字节字符串。

  //根据GB2312获取字节(获取多字节字符串)

  byte[]bytes=string . getbytes( GB 2312 );

  //根据GB2312从字节获取UNICODE字符串

  string=新字符串(字节, GB 2312 );

  //要将字符串按照一定的编码写入文本文件,有两种方法:

  //第一种方法:用Stream类写出按照指定编码转换后的字节串。

  output stream OS=new file output stream( 1 . txt );

  os.write(字节);

  OS . close();

  //第二种方法:用指定的代码构造Writer来写字符串。

  writer ow=new output streamwriter(new file output stream( 2 . txt ), GB 2312 );

  ow.write(字符串);

  ow . close();

  /*最后的1.txt和2.txt是7个字节*/

  如果java的源代码与当前默认的ANSI代码不匹配,需要在编译时注明源代码。例如:

  以上,要注意区分源程序的编码和I/O操作的编码。前者在编译时工作,后者在运行时工作。

  在将“字节串”转换为“UNICODE串”时,例如读取文本文件或通过网络传输文本时,很容易将“字节串”简单地视为单字节串,采用每“字节”为“一个字符”的方法进行转换。

  实际上,在非英语环境中,“字节串”应该被用作ANSI字符串,并且应该采用适当的编码来获得UNICODE字符串。有可能“多个字节”可以得到“一个字符”。

  通常一直用英文开发的程序员容易产生这种误解。

  在DOS、Windows 98和其他非UNICODE环境中,字符串以ANSI编码的字节形式存在。这个字符串是以字节的形式存在的,需要知道正确使用的是哪种编码。这让我们形成了一种惯性思维:“字符串编码”。

  当支持UNICODE后,Java中的字符串存储为字符的“序列号”,而不是“一些编码的字节”,所以“字符串的编码”这个概念就不复存在了。只有当“字符串”和“字节串”进行转换,或者将一个“字节串”视为ANSI字符串时,才存在编码的概念。

  很多人都有这种误解。

  第一种误解往往是乱码的原因。第二种误解往往会让本来容易纠正的乱码问题变得更加复杂。

  "?"还有“?”中间的“B”代表Base64。如果是“Q”,表示引用-可打印。

  最后“?”用“?="之间的部分是由GB2312转换成字节串,再由Base64转换的标题内容。

  如果“传输代码”更改为“报价-可打印”,同样,如果标题内容为“中等”:

  如果阅读时出现乱码,通常是因为“字符编码”或“传输编码”指定不正确或不正确。例如,一些电子邮件组件发送标题为“medium”的电子邮件:

  4.纠正几个误解

  误解:“ISO-8859-1是国际代码?”

  不会。Iso-8859-1只是单字节字符集中最简单的一种,即“字节数”与“UNICODE字符数”一致的编码规则。当我们在不知道是哪种ANSI编码的情况下,想把一个“字节串”转换成“字符串”时,应该先把“每一个字节”临时转换成“一个字符”,这样不会造成信息丢失。然后,可以使用bytes=string . getbytes( ISO-8859-1 )的方法来恢复原始的字节字符串。

  误解:“Java里,怎么知道一个字符串的内部代码?”

  在Java中,字符串类java.lang.String处理UNICODE字符串,而不是ANSI字符串。我们只需要把字符串当作“抽象符号的字符串”。所以不存在字符串内码的问题。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: