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为了节约存储空间和网络传输的流量UTF-8采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以由1-6个字节组成。
Java采用UTF-16编码。在Java诞生的时候,UTF-16编码使用的更广泛,而且定长编码的形式也方便计算器处理。后来,随着互联网的流行和壮大,互联网的普及,强烈要求出现一种统一的编码方式,UTF-8编码才得以出现。
关于Unicode与UTF-8之间的转换,可参考http://cmsblogs.com/?p=1458
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 00 | UTF-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.getBytes()是一个用于将String的内码转换为指定的外码的方法。无参数版使用平台的默认编码作为外码,有参数版使用参数指定的编码作为外码;将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:[http://www.baidu.com/a我是?mparam=abc 我是谁] --> [http://www.baidu.com/a%E6%88%91?param=abc%20%E6%88%91%E6%98%AF%E8%B0%81]
服务器使用固定编码进行解码操作,如tomcat8以后默认编码格式是UTF-8;7之前的都是ISO-8859-1。也可以在/conf/server.xml中修改。
URL方式提交数据是很容易产生乱码问题的。关于一些解决乱码的方法,可参考http://cmsblogs.com/?p=1526,核心思想是直接通过JS一次或两次Encode,跳过浏览器的Encode。
2.表单提交(GET/POST)
表单形式一般都不会出现乱码问题。它采用的编码是由页面来决定的即charset。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
它可以通过JSP设定。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
其中pageEncoding是jsp文件,而contentType的charset是指服务器发送给客户端时的内容编码。
流程大致为:JSP File ---[use pageEncoding/file.encoding]---> servlet.java ---[UTF-16]---> servlet.class ---[contentType/ISO-8859-1]---> Browser ---> HTML
[代码1]
public static void main(String[] args) {
// 十六进制表示-128,127
byte b1 = -0x80, b2 = 0x7F;
System.out.println(b1 + ", " + b2); // output: -128, 127
// 八进制表示-128,127
byte b3 = -0200, b4 = 0177;
System.out.println(b3 + ", " + b4); // output: -128, 127
// 十进制表示-128,127
byte b5 = -128, b6 = 127;
System.out.println(b5 + ", " + b6); // output: -128, 127
// 也可以用0x00-0x7F的ASCII表示
byte b7 = '9', b8 = 'a';
System.out.println(b7 + ", " + b8); // output: 57, 97
// 如果想把-128~-1转化成128~255可以用下面方法(负数存补码)
byte b9 = -0x00, b10 = -0x01, b11 = -0x80;
System.out.println(Byte.toUnsignedInt(b9) + ", " + Byte.toUnsignedInt(b10) + ", " + Byte.toUnsignedInt(b11));// output: 0, 255, 128
}
[代码2]
public static void main(String[] args) {
// char也可以八,十,十六用进制表示
char c1 = 0x61; // 'a'
char c2 = 0x6211; // '我'
System.out.println(c1 + ", " + c2); // output: a, 我
}
[代码3]
public static void main(String[] args) throws Exception {
// 对字符串用不同编码格式编码
String s1 = "abc 我是谁";
char[] chars1 = s1.toCharArray();
byte[] bytes1 = s1.getBytes("ISO-8859-1");
byte[] bytes2 = s1.getBytes("GBK");
byte[] bytes3 = s1.getBytes("UTF-8");
byte[] bytes4 = s1.getBytes("UTF-16");
printChart(chars1); // output: 61 62 63 20 6211 662F 8C01
printChart(bytes1); // output: 61 62 63 20 3F 3F 3F
printChart(bytes2); // output: 61 62 63 20 CE D2 CA C7 CB AD
printChart(bytes3); // output: 61 62 63 20 E6 88 91 E6 98 AF E8 B0 81
printChart(bytes4); // output: FE FF 00 61 00 62 00 63 00 20 62 11 66 2F 8C 01
// 对字符串用不同编码格式解码
System.out.println(Charset.defaultCharset().name()); // output: GBK (不同操作系统不同语言默认的系统编码格式有可能不同,LZ是中文Win7)
System.out.println(chars1); // output: abc 我是谁
System.out.println(new String(bytes1, "ISO-8859-1")); // output: abc ???(不可逆的乱码)
System.out.println(new String(bytes2)); // output: abc 我是谁
System.out.println(new String(bytes2, "UTF-8")); // output: abc ?????(乱码)
System.out.println(new String(bytes3, "UTF-8")); // output: abc 我是谁
System.out.println(new String(bytes4, "UTF-16")); // output: abc 我是谁
}
标志 | a | b | c | (空格) | 我 | 是 | 谁 | |
toCharArray[内码] | 61 | 62 | 63 | 20 | 62 11 | 66 2F | 8C 01 | |
ISO-8859-1 | 61 | 62 | 63 | 20 | 3F | 3F | 3F | |
GBK | 61 | 62 | 63 | 20 | CE D2 | CA C7 | CB AD | |
UTF-8 | 61 | 62 | 63 | 20 | E6 88 91 | E6 98 AF | E8 B0 81 | |
UTF-16 | FE FF | 00 61 | 00 62 | 00 63 | 00 20 | 62 11 | 66 2F | 8C 01 |
[代码4]
public static void main(String[] args) throws Exception {
// 读字节流
readByte("./conf/test2_GBK", "UTF-8"); // output: abc ?????(乱码)
readByte("./conf/test2_GBK", "GBK"); // output: abc 我是谁
readByte("./conf/test2_UTF-8", "UTF-8"); // output: abc 我是谁
readByte("./conf/test2_UTF-16", "UTF-16"); // output: abc 我是谁
// 读字符流
readChar("./conf/test2_GBK", "UTF-8"); // output: abc ?????(乱码)
readChar("./conf/test2_GBK", "GBK"); // output: abc 我是谁
readChar("./conf/test2_UTF-8", "UTF-8"); // output: abc 我是谁
readChar("./conf/test2_UTF-16", "UTF-16"); // output: abc 我是谁
}
[静态方法]
/**
* char转换为16进制
*/
public static void printChart(char[] chars) {
for (int i = 0; i < chars.length; i++) {
System.out.print(Integer.toHexString(chars[i]).toUpperCase() + " ");
}
System.out.println();
} /**
* byte转换为16进制
*/
public static void printChart(byte[] bytes) {
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
System.out.print(hex.toUpperCase() + " ");
}
System.out.println();
} /**
* 字节流读取
*/
private static void readByte(String fileName, String charset) {
InputStream input = null;
StringBuilder sb = new StringBuilder();
try {
input = new FileInputStream(new File(fileName));
byte[] bytes = new byte[64];
// InputStream.read()每次都只读取一个字节,效率非常慢,所以使用read(byte[])做为一个缓冲数组
for (int n; (n = input.read(bytes)) != -1; ) {
String str = new String(bytes, 0, n, charset);
sb.append(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(sb.toString());
} /**
* 字符流读取
*/
private static void readChar(String fileName, String charset) {
Reader input = null;
StringBuilder sb = new StringBuilder();
try {
// FileReader继承了InputStreamReader,但并没有实现父类中带字符集参数的构造函数,所以FileReader只能按系统默认的字符集来解码
// input = new FileReader(new File(fileName));
input = new InputStreamReader(new FileInputStream(fileName), charset);
// FileReader.read()每次都只读取一个字符,效率非常慢,所以使用read(char[])做为一个缓冲数组
char[] chars = new char[64];
for (int n; (n = input.read(chars)) != -1; ) {
sb.append(chars, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(sb.toString());
}
Java中的字节,字符与编码,解码的更多相关文章
- java中判断字节数组的编码方式是不是UTF-8
1,用google的工具包,配置maven: <!-- https://mvnrepository.com/artifact/com.googlecode.juniversalchardet/j ...
- 弄清java中的字节与字符
问题 在java中,一个字符等于多少字节? 或者更详细的问:在java中,一个英文字符等于多少字节?一个中文字符等于多少字节? 答案 Java采用unicode来表示字符,java中的一个char是2 ...
- 【项目分析】利用C#改写JAVA中的Base64.DecodeBase64以及Inflater解码
原文:[项目分析]利用C#改写JAVA中的Base64.DecodeBase64以及Inflater解码 最近正在进行项目服务的移植工作,即将JAVA服务的程序移植到DotNet平台中. 在JAVA程 ...
- Java中的字节输入出流和字符输入输出流
Java中的字节输入出流和字符输入输出流 以下哪个流类属于面向字符的输入流( ) A BufferedWriter B FileInputStream C ObjectInputStream D In ...
- c#与java中byte字节的区别及转换方法
原文:c#与java中byte字节的区别及转换方法 在java中 byte的范围在 [-128,127] 在C#中 byte的范围在 [0,255] 所以 java程序与C#程序 进行数据传输的时 ...
- Java中常用的字符编码-解析
ASCII字符编码 美国信息互换标准代码,为罗马字母编制的一套编码,主要用于表达现代英语和其他西欧语言中的字符,1字节的7位表示一个字符. ISO-8859-1字符编码 ISO为西欧语言中的字符制定的 ...
- java中字节流和字符流的区别
流分类: 1.Java的字节流 InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先.2.Java的字符流 Reader是所有读取字符串输入流的祖先,而 ...
- 在Java中按字节获得字符串长度的三种方法
转载:http://www.blogjava.net/nokiaguy/archive/2010/04/11/317982.html 由于Java是基于Unicode编码的,因此,一个汉字的长度为1, ...
- Java中根据字节截取字符串
一.简介 为了统一世界各国的字符集,流行开了Unicode字符集,java也支持Unicode编码,即java中char存的是代码点值,即无论是‘A’还是‘中’都占两个字节. 代码点值:与Unicod ...
随机推荐
- 类的命名空间与卸载详解及jvisualvm使用
类的命名空间详解: 在上一次[https://www.cnblogs.com/webor2006/p/9108301.html]最后实验中有一个违背咱们理解的,这里回顾一下: 也就是说,"某 ...
- okhttp任务调度核心类dispatcher解析
在之前已经对okhttp的同步和异步请求的流程进行了详细的分析,其中任务调度是由dispatcher来实现的,非常重要,所以这次专门来对它进行一个了解,带着问题去进行探究: Q1:okhttp如何实现 ...
- Python: sqlite3模块
sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块 SQLite 是一个C语言库,它可以提供一种轻量级的基于磁盘的数据库,这种数据库不需要独立的服务器进程,也允许需要使用一种 ...
- 用idea操作svn
使用SVN前提必须安装好服务端和客户端,或者知道服务端的url才能对服务器中的文件进行操作. 服务端:SVN service 客户端:TortoiseSVN 提交 第一步:确认SVN 服务器是否开启 ...
- JavaScript中的类(Class)
基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到的,新的class写法是让对象原型的写法更加清晰,更像面向对象编程的语法而已. ES5生成例对象传统方法是通过构造函 ...
- 操作mysql 中文乱码情况
解决方法 : 在连接字符串中设置charset=utf8 即可正常添加中文字符 <add name="mtgzghEntities" connectionString=& ...
- Windows 7 [Web应用程序项目***已配置为使用IIS。无法访问IIS元数据库,您没有足够的特权访问计算机上的IIS网站]
如下所示,我最近也遇到这个类似的错误(Win7 + IIS7.0),最后如下图解决 系统用户权限问题, 通过管理员运行 即可正常使用.
- EM算法 小结
猴子吃果冻 博客园 首页 新随笔 联系 管理 订阅 随笔- 35 文章- 0 评论- 3 4-EM算法原理及利用EM求解GMM参数过程 1.极大似然估计 原理:假设在一个罐子中放着许多白球和 ...
- codeforces685B
CF685B Kay and Snowflake 题意翻译 输入一棵树,判断每一棵子树的重心是哪一个节点. 题目描述 After the piece of a devilish mirror hit ...
- 集合家族——Vector
一.vector简介 Vector 可以实现可增长的对象数组.与数组一样,它包含可以使用整数索引进行访问的组件.不过,Vector 的大小是可以增加或者减小的,以便适应创建 Vector 后进行添加或 ...