Java中的字节,字符与编码,解码
ASCII编码
ASCII码主要是为了表⽰英⽂字符⽽设计的,ASCII码⼀共规定了128个字符的编码(0x00-0x7F),只占⽤了⼀个字节的后⾯7位,最前⾯的1位统⼀规定为0。
ISO-8859-1编码
为了扩展覆盖其他语⾔字符,ISO组织在ASCII码基础上⼜制定了⼀系列标准⽤来扩展ASCII编码,它们是ISO-8859-1~ISO-8859-15,其中ISO-8859-1应⽤得最⼴泛。
ISO-8859-1仍然是单字节编码,它总共能表⽰256个字符。ISO-8859-1向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII⼀致。
因为ISO-8859-1编码范围使⽤了单字节内的所有空间,在⽀持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换⾔之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。
Unicode,UCS2和UCS4
Unicode是为整合全世界的所有语⾔⽂字⽽诞⽣的。任何⽂字在Unicode中都对应⼀个值,这个值称为代码点(Code Point),常写成 U+XXXX的格式。⽽⽂字和代码点之间的对应关系就有UCS-2和UCS-4。
UCS-2:⽤两个字节来表⽰代码点,其取值范围为 U+0000~U+FFFF。
UCS-4:为了能表⽰更多的⽂字,⼈们⼜提出了UCS-4,即⽤四个字节表⽰代码点。它的范围为 U+00000000~U+7FFFFFFF,其中U+00000000~
U+0000FFFF和UCS-2是⼀样的。
要注意,UCS-2和UCS-4只规定了代码点和⽂字之间的对应关系,并没有规定代码点在计算机中如何存储。规定存储⽅式的称为UTF(Unicode Transformation Format),其中应⽤较多的就是UTF-8和UTF-16了。
UTF-8,UTF-16,UTF-32
UTF-32是对应于UCS-4,不常⽤。
UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian或Little Endian⽅式直接保
存下来。所以UTF-16采⽤2个字节来存储Unicode。UTF-16也可以表⽰UCS-4的部分字符,所以UTF-16也采⽤4个字节来存储Unicode。
UTF-8为了节约存储空间和⽹络传输的流量U TF-8采⽤了⼀种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以由1-6个字节组成。
Java采⽤UTF-16编码。在Java诞⽣的时候,UTF-16编码使⽤的更⼴泛,⽽且定长编码的形式也⽅便计算器处理。后来,随着互联⽹的流⾏和壮⼤,互联⽹的普及,强烈要求出现⼀种统⼀的编码⽅式,UTF-8编码才得以出现。
关于Unicode与UTF-8之间的转换,可参考
Big Endian,Little Endian与⽂本开头的标志
⼀个字符可能占⽤多个字节,⽐如字符0xABCD,如果存储为AB CD,则称为Big Endian;如果存储为 CD AB,则称为Little Endian。
要知道具体是哪种编码⽅式,需要判断⽂本开头的标志:
EF BB BF UTF-8([EF BB BF]也称为BOM,可选项,⼀般Windows加,Linux不加)
FE FF UTF-16/UCS-2, little endian(默认)
FF FE UTF-16/UCS-2, big endian
FF FE 00 00UTF-32/UCS-4, little endian(默认)
00 00 FE FF UTF-32/UCS-4, big-endian
字节,字符转换及不同编码格式编码,解码
Java中字节是byte,占8位(1 byte = 8 bit),最⾼位是符号位,表⽰范围为-128~127。[代码1]
Java规定了字符的内码要⽤UTF-16编码,占16位(2 byte = 16 bit)表⽰范围为0~65535(没有符号位),⽤char表⽰(内码是程序内部使⽤的字符编码,特别是某种语⾔实现其char或String类型在内存⾥⽤的内部编码)。[代码2]
String的内容⽤外码编码好,结果放在⼀个新byte[]返回。[代码3]
字节流与字符流
InputStream为字节输⼊流的所有类的超类,Reader为读取字符流的抽象类。java读取⽂件的⽅式分为按字节流读取和按字符流读取,其中InputStream、Reader是这两种读取⽅式的超类。
其实字符流可以看做是⼀种包装流,它的底层还是采⽤字节流来读取字节,然后它使⽤指定的编码⽅式将读取字节解码为字符。在读取的时候字符读取每次是读取2个字节,字节流每次读取1个字节。[代码4]
Java web中的编码,解码
⽤户向服务器发送HTTP请求主要有以下两种⽅式:
1.URL⽅式直接访问
浏览器将会对URL的path和parameter(QueryString)进⾏编码操作。URL的编码规范规定浏览器将⾮ASCII字符按照某种编码格式编码成16进制数字然后将每个16进制表⽰的字节前加上"%"。但是对于不同的浏览器,版本,操作系统等环境都会导致编码结果不同。例Chrom使⽤UTF-8:[www.baidu/a我是? mparam=abc 我是谁] --> [www.baidu/a%E6%88%91?param=abc%20%E6%88%91%E6%98%AF%E8%B0%81]
服务器使⽤固定编码进⾏解码操作,如tomcat8以后默认编码格式是UTF-8;7之前的都是ISO-8859-1。也可以在/l中修改。
URL⽅式提交数据是很容易产⽣乱码问题的。关于⼀些解决乱码的⽅法,可参考,核⼼思想是直接通过JS⼀次或两次Encode,跳过浏览器的Encode。
2.表单提交(GET/POST)
表单形式⼀般都不会出现乱码问题。它采⽤的编码是由页⾯来决定的即charset。
1<!DOCTYPE html>
2<html>
3<head>
4<meta charset="UTF-8">
5<title>Title</title>
6</head>
7<body>
8</body>
9</html>
它可以通过JSP设定。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
其中pageEncoding是jsp⽂件,⽽contentType的charset是指服务器发送给客户端时的内容编码。
流程⼤致为:JSP File ---[use ding]---> servlet.java ---[UTF-16]---> servlet.class ---[contentType/ISO-8859-1]---> Browser ---> HTML
[代码1]
1public static void main(String[] args) {
2// ⼗六进制表⽰-128,127
3byte b1 = -0x80, b2 = 0x7F;
4        System.out.println(b1 + ", " + b2); // output: -128, 127
5// ⼋进制表⽰-128,127
6byte b3 = -0200, b4 = 0177;
7        System.out.println(b3 + ", " + b4); // output: -128, 127
8// ⼗进制表⽰-128,127
9byte b5 = -128, b6 = 127;
10        System.out.println(b5 + ", " + b6); // output: -128, 127
11// 也可以⽤0x00-0x7F的ASCII表⽰
12byte b7 = '9', b8 = 'a';
13        System.out.println(b7 + ", " + b8); // output: 57, 97
14// 如果想把-128~-1转化成128~255可以⽤下⾯⽅法(负数存补码)
15byte b9 = -0x00, b10 = -0x01, b11 = -0x80;
16        System.out.UnsignedInt(b9) + ", " + UnsignedInt(b10) + ", " + UnsignedInt(b11));// output: 0, 255, 128
17    }
[代码2]
1public static void main(String[] args) {
2// char也可以⼋,⼗,⼗六⽤进制表⽰
3char c1 = 0x61; // 'a'
4char c2 = 0x6211; // '我'
5        System.out.println(c1 + ", " + c2); // output: a, 我
6    }
[代码3]
1public static void main(String[] args) throws Exception {
2// 对字符串⽤不同编码格式编码
3        String s1 = "abc 我是谁";
4char[] chars1 = s1.toCharArray();
5byte[] bytes1 = s1.getBytes("ISO-8859-1");
6byte[] bytes2 = s1.getBytes("GBK");
7byte[] bytes3 = s1.getBytes("UTF-8");
8byte[] bytes4 = s1.getBytes("UTF-16");
9        printChart(chars1); // output: 61 62 63 20 6211 662F 8C01
10        printChart(bytes1); // output: 61 62 63 20 3F 3F 3F
11        printChart(bytes2); // output: 61 62 63 20 CE D2 CA C7 CB AD
12        printChart(bytes3); // output: 61 62 63 20 E6 88 91 E6 98 AF E8 B0 81
13        printChart(bytes4); // output: FE FF 00 61 00 62 00 63 00 20 62 11 66 2F 8C 01
14// 对字符串⽤不同编码格式解码
15        System.out.println(Charset.defaultCharset().name()); // output: GBK (不同操作系统不同语⾔默认的系统编码格式有可能不同,LZ是中⽂Win7)
16        System.out.println(chars1); // output: abc 我是谁
17        System.out.println(new String(bytes1, "ISO-8859-1")); // output: abc ???(不可逆的乱码)
18        System.out.println(new String(bytes2)); // output: abc 我是谁
19        System.out.println(new String(bytes2, "UTF-8")); // output: abc ?????(乱码)
20        System.out.println(new String(bytes3, "UTF-8")); // output: abc 我是谁
21        System.out.println(new String(bytes4, "UTF-16")); // output: abc 我是谁
22    }
标志a b c(空格)我是谁
toCharArray[内码]6162632062 1166 2F8C 01
ISO-8859-1616263203F3F3F
GBK61626320CE D2CA C7CB AD
UTF-861626320E6 88 91E6 98 AF E8 B0 81
UTF-16FE FF00 6100 6200 6300 2062 1166 2F8C 01
[代码4]
1public static void main(String[] args) throws Exception {
2// 读字节流
3        readByte("./conf/test2_GBK", "UTF-8"); // output: abc ?????(乱码)
4        readByte("./conf/test2_GBK", "GBK"); // output: abc 我是谁
5        readByte("./conf/test2_UTF-8", "UTF-8"); // output: abc 我是谁
6        readByte("./conf/test2_UTF-16", "UTF-16"); // output: abc 我是谁
7// 读字符流
8        readChar("./conf/test2_GBK", "UTF-8"); // output: abc ?????(乱码)
9        readChar("./conf/test2_GBK", "GBK"); // output: abc 我是谁
10        readChar("./conf/test2_UTF-8", "UTF-8"); // output: abc 我是谁
11        readChar("./conf/test2_UTF-16", "UTF-16"); // output: abc 我是谁
12    }
[静态⽅法]
1/**
2    * char转换为16进制
3*/
4public static void printChart(char[] chars) {
5for (int i = 0; i < chars.length; i++) {
6            System.out.HexString(chars[i]).toUpperCase() + " ");
7        }
8        System.out.println();
9    }
10
11/**
12    * byte转换为16进制
13*/
14public static void printChart(byte[] bytes) {
15for (int i = 0; i < bytes.length; i++) {
16            String hex = HexString(bytes[i] & 0xFF);
17if (hex.length() == 1) {
18                hex = '0' + hex;
19            }
20            System.out.UpperCase() + " ");
21        }
22        System.out.println();
23    }
24
25/**
26    * 字节流读取
27*/
28private static void readByte(String fileName, String charset) {
29        InputStream input = null;
30        StringBuilder sb = new StringBuilder();
31try {
32            input = new FileInputStream(new File(fileName));
33byte[] bytes = new byte[64];
34// ad()每次都只读取⼀个字节,效率⾮常慢,所以使⽤read(byte[])做为⼀个缓冲数组
35for (int n; (n = ad(bytes)) != -1; ) {
36                String str = new String(bytes, 0, n, charset);
37                sb.append(str);
38            }
39        } catch (IOException e) {
40            e.printStackTrace();
41        } finally {
42try {
43                input.close();
44            } catch (IOException e) {
45                e.printStackTrace();
46            }
47        }
48        System.out.String());
49    }
50
51/**
url编码和utf8区别
52    * 字符流读取
53*/
54private static void readChar(String fileName, String charset) {
55        Reader input = null;
56        StringBuilder sb = new StringBuilder();
57try {
58// FileReader继承了InputStreamReader,但并没有实现⽗类中带字符集参数的构造函数,所以FileReader只能按系统默认的字符集来解码59// input = new FileReader(new File(fileName));
60            input = new InputStreamReader(new FileInputStream(fileName), charset);
61// ad()每次都只读取⼀个字符,效率⾮常慢,所以使⽤read(char[])做为⼀个缓冲数组
62char[] chars = new char[64];
63for (int n; (n = ad(chars)) != -1; ) {
64                sb.append(chars, 0, n);
65            }
66        } catch (IOException e) {
67            e.printStackTrace();
68        } finally {
69try {
70                input.close();
71            } catch (IOException e) {
72                e.printStackTrace();
73            }
74        }
75        System.out.String());
76    }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。