1.为什么会出现字符编码
(此字符编码不是 字符集中的字符编码)因为 计算机中保存的数据都是二进制数据,这只有计算机能看懂,而我们人类是看不懂的,所以先要将人类语言 按照规则 翻译成计算机的语言,然后人类读的时候再将 计算机语言按照规则翻译成人类语言
2.字符集与字符编码
字符集:就是字符按照一定规则转化成二进制进行保存,因为大家使用的规则不一样,使用字符集就闪亮登场了
字符编码:虽然字符集已经将字符映射到了对应的二进制,但是不一定直接将它存贮,有时候,我们按照一定的规则,将它再次处理,以更加适应计算机存储、网络传输的需要。
常见的编码:
-
ASCII
总共128个用1个字节的低7位表示,0~31是控制字符如换行,回车,删除等,32 ~126是打印字符 (既表示了字符集,也代表一种字符编码) -
ISO-8859-1 (兼容ASCII编码) 数字和西欧字母
ASCII 只能表示128个字符,太少了,于是拓展了,其中ISO-8859-1涵盖了大多数西欧语言字符,单字节编码 ,总共表示256个字符(既表示了字符集,也代表一种字符编码) -
GB2312 (兼容ASCII)
双字节编码,编码范围A1~A9 是符号区,A1到A9是符号区,总共682符号;B0 ~ F7 汉字区,包含6763个汉字 -
GBK(兼容GB2312)
GBK是GB2312的拓展 -
UNICODE(兼容ISO-8859-1) 统一码 万国码
1.1UNICODE 与UTF
Unicode只定义了字符集,没有定义字符编码。
1.1.1 为什么Unicode不像ASCII码一样既是字符集又是字符编码呢?
例如 "哈"这个字符 对应Unicode字符集中的0x54c8,对应二进制为101 0100 1100 1000
一个15位,所有至少需要2个字节来表示,但是Unicode表很大啊,可能用3个字节,4个字节但是我程序获取的时候不知道几个字节代表一个字符,如果用最大字节数,代表一个字符,不够补0,这样会造成造成极大的浪费
因为UNICODE要存放很多数据,完全按照UNICODE来存储数据会造成极大的浪费
所以出现了很多对UNICODE减肥的编码
- UTF-8
- UTF-16
- UTF-32
3.Java中的乱码问题
3.1 为什么会出现乱码问题
因为编码和解码使用的规则不一样(不兼容)
3.2 Java中的编码解码
什么时候需要编码,在数据传输的时候
3.2.1 IO
io按传输类型分为 字节流和字符流
字节流 你保存的是什么字节你读取的就是什么字节 转字符串的时候注意一下编码
new String(bytes,"ISO-8859-1");
字符流是字节流+字符编码
3.2.1.1 InputStreamWrite和OutputStreamWrite
FileWriter 是OutputStreamWrite的子类 相当于
InputStreamReader input =new InputStreamReader(new FileInputStream(file),"默认编码");
下面会提及默认编码
字符转字节
System.out.println(System.getProperty("file.encoding"));
System.out.println(Charset.defaultCharset());
String fileName = "D:/study/test/temporary.txt";
try(FileWriter fileWriter = new FileWriter(fileName);) {
String s = "我真帅";
fileWriter.write(s);
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
输出
UTF-16
UTF-16
以InputStreamWrite为栗
字节转字符
try ( InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("D:/study/test/temporary.txt"),"UTF-16");){
char[] chars=new char[20];
inputStreamReader.read(chars);
System.out.println(new String(chars));
} catch (IOException e) {
e.printStackTrace();
}
输出
我真帅
这时候我们如果用不同的编码(同时不兼容)去读取试一试
try ( InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("D:/study/test/temporary.txt"),"UTF-8");){
char[] chars=new char[20];
inputStreamReader.read(chars);
System.out.println(new String(chars));
} catch (IOException e) {
e.printStackTrace();
}
输出
��bw^
我的脸在你看来很帅,有的人看就马赛克了
Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。值得注意的是如果你没有指定 Charset,将使用本地环境中的默认字符集,例如在中文环境中将使用 GBK 编码。
建议写用什么字符编码读就用什么字符编码
3.2.2 字符串
String是由一个个char组成的 ,java中以UTF-16作为内存的字符存储格式,UTF-16用两个字节来表示Unicode的转化格式,是定长的表述方式
String iso="wo hao shuai";
//编码
byte[] bytes = iso.getBytes("ISO-8859-1");
//解码
String string = new String(bytes,"ISO-8859-1");
System.out.println(string);
输出
wo hao shuai
如果你的传输方使用了ISO_8859_1而你用的不是这个且不兼容就会出现乱码
String iso="wo hao shuai";
//编码
byte[] bytes = iso.getBytes("ISO-8859-1");
//解码
String string = new String(bytes,"UTF-16");
System.out.println(string);
输出
睯慯桵慩
String.getBytes(charsetName)时序图
3.2.3 默认字符集
获取file.encoding jvm系统编码,如果jvm启动的时候不指定file.encoding 默认为当前操作系统编码,如果为空默认字符集为UTF-8
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
String csn = AccessController.doPrivileged(
new GetPropertyAction("file.encoding"));
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
3.2.4 Web 涉及到的编码
3.2.4.1 URL编解码
https://www.baidu.com/s?ie=utf-8&tn=02003390_21_hao_pg&wd=%E4%BB%8A%E5%A4%A9%E5%8F%88%E5%B8%85%E4%BA%86
一段URL中很有可能出现中文参数,那么这段URL那么是按照什么规则来编码和解码的呢?
浏览器编码URL是将非ASCII字符按照某种编码格式编码成16进制然后将每个16进制表示的字节前加上%
关于客户端浏览器与服务端先挖个坑,以后填
-2020年8月6日 留-
3.2.5 总结
使用统一的编码,避免使用软件或者操作系统默认的编码格式
参考:
深入分析Java web 技术内幕