unicode,UTF8深入解说

Java与编码问题串讲之二–如何理解java采用Unicode编码

https://blog.csdn.net/shijinupc/article/details/7679945

Java开发者必须牢记:Java中字符仅以一种形式存在,那就是Unicode(不选择任何特定的编码,直接使用他们在字符集中的编号,这是统一的唯一方法)。由于java采用unicode编码,char java中占2个字节。2个字节(16位)来表示一个字符。

    这里的Java是指在JVM中、在内存中、在代码里声明的每一个charString类型的变量中。

public class UnicodeTest {
public static void main(String[] args) throws UnsupportedEncodingException {
System.out.println(System.getProperty(“file.encoding”));
char han = ‘永’;
System.out.format(“%x”, (short) han);// 对第二个参数(短整型)格式化为十六进制输出,0x开头
System.out.println(“”);
// 输出6c38
char han1 = 0x6c38;// 0x5140;// 16*3+8 + 6*16*16*16 + 12*16*16;//0x6c38;
System.out.println(han1);
System.out.println((short) han1);
System.out.println(“————————————-“);
// String.getBytes(“aaa”);

String str = “永”;
byte b[] = null;
// UTF8汉字占三个字节
b = str.getBytes(“UTF-8”);
System.out.println(“UTF8编码格式下,一个汉字占” + b.length + “字节: “);
for (byte i : b) {
System.out.format(“%x “, i);
}
System.out.println();
// 前面多的两个是字序标记
b = str.getBytes(“UTF-16”);
System.out.println(“UTF16编码格式下,一个汉字占” + (b.length – 2) + “字节: “);
for (byte i : b) {
System.out.format(“%x “, i);
}
System.out.println();
// ANSI码小于128的字符占一个字节,其余占俩字节
b = str.getBytes();
System.out.println(“默认编码” + System.getProperty(“file.encoding”) + “编码格式下,一个汉字占” + b.length + “字节:”);
for (byte i : b) {
System.out.format(“%x “, i);
}
}
}

也就是说,只要我们正确地读入了汉字“永”字,那么它在内存中的表示形式一定是0x6c38,没有其他任何值能替代这个字。

JVM的折中约定使得一个字符分为两部分:JVM内部和OS的文件系统JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方案。因此可以说所有的编码转换只发生在边界的地方,JVMOS的交界处,也就是各种输入/输出流(或者ReaderWriter类)起作用的地方。

所有的I/O基本上可以分为两大阵营:面向字符的输入/输出流;面向字节的输入/输出流。这个“面向”是指这些类在处理输入/输出的时候,在哪个意义上保持一致。

    如果面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容一致,不能变换任何01的顺序。这种输入/输出方式很适合诸如视频文件或者音频文件,因为不需要变换任何文件内容。

而面向字符的I/O是指希望系统中的文件的字符和读入内存的“字符”要一致。例如:我们的中文版XP系统上有一个GBK的文本文件,其中有一个“永”字,我们不关心这个字的GBK编码是什么,只希望在使用面向字符的I/O把它读入内存并保存在一个char型变量中时,I/O系统不要直接把“永”字的GBK编码放到这个字符(char)型变量中,我们不关心这个char型变量具体的二进制内容到底是多少,只希望这个字符读进来之后仍然是“永”字。

   从这个意义上可以看出,面向字符的I/O类,也就是ReaderWriter类,实际上隐式做了编码转换,在输出时,将内存中的Unicode字符使用系统默认编码方式进行了编码,而在输入时,将文件系统中已经编码过的字符使用默认编码方案进行了还原。这里要注意,ReaderWriter只会使用这个默认的编码来做转换,而不能为一个ReaderWriter指定转换时使用的编码。这也意味着,如果使用中文版WindowsXP系统,其中存放了一个UTF8编码的文件,当采用Reader类来读入的时候,它还会用GBK来转换,转换后的内容当然不对。这其实是一种傻瓜式的功能提供方式,对大多数初级用户(以及不需要跨平台的高级用户,windows一般采用GBKlinux一般采用UTF8)来说反而是一件好事。

   如果用到GBK编码以外的文件,就必须采用编码转换:一个字符与字节之间的转换。因此,JavaI/O系统中能够指定转换编码的地方,也就是在字符与字节转换的地方,那就是InputStremReaderOutputStreamWriter。这两个类是字节流和字符流的适配器类,它们承担编码转换的任务。

既然java是用unicode来编码字符,这个中文字符的unicode就是2个字节。String.getBytes(encoding)方法是获取指定编码的byte数组表示,通常gbk/gb23122个字节,utf-83个字节。如果不指定encoding则取系统默认的encoding

由于JDK是国际版的,在编译的时候,如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式,则javac.exe首先获得我们操作系统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后,javac把转换后的unicode格式的文件进行编译成.class类文件,此时.class文件是UNICODE编码的,它暂放在内存中,紧接着,JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说,我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为UNICODE格式了。

谈谈对Java中Unicode、编码的理解

https://www.cnblogs.com/newstar/archive/2011/06/13/2079870.html

我们经常会遇到编码问题。Java号称国际化的语言,是因为它的class文件采用UTF-8,而JVM运行时使用UTF-16(至于为什么JVM中要采用UTF-16,我没看过 相关的资料,但我猜可能是因为JAVA里面一个字符(char)就是16位的,而UTF-16正是双字节编码),都是unicode的编码。     unicode 的目标就是能支持世界上所有的字符集,也就是说几乎所有的字符集包含的字符在unicode中都有对应的编码。在unicode中,字符与代码的映射关 系,就是unicode字符集,称为UCS(Unicode Character Set),每个unicode字符编码称为code point(代码点?)。UTF-8和UTF-16是不同的UCS编码方法,UTF就是UCS Transformation Format。;

在Java 中,String的getBytes()方法就是对特定的字符串(unicode)按照给定的字符集进行编码(encode),new String()则可以按照某个字符集将字节流转换回unicode(decode)。Java里面的每一个String都是unicode编码。

再来看页面,如果不做特殊处理,Form的提交就按照页面的ContentType设置中的字符集进行编码转换,发送到后台,后台必须利用req.setCharacterEncoding来指定参数的编码格式(不同的应用服务器应有不同的指定方式),才能正确解码。

Java 里面的encode和decode都是相对于unicode而言的,encode的意思是将char[] –> XXX Encoding byte[],decode就是由XXX Encoding byte[] –> char[]。平常,当我们说“将GBK编码转换为UTF-8编码”的时候,实际的意思就是:GBK Encoding byte[] –> UTF-8 Encoding byte[],这种转换只有在需要用byte[]传输数据的时候才有意义,否则便是毫无意义的。

首先要说明的一点是:Java中的String对象就是一个unicode编码的字符串。

但是,我们通常会听到有人说:“我们需要将String由ISO-8859-1转换为GBK编码”,这又是怎么回事呢?实际上,我们并不是要“将 一个由ISO-8859-1编码的String转换为GBK编码的String”,反复说明的是,JAVA中的String都是unicode编码的,所以不存在“ISO- 8859-1编码的String”或“GBK编码的String”这样的说法。而需要转换的唯一的原因是String进行了错误的编码。我们经常会碰到由ISO-8859- 1转换为诸如GBK/UTF-8等等这样的需求。所谓的转换过程是:String –> byte[] –>String。
也许 你非常清楚这个过程的代码:new String(text.getBytes(“ISO-8859-1″),”GBK”)。但是,要真正理解起来并不是那么简单。表面上看似乎很容易理解, 不就是将text String对象按照ISO-8859-1的方式编码为byte[]然后再把它按照GBK的方式转换为String吗?但是这句代码很容易会被误解为: “将text String由ISO-8859-1转换为GBK编码”,这种说法是错误的。难道你见过用这样的代码:new String(text.getBytes(“GBK”),”UTF-8″)来对String进行编码转换的吗?

之所以你会经常看到new String(text.getBytes(“ISO-8859-1″),”GBK”)这句代码,是因为一个GBK的字节流被错误地以ISO-8859- 1的方式转换为String(unicode)了!发生这种情况最普遍的地方是一个GBK编码的网页向后台提交数据的时候,就有可能会看到这句代码的出 现。GBK的流被错误的当成ISO8859-1的流,所以便得到了一个错误的String。由于ISO8859-1是单字节编码,所以每个字节被按照原样 转换为String,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!所以那句经典的new String(text.getBytes(“ISO-8859-1″),”GBK”)便出现了。

如果系统误以为是其它编码格式,就有可能再也转换不回来了,因为编码转换并不是负负得正那么简单的

Add a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注