java的编码问题:byte、char、string 您所在的位置:网站首页 移动怎么取消和留言来电提醒 java的编码问题:byte、char、string

java的编码问题:byte、char、string

2024-06-15 21:39| 来源: 网络整理| 查看: 265

关注点

要解决几个问题:

java中,出现乱码的原因?解决的基本原理?(下面两个是具体问题) java中,对字符串String的读取,出现乱码的解决办法? java中,对文件File的读写,出现乱码的解决办法? 补充知识: 编码:几种编码方式之间的差异:ASCII、UTF-8、GB2312、GBK、ISO8859-1; 文件:java下,文件读写,效率和编码; 数组:java下,基础类型(char、byte、int)数组的定义和使用 乱码

java中,对String进行编码、解码的基本过程,见上图;简要解释一下:

编码解码,通过字符集映射为,然后再依照映射为的过程;StringcharsetInbyte[]byte[]charsetOutString String是;char[] 编码解码,本质是:通过字符集映射为,然后再依照映射为的过程;char[]charsetInbyte[]byte[]charsetOutchar[] charset字符集,对应编码方式,通俗说,就是一张映射表,既可以将char映射为byte,也可以将byte映射为char。 编码的基本解释

几点:

编码方式,字符集,都是针对 来说的,即,针对机器上存储的原始二进制字节;byte[] 无论什么编码方式,在 上是统一的;char[] java 中 String 的编解码

java 中 File 的编解码

java 读写 file,有两种方式:

按照 byte 读取,此为文件的镜像,不涉及文件编码问题; 按照 char 读取,此过程中,涉及两处编码字符集:读取文件、写入文件; 何时出现乱码?如何解决?

依照前文分析的编码解码基本过程,产生乱码,本质是输入的与最终输出的不一致,有如下几种:char[]char[]

char在字符集中不存在,无法找出对应的,只能强制转换为某个默认的;charsetInbyte[]byte[] charsetIn与不一致;charsetOut

因此,解决乱码问题的思路也是清晰的:

选用合适的字符集,当然,尽可能大的字符集最好,不过,能够满足需求即可;charsetIncharsetIn 保证与字符集完全一致;charsetIncharsetOut

思考:上数的讨论都是基于原始输入为正常String,没有携带乱码,但有一种情况:原始中自身就携带乱码,此时,如何处理? RE:特别是,针对ASCII中的控制字符(contorl code),例如:、,此类字符,会产生转码后的换行问题,如何提前对此类字符进行处理?具体需要认真分析此类字符产生的原因,初步分析,两类场景:StringString^M^C

control code:当作普通字符,不进行换行、tab等特殊操作,可以直接替换为特征字符,例如:,替换为^MCarriage return; control code:传统用法,都是翻译成其特殊含义对应的操作吗?例如:,就翻译为换行的操作;^M

疑问:ASCII的control code是如何输入到File中的?可以直接通过键盘输入吗?还有哪些场景?

UPDATE 2015-03-18

中文编码问题:

通常读取文件内容,两类:按照byte读取、按照char读取; 按照char方式读取会出现乱码; char方式在哪个阶段出现乱码?byte转换为char的时候。 因此,图片中展示的基本原理,而在实际情况中,是byte–char–byte–char,根据发生地点来划分的?本地的byte–char–byte,然后以byte方式发送出去,对端再将byte–char。

参考下文的示例代码即可。

字符串的编解码

几点:

获取对应的:;Stringbyte[]String.getBytes(charset) 输出内容:byte[]Arrays.toString(byte[]) 将编码为:byte[]Stringnew String(byte[], charset);

测试代码如下:

package com.github.ningg; import java.io.UnsupportedEncodingException; import java.util.Arrays; public class ByteAndString { public static void main(String[] args) throws UnsupportedEncodingException{ String inputStr = "你好, Hello"; String charset = "UTF-8"; String outputStr = null; byte[] firstLayerByteArray = null; // default firstLayerByteArray = inputStr.getBytes(); outputStr = new String(firstLayerByteArray); System.out.println("default:"); System.out.println(Arrays.toString(firstLayerByteArray)); System.out.println(outputStr); System.out.println("-------------"); // UTF-8 charset = "UTF-8"; firstLayerByteArray = inputStr.getBytes(charset); outputStr = new String(firstLayerByteArray, charset); System.out.println(charset + ":"); System.out.println(Arrays.toString(firstLayerByteArray)); System.out.println(outputStr); System.out.println("-------------"); // GBK charset = "GBK"; firstLayerByteArray = inputStr.getBytes(charset); outputStr = new String(firstLayerByteArray, charset); System.out.println(charset + ":"); System.out.println(Arrays.toString(firstLayerByteArray)); System.out.println(outputStr); System.out.println("-------------"); // GB2312 charset = "GB2312"; firstLayerByteArray = inputStr.getBytes(charset); outputStr = new String(firstLayerByteArray, charset); System.out.println(charset + ":"); System.out.println(Arrays.toString(firstLayerByteArray)); System.out.println(outputStr); System.out.println("-------------"); // ISO-8859-1 charset = "ISO-8859-1"; firstLayerByteArray = inputStr.getBytes(charset); outputStr = new String(firstLayerByteArray, charset); System.out.println(charset + ":"); System.out.println(Arrays.toString(firstLayerByteArray)); System.out.println(outputStr); System.out.println("-------------"); } }

输出内容,如下:

default: [-28, -67, -96, -27, -91, -67, 44, 32, 72, 101, 108, 108, 111] 你好, Hello ------------- UTF-8: [-28, -67, -96, -27, -91, -67, 44, 32, 72, 101, 108, 108, 111] 你好, Hello ------------- GBK: [-60, -29, -70, -61, 44, 32, 72, 101, 108, 108, 111] 你好, Hello ------------- GB2312: [-60, -29, -70, -61, 44, 32, 72, 101, 108, 108, 111] 你好, Hello ------------- ISO-8859-1: [63, 63, 44, 32, 72, 101, 108, 108, 111] ??, Hello -------------

备注:之前写过,一个java中数组的博文,可以参考一下;

文件内容的编解码

下面示例代码,简要说明,以某一指定charsetIn读取文件,再以指定charsetOut写入文件即可;完整示例代码如下:

package com.github.ningg; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class FileAndCharset { public static void main(String[] args) throws IOException { String srcFile = "E:/1.log"; String destFile = "E:/1utf8.log"; String charsetIn = "GBK"; String charsetOut = "UTF-8"; FileInputStream fileInputStream = new FileInputStream(srcFile); FileOutputStream fileOutputStream = new FileOutputStream(destFile); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, charsetIn); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charsetOut); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); String singleLine = null; while( (singleLine = bufferedReader.readLine()) != null ){ bufferedWriter.write(singleLine); bufferedWriter.newLine(); } bufferedWriter.flush(); bufferedWriter.close(); bufferedReader.close(); } }

备注:之前写过一篇Java读写File的博客,可以参考一下。

编码方式

思考:几个小疑问:

什么是字符集(charset)?是一个char与byte之间的映射表吗? 定长码、变长码; 存储编码、传输编码;

几种编码方式之间的联系:

ASCII

ASCII:American Standard Code for Information Interchange(信息交换,美国标准码);简单说几点:

US-ASCII:原始的ASCII

1963年 7-bit(128个字符) letters、numerals、symbols、device control code fixed-length

但是,计算机中一个byte有8bit,因此,就打起了剩余1-bit的主意:

ISO 8859,针对8-bit ASCII extensions部分,定义的规范; ISO 8859-1,又称,,针对西欧常用语言(Western European Language)的拓展;ISO Latin 1 ISO 8859-2,东欧常用语言(Eastern European Language)的扩展;

一个byte,8 bit,并不能满足所有character encodings的需要,因此出现了multi-byte character encodings,整体上两类:

Extended ASCII:多字节编码时,ASCII已经表示的字符,仍用ASCII的8-bit表示: 保留来标识原始的ASCII;0x00-0x7F 启用来标识多字节的字符;0x80--0xFF 典型代表:UTF-8 非 Extended ASCII:不保留单字节的ASCII表示; GB2312

整体上说一下GB2312、GBK、GB18030几种编码之间的关系:

GB2312,固定码,2 byte: 简体汉字 原有的拉丁字母、数字、符号,编码进去,称为;全角 原有US-ASCII(7 bit)的拉丁字母、数字、符号,称为;半角 GBK 繁体字 生僻字 当前软件,支持GBK的较多,而相对GB18030更为普及 GB18030 少数民族字符 生僻字

备注:,(国标);GBGuojia Biaozhun

几点:

GB2312,为GBK、GB18030的子集; GBK\GB18030,相对于GB2312,在罕见字、繁体字上,有增强; GB2312,包含个中文字符;6763 GB2312,对应一个的表格,每个空格(codepoint),都是two-byte();94x942 bytes EUC-CN(Extended Unix Code),GB2312标准的常用实现,同时兼容ASCII(,最高位为); 0x00-0X7F0 1st byte:;(最高位为)0xA1-0xF71 2nd byte:;0xA1-0xFE

疑问:,官网提到,其是标准的实现,多字节编码,最大可包含`G1G2G3G0ISO-646US-ASCII`),但GB2312为 2 byte的定长字符集,这可如何是好?EUCISO-2022G0\\,4个字节;其中通常兼容标准(

RE:

charset,字符集,标识char与byte之间的映射关系; GB2312标准,ISO-2022标准,是标准,而不是char与byte之间映射的字符集; GB2312标准,只说明了原理:用2byte标识,而没有设定每个char对应的byte一定要为多少,具体实现方式可以调整; ISO-2022标准,说明原理:最多可以利用 4 byte来标识字符; EUC-CN(Extended Unix Code),是具体实现,满足GB2312和ISO-2022两个标准;(有时,EUC-CN为GB18030标准和ISO-2022标准的具体实现)

相对于,效率更高,几点:UTF-8GB2312

没有保留bits,用于标识 3 byte 或 4 byte; 没有保留bit,用于detect tailing bytes;(什么含义?需要检测末尾byte吗?之前Huffman coding,变长编码,不需要额外的字节,来标识tailing bytes)

思考:

GB2312中包含ASCII中已经包含的英文字符吗? GB2312标准中,不包含ASCII;但其实现方式:EUC-CN,除了支持GB2312(2 byte)之外,还兼容ASCII(1 byte); 若包含ASCII的英文字符,那英文字符占几个字节? EUC-CN:实现GB2312的字符,字节高位为;ASCII的字符,字节高位为;10 EUC-CN:为变长编码,既包含 2 byte的GB2312字符,也包含 1 byte的ASCII字符;

建议:推荐使用大字符集,GBK、GB18030;

GBK

几点:

GBK:Guojia Biaozhun Kuozhan; 简体中文、繁体中文; 字符集: GB2312 ASCII GB18030

几点:

同时兼容和两个标准;GB2312GBK 是Unicode的一种?支持简体字、繁体字; Unicode Transformation Format(an encoding of all Unicode code points); 字符集: ASCII GB2312 GBK 具体编码实现: 1 byte 2 byte 4 byte

更多阅读:

信息交换用汉字编码字符集·基本集 Unicode

几点:

目标:通用字符集; 包含100,000+的字符; 前256个字符,保留给ISO-8859-1,基本的拉丁字母(西欧语系的字母);(基本的拉丁字母用途太广泛) 具体实现: UTF-8 UTF-16 UTF-32 UTF-8

几点:

基于Unicode标准的一个具体实现方式; 完全兼容编码方式;(第一字节,兼容ASCII)ISO-8859-1 变长码,每个字符使用1-3个byte编码,并利用首位或进行识别;01 Unicode的实现方式称为(Unicode Transformation Format,简称UTF)Unicode转换格式 补充:UTF-8编码方式,中文占 3 byte;

具体:

128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(Unicode范围由U+0080至U+07FF)。 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。 其他极少使用的 Unicode 辅助平面的字符使用四至六字节编码(Unicode范围由U+10000至U+1FFFFF使用四字节,Unicode范围由U+200000至U+3FFFFFF使用五字节,Unicode范围由U+4000000至U+7FFFFFFF使用六字节)

网络上数据编码的演进趋势:(网络上传输的byte,是char通过哪种字符集映射过来的?)

附录 Huffman coding

Huffman coding,霍夫曼编码:

基本原则:符号的编码长度,按照符号出现概率大小,逆向排序; 最优二叉树:带权路径长度最小的二叉树; 常用语数据压缩,无损耗压缩,是一致性编码(“熵编码”); 具体操作:按概率大小排序,最小的两个概率相加,迭代到总概率;1

更多阅读,参考Huffman coding。

字母体系

世界三大字母体系:

拉丁字母 斯拉夫字母 阿拉伯字母

拉丁字母(下图):

备注:中国的汉语拼音方案,也是以拉丁字母为基础的。

斯拉夫字母(下图):

阿拉伯字母(下图):

utf_unicode_ci和utf8_general_ci

在数据库系统MySQL中有多种字符集,其中utf8_unicode_ci和utf8_general_ci是最常用的,但是utf8_general_ci对某些语言的支持有一些小问题,如果可以接受,那最好使用utf8_general_ci,因为它速度快。否则,请使用较为精确的utf8_unicode_ci,不过速度会慢一些。

多语言无差错,首选“utf_unicode_ci”。

全角 & 半角

信息交换用汉字编码字符集·基本集对应的是GB2312,其中也指定了拉丁字母(英文字母)对应的 2 byte编码,这个只有在情况下,才输入GB2312字符集中的拉丁字母,而时,输入的为ASCII下的拉丁字母编码;全角半角

备注:上述查询文档中,字符对应的二进制表示时,借助工具中的。UltraEdit十六进制模式

关于`半角`,几点:全角

输入汉字,全角、半角,没有区别,对应的GB2312编码完全一致,2 byte; 输入英文字母、符号、数字,有区别: 全角:使用GB2312中对应的编码,占用 2 byte; 半角:使用ASCII中编码,占用 1 byte; 补充:全角存在的意义是,方便显示的整齐和美观; 参考来源 ASCII wiki Extended ASCII wiki Character Encoding wiki Extended Unix Code wiki Huffman coding Unicode wiki UTF-8 wiki Unicode wiki(中文) ISO-8859-1 wiki(中文) UTF-8 wiki(中文) 信息交换用汉字编码字符集·基本集 字符编码笔记:ASCII,Unicode和UTF-8


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有