精华内容
下载资源
问答
  • 1】编码基本知识最早的编码是iso8859-1,和ascii编码相似。但为了方便表示各种各样的语言,逐渐出现了很多标准编码,重要的有如下几个。① ISO8859-1 通常叫做Latin-1;② GB2312/GBK;③ unicode;④ UTF。【2】ISO...

    【1】big endian和little endian

    big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。如果将49写在前面,就是little endian。

    “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。

    我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。

    如UTF-16BE,UTF-16LE分别是UTF-16(big endian),UTF-16(little endian)。


    【2】字符编码、内码,汉字编码

    字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。

    GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。

    GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。

    从ASCII、GB2312到GBK,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK都属于双字节字符集 (DBCS)。

    2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说,GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字(Unicode码0x3400-0x4db5),一共收录了27484个汉字。

    CJK就是中日韩的意思。Unicode为了节省码位,将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 10646-1的中文版,相当于Unicode 1.1。

    GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。 例如:UCS的0x3400在GB18030中的编码应该是8139EF30,UCS的0x3401在GB18030中的编码应该是8139EF31。

    微软提供了GB18030的升级包,但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体:新宋体-18030,并不改变内码。Windows 的内码仍然是GBK。

    GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
    .
    对于任何字符编码,编码单元的顺序是由编码方案指定的,与endian无关。例如GBK的编码单元是字节,用两个字节表示一个汉字。 这两个字节的顺序是固定的,不受CPU字节序的影响。UTF-16的编码单元是word(双字节),word之间的顺序是编码方案指定的,word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。
    .
    GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。


    【2】几个常见的编码

    最早的编码是iso8859-1,和ASCII编码相似。但为了方便表示各种各样的语言,逐渐出现了很多标准编码,重要的有如下几个:

    ① ISO8859-1 通常叫做Latin-1;
    
    ② GB2312/GBK;
    
    ③ Unicode(简称UCS);
    
    ④ UTF,UTF是“UCS Transformation Format”的缩写。
    

    以"中文"两个字为例。

    经查表可以知道其GB2312编码是"d6d0 cec4",Unicode编码为"4e2d 6587",UTF编码就是"e4b8ad e69687"。

    注意,这两个字没有iso8859-1编码,但可以用iso8859-1编码来"表示"。

    8个二进制位表示一个字节,即 1Byte = 8bit;四个二进制位表示一个16进制位;三个二进制位对应一个8进制位。

    GB2312使用两个字节表示一个中文字符。UTF使用三个字节表示一个中文字符(统一使用的16进制)。

    几个编码简要介绍如下。

    ① ISO8859-1

    属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母a的编码为0x61=97。

    很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用 iso8859-1编码来表示。而且在很多协议上,默认使用该编码。

    比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为例,应该是"d6d0 cec4"两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:“d6 d0 ce c4”(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4 b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。


    ② GB2312/GBK

    这就是汉字的国标码,专门用来表示汉字,是双字节编码,而英文字母和iso8859-1一致(兼容iso8859-1编码)。

    其中gbk编码能够用来同时表示繁体字和简体字,而 gb2312只能表示简体字,gbk是兼容gb2312编码的。


    ③ Unicode

    简称UCS,这是最统一的编码,可以用来表示所有语言的字符,是定长双字节和定长四字节的编码(UCS2 & UCS4),包括英文字母在内。

    所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母a为"00 61"。

    需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用 unicode编码来处理的,比如java。


    ④ UTF

    考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表示。

    所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。

    另外,utf编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。

    注意,虽然说utf是为了使用更少的空间而使用的,但那只是相对于unicode编码来说,如果已经知道是汉字,则使用GB2312/GBK无疑是最节省的。

    不过另一方面,值得说明的是,虽然utf编码对汉字使用3个字节,但即使对于汉字网页,utf编码也会比unicode编码节省,因为网页中包含了很多的英文字符。


    【3】Unicode、UCS和UTF

    前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。

    Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。

    根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。

    在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。

    目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。

    UTF是“UCS Transformation Format”的缩写。

    UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。

    IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。

    目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。

    Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。

    微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。


    【4】UCS-2、UCS-4、BMP

    UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:

    UCS-2有216=65536个码位,UCS-4有231=2147483648个码位。

    UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。

    group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。

    将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。


    【5】UTF编码

    UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:

    UCS-2编码(16进制)UTF-8 字节流(二进制)
    0000 - 007F0xxxxxxx
    0080 - 07FF110xxxxx 10xxxxxx
    0800 - FFFF1110xxxx 10xxxxxx 10xxxxxx

    例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

    UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。


    【6】UTF的字节序和BOM

    UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

    Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:

    在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。

    这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

    Windows就是使用BOM来标记文本文件的编码方式的。


    【7】区位码、GB2312、内码和代码页

    “GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55区是一级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊”。(这个区位输入法可以自动识别16进制的GB2312和10进制的区位码,也就是说输入B0A1同样会得到“啊”。)

    内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。

    内码这个词汇,并没有什么官方的定义,代码页也只是微软这个公司的叫法。作为程序员,我们只要知道它们是什么东西,没有必要过多地考证这些名词。

    Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢?

    是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。

    答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。

    Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。

    有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:

    <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
    

    如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。

    再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码,我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根本没提到这一点。


    【8】&#||&#x又是什么鬼?

    有时候在很多页面地方看到诸如&#26368;&#26032;&#30332;这种格式,那么这种又是什么呢?首先看下图:
    在这里插入图片描述
    在这里插入图片描述
    上图来源地址:查看字符编码(UTF-8)

    可以发现第一张图当进行UTF-8对春节两字进行编码的时候,出现的不是E698A5 E88A82而是&#x6625;&#x8282;。好吧,这是转义序列。

    ① 什么是转义序列

    如下开头的字符是 HTML、XML 等 SGML 类语言的转义序列(escape sequence),它们不是「编码」。

    &#dddd;
    &#xhhhh;
    &#name;
    

    以 HTML 为例,这三种转义序列都称作 character reference:

    前两种是 numeric character reference(NCR),数字取值为目标字符的 Unicode code point。以&#开头的后接十进制数字,以&#x开头的后接十六进制数字。

    后一种是 character entity reference,后接预先定义的 entity(实体) 名称,而 entity 声明了自身指代的字符。

    从 HTML 4 开始,NCR 以 Unicode 为准,与文档编码无关。

    再引申一下,何为实体?为什么需要保留?

    在 HTML 中,某些字符是预留的。在 HTML 中不能使用小于号(<)和大于号(>),这是因为浏览器会误认为它们是标签。如果希望正确地显示预留字符,我们必须在 HTML 源代码中使用字符实体(character entities)。

    字符实体类似这样:

    &entity_name;
    
    或者
    
    &#entity_number;
    

    如下实例:

    显示结果描述实体名称实体编号
    空格&nbsp;&#160;
    <小于号&lt;&#60;
    >大于号&gt;&#62;
    &和号&amp;&#38;
    "引号&quot;&#34;
    撇号&apos; (IE不支持)&#39;
    分(cent)&cent;&#162;
    £镑(pound)&pound;&#163;
    ¥元(yen)&yen;&#165;
    欧元(euro)&euro;&#8364;
    §小节&sect;&#167;
    ©版权(copyright)&copy;&#169;
    ®注册商标&reg;&#174;
    商标&trade;&#8482;
    ×乘号&times;&#215;
    ÷除号&divide;&#247;

    更多详情参考:HTML 字符实体

    这也就是说,当浏览器看到&#x6625;&#x8282;会自动解析为春节—你可以称之为实体解码–参考How browsers work


    ② URL编码解码与UTF-8编码解码

    URL编码即使用某种编码方式进行编码然后使用%拼接起来,如春节使用UTF-8编码方式进行编码结果为:%e6%98%a5%e8%8a%82

    再看上面的图UTF-8编码为E698A5 E88A82,即%拼接起来!

    解码只是编码逆过程,无需多言!


    【9】不同浏览器对URL上面中文编码

    ① 中文作为参数

    格式如下:https://www.baidu.com/s?wd=春节

    • Chrome浏览器

    在这里插入图片描述

    • FireFox

    在这里插入图片描述

    • 360安全浏览器
      在这里插入图片描述
    展开全文
  • 是先将str按 iso-8859-1进行编码 然后再按UTF-8进行解码 还是将str按 iso-8859-1进行解码 然后再按UTF-8进行编码?
  • 关于ISO-8859-1 的相关知识

    千次阅读 2014-12-29 15:38:20
    ISO/IEC 8859-1 ISO 8859-1,正式编号为ISO/IEC 8859-1:1998,又称Latin-1或“西欧语言”,是国际标准化组织内ISO/IEC 8859的第一个8位字符集。它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及...

    ISO/IEC 8859-1


    ISO 8859-1,正式编号为 ISO/IEC 8859-1:1998,又称 Latin-1或“ 西欧语言”,是 国际标准化组织ISO/IEC 8859的第一个8位 字符集。它以 ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个 字母符号,藉以供使用 附加符号拉丁字母语言使用。曾推出过 ISO 8859-1:1987 版。

    此字符集支持部分于欧洲使用的语言,包括阿尔巴尼亚语巴斯克语布列塔尼语加泰罗尼亚语丹麦语荷兰语法罗语弗里西语加利西亚语德语格陵兰语冰岛语爱尔兰盖尔语意大利语拉丁语卢森堡语挪威语葡萄牙语里托罗曼斯语苏格兰盖尔语西班牙语瑞典语

    英语虽然没有重音字母,但仍会标明为ISO/IEC 8859-1编码。除此之外,欧洲以外的部分语言,如南非荷兰语斯瓦希里语印尼语马来语、菲律宾他加洛语等也可使用ISO/IEC 8859-1编码。

    法语芬兰语本来也使用ISO/IEC 8859-1来表示。但因它没有法语使用的 œŒŸ 三个字母及芬兰语使用的 ŠšŽž ,故于1998年被ISO/IEC 8859-15所取代。(ISO 8859-15同时加入了欧元符号)

    ISO/IEC 8859-1
     x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
    0x 
    1x 
    2xSP!"#$%&'()*+,-./
    3x0123456789:;<=>?
    4x@ABCDEFGHIJKLMNO
    5xPQRSTUVWXYZ[\]^_
    6x`abcdefghijklmno
    7xpqrstuvwxyz{|}~ 
    8x 
    9x 
    AxNBSP¡¢£¤¥¦§¨©ª«¬SHY®¯
    Bx°±²³´µ·¸¹º»¼½¾¿
    CxÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ
    DxÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
    Exàáâãäåæçèéêëìíîï
    Fxðñòóôõö÷øùúûüýþÿ

    在上表中,0x20是空格、0xA0是不换行空格、0xAD是选择性连接号

    0x00-0x1F、0x7F、0x80-0x9F在此字符集中未有定义。(控制字符是由ISO/IEC 6429定义)。

    参看[编辑]

    外部链接[编辑]


    展开全文
  • jsp 中 默认iso-8859-1 改成UTF-8 解决方案 丰富的快捷键 本人自己在学习web 开发中的时候 发现创建的jsp页面默认的编号都为 ISO-8859-1的编码 每次都需要手动修改为UTF-8很麻烦 。找到了一个一劳永逸的方法...

    jsp 中 默认iso-8859-1 改成UTF-8 解决方案

    • 丰富的快捷键

    本人自己在学习web 开发中的时候 发现创建的jsp页面默认的编号都为 ISO-8859-1的编码 每次都需要手动修改为UTF-8很麻烦 。找到了一个一劳永逸的方法。上图:

    编码为ISO-8859-1时 ,中文显示为这个样子。

    这里写图片描述

    eclipse创建默认为 ISO-8859-1:

    这里写图片描述

    修改方案:找到window-Preferences 如图:

    这里写图片描述

    修改:

    这里写图片描述

    再次创建JSP 页面实验效果:

    这里写图片描述

    OK!

    展开全文
  • 转载自网络 0 如果你是纯小白,那么请先阅读我的编码总结,对编码有了最基础的...1 我们从一个Java乱码的实例来抛出这个问题。 实例场景: 要求使用Java读取一个 GBK 格式的文件,使用BufferedReader的readLi...

    转载自网络

    0

    如果你是纯小白,那么请先阅读我的编码总结,对编码有了最基础的认识后,进行本篇文章的阅读,我可以保证你可以对Java这块会出现的编码的问题都可以自行一一解决,而且不需要借助google或者百度,全部都可以自己思考解决

    1

    我们从一个Java乱码的实例来抛出这个问题。

    实例场景:

    要求使用Java读取一个 GBK 格式的文件,使用BufferedReader的readLine读取后发现控制台输出乱码

    GBK文件内容如下图所示

    在使用Java程序读取之前,我们先来分析这个文件的二进制内容,这里先向再看这篇文章的人特别强调一点,那就是分析乱码的时候,请务必从二进制出发!虽然你会发现控制台,数据库,文件的内容输出根据编码情况变来变去的,搞的你天花乱坠,但是二进制文件是万变不离其宗的!

    我们使用linux的hexdump来获取二进制数据

    1
    2
    3
    
    zazalu@zazalu-ThinkPad-E480:~/app/JavaProjectWithIDEA/MySpider/src/main/resources$ hd gbktest.txt 
    00000000  49 61 6d be fd c9 bd                              |Iam....|
    00000007
    

     

    得到了49 61 6d be fd c9 bd,这是我们这个文件的十六进制表示,随后我们逐个转换为二进制如下(不足8位的我在最前面补0了,8bit = 1byte)

    1
    2
    
    49       61       6d       be       fd       c9       bd 
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
    

     

    [注意]: 可以对照这张码表来看看hexdump程序对不对,我自己对照过了,befd,山是c9bd,没问题。前面的英文直接对照ASCII码表即可,都吻合!

    到这里我们就知道了Iam君山这句话的GBK二进制数据表示就是01001001 01100001 01101101 10111110 11111101 11001001 10111101

    2

    接下来我们使用BufferedReader的readLine来读取这个片段,来重现一种乱码出现的情况!

    java程序如下:

    1
    2
    3
    4
    5
    6
    7
    
    File file = new File("gbktest.txt");
    try(BufferedReader bufferedReader = new BufferedReader(new FileReader(file))){
        String s = "";
        while ( (s = bufferedReader.readLine()) != null ){
            System.out.println(s);//控制台输出 Iam��ɽ
        }
    }
    

     

    这个程序读取gbktest.txt的内容并且打印到了标准输出上,也就是控制台上。

    为了方便后续的讨论,我将这个程序在执行过程中存在的不同版本的字节流命名如下:

    1. gbktest.txt本身的字节流,一串GBK字节流,二进制表示如下:01001001 01100001 01101101 10111110 11111101 11001001 10111101,我们称它为GBK流

    2. BufferedReader使用readLine获得到了一串字符串据s,Java的字符串内部使用Unicode编码(内码)存储,我们称它为Unicode流

    3. 控制台获取s的内容打印到标准输出上的字节流,由于标准输出我这边是UTF-8编码,所以我们称它为UTF-8流

    为了理解第三节的讨论,你必须要对下面的知识点有清醒的认识

    1. java的String内部使用16位空间存储字符,也就是Unicode字符
    2. UTF-8不会一口气转换成GBK,中间必须使用Unicode字符来过渡

    3

    接下来我一步步来透彻的讲解这些代码发生了什么

    1. bufferedReader.readLine(),bufferedReader内部默认使用UTF-8编码来读取,比对Unicode表来转换字节到字符(网上可以查到),使用read,一次只读取一个字节,最后拼成一串char数组返回,所以按照它的读取规则,我们的GBK流会被如下解析
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    	
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
      |
      V
    对照UTF-8表,发现是I,然后在Unicode的总表上查阅I字符怎么表示,最终填入char[0] = 'U+0049' 也就是'I'
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
                |
                V
    对照UTF-8表,发现是a,然后在Unicode的总表上查阅a字符怎么表示,最终填入char[1] = 'U+0061' 也就是'a'
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
                         |
                         V
        对照UTF-8表,发现是m,然后在Unicode的总表上查阅m字符怎么表示,最终填入char[2] = 'U+006D' 也就是'm'
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
                                  |
                                  V
        对照UTF-8表,发现不对,UTF-8码表规则不允许用10开头!(UTF-8码表规则在下面附上,请自己比对)
        针对这种情况,转换规则里存在一种机制,会把不允许的字节全部自动变成一个叫"[置换字符](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character"的东西!UTF-8的置换字符为�,在Unicode总表上查出来,所以char[3] = 'U+FFFD' 也就是'�'
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
                                           |
                                           V
        对照UTF-8表,和上面情况一样,发现不允许11111开头!所以char[4] = 'U+FFFD' 也就是'�'
    
    01001001 01100001 01101101 10111110 11111101 11001001 10111101
                                                    |       |
                                                    V       V
        对照UTF-8表,发现是第一个字节110开头第二个字节10开头,符合utf-8双字节表示的情况!所以一口气读取2个字节,转换成Unicode码为`U+027D`.所以char[5] = 'U+027D' 也就是'ɽ'
    1
    2
    3
    4
    5
    6
    7
    
    	
    
    Unicode符号范围 | UTF-8编码方式
    (十六进制) | (二进制)
    --------------------+---------------------------------------------
    0000 0000-0000 007F | 0xxxxxxx
    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    所以综上所述,我们的String s的内容的Unicode码为U+0049 U+0061 U+006D U+FFFD U+FFFD U+027D

    这串Unicode的码就是我们读取后java底层保存的真正内容!也就是Iam��ɽ

    从这里我们其实已经得到了答案,那就是BUfferedReader的这种读取方式直接就把我们的源文件流内容完全变掉了,变成了一串新的东西,错就错在它读取的时候按照UTF-8的规则来转换,把本来应该双字节双字节为单位读取的二进制数据,’翻译’成了另一个样子!

    4

    那么我们现在的关键问题是,这串Unicode码能不能通过简单的方式转换为没有乱码的样子?

    在网上流行的方法会让你这么做:

    1
    
    	
    
    System.out.println(new String(s.getBytes("ISO-8859-1"),"GBK"));//这个在一些例子里可以成功实现快速转换,但是在我们这个例子是行不通的!为什么?下面解释

    类比到我们这个例子里,也许就是这么做

    1
    
    	
    
    System.out.println(new String(s.getBytes("UTF-8"),"GBK"));//输出 Iam锟斤拷山

    可以看到输出变成了’Iam锟斤拷山’,和我们本来的意思不一样了!所以它虽然看上去变成中文了,但是实际上依旧是乱码!下面依旧一步步来给你解释为什么会这样!

    首先s.getBytes("UTF-8"),这个代码的意思是将字符串s的Unicode码(内码)转换为UTF-8码,返回一个byte[],如下

    1
    2
    3
    4
    5
    6
    
    	
    
    由于UTF-8很多是三字节的,用二进制表示会太长不已阅读,这里就用十六进制表示下,你可以自己转
    Unicode:
    U+0049 U+0061 U+006D U+FFFD U+FFFD U+027D
    
    使用UTF-8码表翻译Unicode,得到如下二进制数据:
    01001001 01100001 11001001 11101111 10111111 10111101 11101111 10111111 10111101 11001001 10111101

    所以我们得到的byte数组内容就是01001001 01100001 11001001 11101111 10111111 10111101 11101111 10111111 10111101 11001001 10111101

    其次new String(s.getBytes("UTF-8"),"GBK")的第二个参数会用GBK的字节读取规则来转换这个byte[],把它变成Unicode码最后存在字符串s中,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    
    	
    
    byte[]:
    01001001 01100001 11001001 11101111 10111111 10111101 11101111 10111111 10111101 11001001 10111101
    
    先转换为GBK
    0049 0061 006D EFBF BDEF BFBD C9BD
    
    然后转换为Unicode存储至String中
    I    a    m    锟   斤   拷   山

    所以我们通过这个方式转换后,得到了输出为Iam锟斤拷山

    [小节]: 所以单纯使用System.out.println(new String(s.getBytes("UTF-8"),"GBK"));这种方式来教别人转换是有点误导向的。这种方式的本来意思就是用前者的编码将Unicode转换为UTF-8格式的Byte[],然后再用GBK的码表把这个Byte数组转换为Unicode! 这个过程的作用简直就是搞笑!这个代码也是让我哭笑不得。但是也有些情况用这个代码可以实现快速的转换,比如System.out.println(new String(s.getBytes("ISO-8859-1"),"GBK"));,这是因为ISO-8859-1是8位的编码格式,它正好把一个中文对半拆分成了2个字符,由于是对半的,所以转换为byte数组的时候,结果是一样的,就可以歪打正着的还原回去了!真的哭笑不得

    5

    本次的编码课题就到此结束了,有问题的小伙伴可以在下面评论,看不多评论是因为你没有使用科学上网工具

    参考:GBK码表
    参考:Unicode字符对应所有的编码如何表示的网站,很好用
    参考:在线进制转换工具
    参考:Unicode码表
    参考:UTF-8码表

    展开全文
  • 1、加入fastjson maven 依赖包 <groupId>com.alibaba</groupId> <artifactId>fastjson <version>1.2.30 </dependency>2、springbo
  • 通过输出s_gbk、s_utf8和s_iso88591,会发现s_gbk和s_utf8都是"中",而只有s_iso88591是一个不被识别的字符(可以理解为乱码),为什么使用ISO8859-1编码再组合之后,无法还原"中"字?原因很简单,因为ISO8859-1编码的...
  • UTF-8 GBK ISO-8859-1编码

    千次阅读 2013-08-07 14:45:00
    iSO8859-1,通常叫做Latin-1。Latin-1包括了书写所有西方欧洲语言不可缺少的附加字符。 而gb2312是标准中文字符集。 UTF-8 是 UNICODE 的一种变长字符编码,即 RFC 3629。简单的说——大字符集。可以解决多种语言...
  • charset=iso-8859-1

    2013-06-19 09:51:00
    求解:charset="ISO-8859-1"是什么意思 其实我也不大懂,然后就去翻阅了下资料。科普开始: 网页制作中遇到的编码,乱码问题之一:charset=iso-8859-1和charset=gb2312有什么不同? charset=iso-8859-1是西欧的...
  • 目录 1.1. ISO-8859-1 通常叫做Latin-11.2. GB2312/GBK1.3 unicode1.4 UTF2.1Unicode与各编码之间的直接转换2.2Unicode与各编码之间的交叉转换2.3编码过程中错误诊断参考3.1 getBytes(char...
  • 我有一个用ISO8859-15编码的阿拉伯语文件。如何将其转换为utf8?我用过iconv,但它对我不起作用。iconv -f ISO-8859-15 -t UTF-8 Myfile.txt我想附加文件,但不知道怎么做。iconv是否打印错误消息,或转换错误?...
  • 字符集与编码八之ASCII和ISO-8859-1

    千次阅读 2017-04-19 10:07:42
    ISO-8859-1又称Latin-1,是一个8位单字节字符集,它把ASCII的最高位也利用起来,并兼容了ASCII,新增的理论空间是128,但它并没有完全用完:(截图来自 http://zh.wikipedia.org/wiki/ISO/IEC_8859-1 ) 可以...
  • 这个编码是在Windows(绑架ANSI)进入欧洲(ISO组织可能老巢在欧洲)时候一起搞出来的,标准编号叫做ANSI/ISO-8859-1-1987,简称ISO-8859-1。编码全称叫做 「American National Standard for Information Processing...
  • Tomcat默认全部都是用ISO-8859-1编码,不管你页面用什么显示,Tomcat最终还是会替你将所有字符转做ISO-8859-1.那么,当在另目标页面再用GBK翻译时就会将本来错的编码翻译成GBK的编码,这时的文字会乱码. 所以需要先...
  • MyEclipse中新建一个jsp文件,如果输入中文保存时就会提示错误: Save could not be completed. Reason: some characters cannot be mapped using “ISO-8859-1“ character enco
  • java Servlet里的byte temp[]=s.getBytes(“ISO-8859-1”);是什么意思? 2012-07-17 11:46javaxwow | 分类:JAVA相关 | 浏览721次 在用java Servlet做html的后台 上面有段代码是说处理中文字符串的函数 ...
  • 在web开发的时候,用到的web.xml文件的第一行中前面我知道是版本号,可是后面encoding="ISO-8859-1编码集我就不打知道有多少编码集,就在网上搜了一下,感觉写的不错,特转来以后学习时查看。 转自:...
  • unicode和ISO 8859-1

    2011-03-12 09:06:12
    由于ISO8859-1是单字节编码,所以每个字节被按照原样 转换为String,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!所以那句经典的new String(text.getBytes("ISO-8859-1")...
  • 刚刚在保存编写好的jsp文件时出现"Some characters cannot ...'ISO-8859-1' character encoding. Either changethe encoding or remove the characters which are not supported by the 'ISO-8859-1' charact...
  • which case ISO-8859-1 will be used. 那么也就意味着,从tomcat8.0开始,get请求中的中文参数,不需要特殊处理了。而如果是tomcat8之前的项目要迁移到tomcat8上面来,则也需要特殊注意这个问题,可能要...
  • 然后提示出错: java.io.CharConversionException: Not an ISO 8859-1 character: [留] 意思为:字符转换异常 其实ISO-8859-1为单字节编码,主要用于表示英文字符,无法正确表示中文 所以我们想正确输出,...
  • 如果你是中文的操作系统,就会使GBK,如果是英文的就会是ISO-8859-1 Unicode UNICODE原生的编码方式 Unicode big endian 另一个 UNICODE编码方式 UTF-8 最常见的UTF-8编码方式,数字和字母用一个字节, 汉字用...
  • unicode、gbk、iso8859-1、ascii、utf-8、utf-16、utf-32,这几个名词,代表什么?它们之间有什么关系? utf8没有字节序、utf-16、utf-32有字节序,为什么?大端序、小端序又指的是什么? 解答 为了解答以上问题,...
  • 本文主要包括以下几个方面:编码基本知识,java,系统软件,url,工具软件等。  在下面的描述中,将以"中文"两个字为...注意,这两个字没有iso8859-1编码,但可以用iso8859-1编码来"表示"。 2. 编码基本知识
  • 转自:...String filename=request.getParameter(“filename”); filename=new String(filename.getBytes(“iso8859-1”),”utf-8”); 请解释第二句?用意是什么?为什么这样
  • 第一部分之规律:  1.0x00 ~ 0x7F (包含00与7F)三种编码没有区别.  2.0x80 ~ 0xBF(包含80与BF)  其中:ISO8859-1 = (Unicode char)Unicode = (unsigned char) (utf-8[1]) .  3.0xC0 ~ 0xFF(包含C
  • ISO/IEC 8859-1 拉丁语系1-西欧 1987/1998 ISO/IEC 8859-2 拉丁语系2-中欧 1987/1999 ISO/IEC 8859-3 拉丁语系3-南欧 1988/1999 ISO/IEC 8859-4 拉丁语系4-北欧 1987/1998 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,404
精华内容 6,961
关键字:

iso-8859-1是什么意思