【JAVA编码专题】总结 分类: B1_JAVA 2015-02-11 15:11 290人阅读 评论(0) 收藏
第一部分:编码基础
为什么需要编码:用计算机看得懂的语言(二进制数)表示各种各样的字符。
一、基本概念
ASCII、Unicode、big5、GBK等为字符集,它们只定义了这个字符集内有哪些字符,以及分别用什么数字表示。
而UTF-8与UTF-16则定义了Unicode字符集如何使用计算机看得懂的语言进行传输和保存。
例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:
11000010 10101001 = 0xC2 0xA9
事实上,没有必要将它们严格区分,一些字符集本身就是编码方式,如ASCII。它们均表示如何用二进制数表示一个字符。
因此很多地方将UTF-8、UTF-16与ASCII、GBK等统一当作字符编码方式。
二、常见编码
明白了各种语言需要交流,经过翻译是必要的,那又如何来翻译呢?计算中提拱了多种翻译方式,常见的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。它们都可以被看作为字典,它们规定了转化的规则,按照这个规则就可以让计算机正确的表示我们的字符。目前的编码格式很多,例如 GB2312、GBK、UTF-8、UTF-16 这几种格式都可以表示一个汉字,那我们到底选择哪种编码格式来存储汉字呢?这就要考虑到其它因素了,是存储空间重要还是编码的效率重要。根据这些因素来正确选择编码格式,下面简要介绍一下这几种编码格式。
ASCII 码
学过计算机的人都知道 ASCII 码,总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。
ISO-8859-1
128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。
GB2312
它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。
GBK
全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。
GB18030
全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与 GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。
UTF-16
说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。Unicode 是 Java 和 XML 的基础,下面详细介绍 Unicode 在计算机中的存储形式。
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。
UTF-8
UTF16固定使用2个字节(或者4个字节)来表示字符,这导致与早期大量使用的ASCII码无法兼容,同时一些特殊字符在UNIX系统中存在特殊含义,如 '/0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义。此外,一些最常用的字符(西欧字符)只用一个字节就可以表示,若使用UTF16则浪费带宽或者存储空间。
三、 UTF-8详细介绍
首先 UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或 4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. 除非另外指定, 否则大多数的字节都是这样的(Bigendian convention). 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0x00. 如果要转换成 UCS-4, 则必须在每个 ASCII
字节前插入三个 0x00.
在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 '/0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码.
在 ISO 10646-1 Annex R 和 RFC 2279 里定义的 UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法.
UTF-8 有一下特性:
UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容). 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的.
所有 >U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此, ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分.
表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节. 多字节串的其余字节都在 0x80 到 0xBF 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.
可以编入所有可能的 231个 UCS 代码
UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长.
Bigendian UCS-4 字节串的排列顺序是预定的.
字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到.
下列字节串用来表示一个字符. 用到哪个串取决于该字符在 Unicode 中的序号.
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
xxx 的位置由字符编码数的二进制表示的位填入. 越靠右的 x 具有越少的特殊意义. 只用最短的那个足够表达一个字符编码数的多字节串. 注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目.
例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:
11000010 10101001 = 0xC2 0xA9
而字符 U+2260 = 0010 0010 0110 0000 (不等于) 编码为:
11100010 10001001 10100000 = 0xE2 0x89 0xA0
这种编码的官方名字拼写为 UTF-8, 其中 UTF 代表 UCS Transformation Format. 请勿在任何文档中用其他名字 (比如 utf8 或 UTF_8) 来表示 UTF-8, 当然除非你指的是一个变量名而不是这种编码本身.
第二部分:JAVA编码
一、String中的编码
1、JVM的默认编码为UTF-8
System.out.println(Charset.defaultCharset());
输出:UTF-8
2、获取String的编码
可以通过public byte[] getBytes(String charsetName) throws UnsupportedEncodingException获取某个String的编码
// 二、获取String在各种编码格式中的编码
String s = "aZ中文";
// 1、默认的Unicode编码
byte[] bytesDefault = s.getBytes();
System.out.println(getHexString(bytesDefault)); // 2、也是默认的编码
byte[] bytesDefault2 = s.getBytes(Charset.defaultCharset());
System.out.println(getHexString(bytesDefault2)); // 3、UTF-8编码
byte[] bytesUTF8 = s.getBytes("UTF-8");
System.out.println(getHexString(bytesUTF8)); // 4、UTF-16编码
byte[] bytesUTF16 = s.getBytes("UTF-16");
System.out.println(getHexString(bytesUTF16)); // 5、unicode编码
byte[] bytesUnicode = s.getBytes("UNICODE");
System.out.println(getHexString(bytesUnicode)); // 6、GBK编码
byte[] bytesGBK = s.getBytes("GBK");
System.out.println(getHexString(bytesGBK));
输出:
615ae4b8ade69687
615ae4b8ade69687
615ae4b8ade69687
feff0061005a4e2d6587
feff0061005a4e2d6587
615ad6d0cec4
由此可以看出,unicode与utf16可以认为是同一方式。
3、通过byte[]编码构建String
public String(byte[] bytes, String charsetName) throws UnsupportedEncodingException
用什么方式生成的编码,就应该用原有的方式进行还原
// 三、通过byte[]编码构建String,用什么方式生成的编码,就应该用原有的方式进行还原
// 1、默认编码
String sDefault = new String(bytesDefault);
System.out.println(sDefault); // 2、默认编码2
String sDefault2 = new String(bytesDefault2, Charset.defaultCharset());
System.out.println(sDefault2); // 3、UTF-8编码
String sUTF8 = new String(bytesUTF8, "UTF8");
System.out.println(sUTF8); // 4、UTF-16编码
String sUTF16 = new String(bytesUTF16, "UTF16");
System.out.println(sUTF16); // 5、unicode编码
String sUnicode = new String(bytesUnicode, "Unicode");
System.out.println(sUnicode); // 6、GBK编码
String sGBK = new String(bytesGBK, "GBK");
System.out.println(sGBK);
输出:
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
若用其它编码格式来构建String,则出现乱码
// 若通过其它编码方式进行还原,则出现乱码
// 1、用UTF-16编码解码UTF8生成的字节
String sUTF16Erro = new String(bytesDefault, "UTF16");
System.out.println(sUTF16Erro); // 2、用unicode编码解码UTF8生成的字节
String sUnicodeError = new String(bytesDefault, "Unicode");
System.out.println(sUnicodeError); // 3、用GBK编码解码UTF8生成的字节
String sGBKError = new String(bytesDefault, "GBK");
System.out.println(sGBKError);
输出:
慚귦隇
慚귦隇
aZ涓枃
附完整代码及输出
package org.ljh.javademo.encode; import java.io.IOException;
import java.nio.charset.Charset; public class DefaultEncode { public static void main(String[] args) throws IOException {
// 一、获取当前JVM环境的默认编码
System.out.println(Charset.defaultCharset()); // 二、获取String在各种编码格式中的编码
String s = "aZ中文";
// 1、默认的Unicode编码
byte[] bytesDefault = s.getBytes();
System.out.println(getHexString(bytesDefault)); // 2、也是默认的编码
byte[] bytesDefault2 = s.getBytes(Charset.defaultCharset());
System.out.println(getHexString(bytesDefault2)); // 3、UTF-8编码
byte[] bytesUTF8 = s.getBytes("UTF-8");
System.out.println(getHexString(bytesUTF8)); // 4、UTF-16编码
byte[] bytesUTF16 = s.getBytes("UTF-16");
System.out.println(getHexString(bytesUTF16)); // 5、unicode编码
byte[] bytesUnicode = s.getBytes("UNICODE");
System.out.println(getHexString(bytesUnicode)); // 6、GBK编码
byte[] bytesGBK = s.getBytes("GBK");
System.out.println(getHexString(bytesGBK)); // 三、通过byte[]编码构建String,用什么方式生成的编码,就应该用原有的方式进行还原
// 1、默认编码
String sDefault = new String(bytesDefault);
System.out.println(sDefault); // 2、默认编码2
String sDefault2 = new String(bytesDefault2, Charset.defaultCharset());
System.out.println(sDefault2); // 3、UTF-8编码
String sUTF8 = new String(bytesUTF8, "UTF8");
System.out.println(sUTF8); // 4、UTF-16编码
String sUTF16 = new String(bytesUTF16, "UTF16");
System.out.println(sUTF16); // 5、unicode编码
String sUnicode = new String(bytesUnicode, "Unicode");
System.out.println(sUnicode); // 6、GBK编码
String sGBK = new String(bytesGBK, "GBK");
System.out.println(sGBK); // 若通过其它编码方式进行还原,则出现乱码
// 1、用UTF-16编码解码UTF8生成的字节
String sUTF16Erro = new String(bytesDefault, "UTF16");
System.out.println(sUTF16Erro); // 2、用unicode编码解码UTF8生成的字节
String sUnicodeError = new String(bytesDefault, "Unicode");
System.out.println(sUnicodeError); // 3、用GBK编码解码UTF8生成的字节
String sGBKError = new String(bytesDefault, "GBK");
System.out.println(sGBKError); } // 输入byte[],将之转化为16进制的字符进行输出,如输入{90,20,21},则返回5A1415。因为默认情况下byte以10进制格式进行输出
private static String getHexString(byte[] b) {
String hexs = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
hexs += hex;
}
return hexs;
} }
输出:
UTF-8
615ae4b8ade69687
615ae4b8ade69687
615ae4b8ade69687
feff0061005a4e2d6587
feff0061005a4e2d6587
615ad6d0cec4
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
aZ中文
慚귦隇
慚귦隇
aZ涓枃
二、JAVA IO中的编码
版权声明:本文为博主原创文章,未经博主允许不得转载。
【JAVA编码专题】总结 分类: B1_JAVA 2015-02-11 15:11 290人阅读 评论(0) 收藏的更多相关文章
- Base64编码与解码 分类: 中文信息处理 2014-11-03 21:58 505人阅读 评论(0) 收藏
Base64是一种将二进制转为可打印字符的编码方法,主要用于邮件传输.Base64将64个字符(A-Z,a-z,0-9,+,/)作为基本字符集,把所有符号转换为这个字符集中的字符. 编码: 编码每次将 ...
- Find The Multiple 分类: 搜索 POJ 2015-08-09 15:19 3人阅读 评论(0) 收藏
Find The Multiple Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 21851 Accepted: 8984 Sp ...
- PIE(二分) 分类: 二分查找 2015-06-07 15:46 9人阅读 评论(0) 收藏
Pie Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submissio ...
- 【solr基础教程之九】客户端 分类: H4_SOLR/LUCENCE 2014-07-30 15:28 904人阅读 评论(0) 收藏
一.Java Script 1.由于Solr本身可以返回Json格式的结果,而JavaScript对于处理Json数据具有天然的优势,因此使用JavaScript实现Solr客户端是一个很好的选择. ...
- 周赛-DZY Loves Chessboard 分类: 比赛 搜索 2015-08-08 15:48 4人阅读 评论(0) 收藏
DZY Loves Chessboard time limit per test 1 second memory limit per test 256 megabytes input standard ...
- Ultra-QuickSort 分类: POJ 排序 2015-08-03 15:39 2人阅读 评论(0) 收藏
Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 48111 Accepted: 17549 ...
- Drainage Ditches 分类: POJ 图论 2015-07-29 15:01 7人阅读 评论(0) 收藏
Drainage Ditches Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 62016 Accepted: 23808 De ...
- cf 61E. Enemy is weak 树状数组求逆序数(WA) 分类: Brush Mode 2014-10-19 15:16 104人阅读 评论(0) 收藏
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> ...
- max_flow(Dinic) 分类: ACM TYPE 2014-09-02 15:42 94人阅读 评论(0) 收藏
#include <cstdio> #include <iostream> #include <cstring> #include<queue> #in ...
- SQL 分组 加列 加自编号 自编号限定 分类: SQL Server 2014-11-25 15:41 283人阅读 评论(0) 收藏
说明: (1)日期以年月形式显示:convert(varchar(7),字段名,120) , (2)加一列 (3)自编号: row_number() over(order by 字段名 desc) a ...
随机推荐
- read---读取变量值
read命令从键盘读取变量的值,通常用在shell脚本中与用户进行交互的场合.该命令可以一次读取多个变量的值,变量和输入的值都需要使用空格隔开.在read命令后面,如果没有指定变量名,读取的数据将被自 ...
- 【Hello 2018 C】Party Lemonade
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 处理出凑够2^j最少需要花费多少钱. 即试着把第i种物品买2^(j-i)个,看看会不会更便宜 记录在huafei[0..31]中 然 ...
- java移位操作符注意的问题
如果对char,byte或者short类型的数值进行移位处理,那么在移位进行之前,他们会被转为int类型, 并且所得到的结果也是一个int型. 若对long类型的数值进行处理所得到的结果也是long. ...
- mahout用到的典型测试数据集
http://archive.ics.uci.edu/ml/databases/synthetic_control/ 继续
- Codefroces 812 B. Sagheer, the Hausmeister
http://codeforces.com/problemset/problem/812/B B. Sagheer, the Hausmeister time limit per test 1 sec ...
- mobx项目创建 + mobx项目流程代码
一. 安装mobx 1. react 安装并 reject抽离配置 1. 全局安装 create-react-app 这个脚手架 npm/cnpm i create-react-app -g yarn ...
- Node中的JavaScript和浏览器中的JavaScript的区别
浏览器中的JavaScript: 1.基于ECMAscript规范,这个规范规定了语法 2.添加了dom:用来处理文档 document object model 3.添加了BOM:用于操作浏览器 w ...
- (转)Linux下查看Nginx Apache MySQL的并发连接数和连接状态
转自: http://www.ha97.com/4106.html 1.查看Web服务器(Nginx Apache)的并发请求数及其TCP连接状态:netstat -n | awk '/^tcp/ { ...
- TreeView 的简单实用
TreeView组件是由多个类来定义的,TreeView组件是由命名空间"System.Windows.Forms"中的"TreeView"类来定义的,而其中的 ...
- [转]Linq使用心得——SelectMany替代二重foreach循环
本篇记录了Linq学习的心得,较为浅显,各位大牛请轻拍. 学习Linq其实已经很久了,但是一直没有使用的习惯,故水平也始终没有提高.近来刻意强迫自己用Linq来替代C# 2.0的一些写法.这里有一些心 ...