how2j.cn

本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



42分55秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)



步骤 1 : 编码概念   
步骤 2 : 常见编码   
步骤 3 : UNICODE和UTF   
步骤 4 : Java采用的是Unicode   
步骤 5 : 一个汉字使用不同编码方式的表现   
步骤 6 : 文件的编码方式-记事本   
步骤 7 : 文件的编码方式-eclipse   
步骤 8 : 用FileInputStream 字节流正确读取中文   
步骤 9 : 用FileReader 字符流正确读取中文   
步骤 10 : 练习-数字对应的中文   
步骤 11 : 答案-数字对应的中文   
步骤 12 : 练习-移除BOM   
步骤 13 : 答案-移除BOM   

计算机存放数据只能存放数字,所有的字符都会被转换为不同的数字。
就像一个棋盘一样,不同的字,处于不同的位置,而不同的位置,有不同的数字编号。
有的棋盘很小,只能放数字和英文
有的大一点,还能放中文
有的“足够”大,能够放下世界人民所使用的所有文字和符号

如图所示,英文字符 A 能够放在所有的棋盘里,而且位置都差不多
中文字符, 中文字符 能够放在后两种棋盘里,并且位置不一样,而且在小的那个棋盘里,就放不下中文
编码概念
工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)

其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
根据前面的学习,我们了解到不同的编码方式对应不同的棋盘,而UNICODE因为要存放所有的数据,那么它的棋盘是最大的。
不仅如此,棋盘里每个数字都是很长的(4个字节),因为不仅要表示字母,还要表示汉字等。

如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。
比如在ISO-8859-1中,a 字符对应的数字是0x61
而UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科
UNICODE和UTF
步骤 4 :

Java采用的是Unicode

edit
写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE. "中"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。
package stream; public class TestStream { public static void main(String[] args) { String str = "中"; } }
package stream;

public class TestStream {
	public static void main(String[] args) {
		String str = "中";
	}
}
步骤 5 :

一个汉字使用不同编码方式的表现

edit
以字符 为例,查看其在不同编码方式下的值是多少

也即在不同的棋盘上的位置
一个汉字使用不同编码方式的表现
package stream; import java.io.UnsupportedEncodingException; public class TestStream { public static void main(String[] args) { String str = "中"; showCode(str); } private static void showCode(String str) { String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" }; for (String encode : encodes) { showCode(str, encode); } } private static void showCode(String str, String encode) { try { System.out.printf("字符: \"%s\" 的在编码方式%s下的十六进制值是%n", str, encode); byte[] bs = str.getBytes(encode); for (byte b : bs) { int i = b&0xff; System.out.print(Integer.toHexString(i) + "\t"); } System.out.println(); System.out.println(); } catch (UnsupportedEncodingException e) { System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\n", encode, str); } } }
步骤 6 :

文件的编码方式-记事本

edit
接下来讲,字符在文件中的保存
字符保存在文件中肯定也是以数字形式保存的,即对应在不同的棋盘上的不同的数字
记事本打开任意文本文件,并且另存为,就能够在编码这里看到一个下拉。
ANSI 这个不是ASCII的意思,而是采用本地编码的意思。如果你是中文的操作系统,就会使GBK,如果是英文的就会是ISO-8859-1
Unicode UNICODE原生的编码方式
Unicode big endian 另一个 UNICODE编码方式
UTF-8 最常见的UTF-8编码方式,数字和字母用一个字节, 汉字用3个字节。
文件的编码方式-记事本
步骤 7 :

文件的编码方式-eclipse

edit
eclipse也有类似的编码方式,右键任意文本文件,点击最下面的"property"
就可以看到Text file encoding
也有ISO-8859-1,GBK,UTF-8等等选项。
其他的US-ASCII,UTF-16,UTF-16BE,UTF-16LE不常用。
文件的编码方式-eclipse
步骤 8 :

用FileInputStream 字节流正确读取中文

edit
为了能够正确的读取中文内容
1. 必须了解文本是以哪种编码方式保存字符的
2. 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符
如本例,一个文件中的内容是字符,编码方式是GBK,那么读出来的数据一定是D6D0。
再使用GBK编码方式识别D6D0,就能正确的得到字符

注: 在GBK的棋盘上找到的字后,JVM会自动找到在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中
用FileInputStream 字节流正确读取中文
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { File f = new File("E:\\project\\j2se\\src\\test.txt"); try (FileInputStream fis = new FileInputStream(f);) { byte[] all = new byte[(int) f.length()]; fis.read(all); //文件中读出来的数据是 System.out.println("文件中读出来的数据是:"); for (byte b : all) { int i = b&0x000000ff; //只取16进制的后两位 System.out.println(Integer.toHexString(i)); } System.out.println("把这个数字,放在GBK的棋盘上去:"); String str = new String(all,"GBK"); System.out.println(str); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 9 :

用FileReader 字符流正确读取中文

edit
FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符
而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"));

在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。

解释: 为什么中字前面有一个?
如果是使用记事本另存为UTF-8的格式,那么在第一个字节有一个标示符,叫做BOM用来标志这个文件是用UTF-8来编码的。
用FileReader 字符流正确读取中文
package stream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; public class TestStream { public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException { File f = new File("E:\\project\\j2se\\src\\test.txt"); System.out.println("默认编码方式:"+Charset.defaultCharset()); //FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了 //而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK try (FileReader fr = new FileReader(f)) { char[] cs = new char[(int) f.length()]; fr.read(cs); System.out.printf("FileReader会使用默认的编码方式%s,识别出来的字符是:%n",Charset.defaultCharset()); System.out.println(new String(cs)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替 //并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式 try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) { char[] cs = new char[(int) f.length()]; isr.read(cs); System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n"); System.out.println(new String(cs)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
步骤 10 :

练习-数字对应的中文

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字
步骤 11 :

答案-数字对应的中文

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

1分10秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


答案-数字对应的中文
package stream; import java.io.UnsupportedEncodingException; public class TestStream { public static void main(String[] args) throws UnsupportedEncodingException { // 找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字 byte[] bs = new byte[3]; bs[0] = (byte) 0xE5; bs[1] = (byte) 0xB1; bs[2] = (byte) 0x8C; String str =new String(bs,"UTF-8"); System.out.println("E5 B1 8C 对应的字符是:"+str); } }
package stream;

import java.io.UnsupportedEncodingException;

public class TestStream {
	public static void main(String[] args) throws UnsupportedEncodingException {
//		找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字 
		byte[] bs = new byte[3];
		bs[0] = (byte) 0xE5;
		bs[1] = (byte) 0xB1;
		bs[2] = (byte) 0x8C;
		
		String str  =new String(bs,"UTF-8");
		System.out.println("E5 B1 8C 对应的字符是:"+str);
	}
}
步骤 12 :

练习-移除BOM

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
如果用记事本根据UTF-8编码保存汉字就会在最前面生成一段标示符,这个标示符用于表示该文件是使用UTF-8编码的。

找出这段标示符对应的十六进制,并且开发一个方法,自动去除这段标示符
步骤 13 :

答案-移除BOM

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费5个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费5个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

8分57秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


答案-移除BOM
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; public class TestStream { public static void main(String[] args) { File f = new File("E:\\project\\j2se\\src\\test.txt"); try (FileInputStream fis = new FileInputStream(f);) { byte[] all = new byte[(int) f.length()]; fis.read(all); System.out.println("首先确认按照UTF-8识别出来有?"); String str = new String(all,"UTF-8"); System.out.println(str); System.out.println("根据前面的所学,知道'中'字对应的UTF-8编码是:e4 b8 ad"); System.out.println("打印出文件里所有的数据的16进制是:"); for (byte b : all) { int i = b&0xff; System.out.print(Integer.toHexString(i)+ " "); } System.out.println(); System.out.println("通过观察法得出 UTF-8的 BOM 是 ef bb bf"); byte[] bom = new byte[3]; bom[0] = (byte) 0xef; bom[1] = (byte) 0xbb; bom[2] = (byte) 0xbf; byte[] fileContentWithoutBOM= removeBom(all,bom); System.out.println("去掉了BOM之后的数据的16进制是:"); for (byte b : fileContentWithoutBOM) { int i = b&0xff; System.out.print(Integer.toHexString(i)+ " "); } System.out.println(); System.out.println("对应的字符串就没有问号了:"); String strWithoutBOM=new String(fileContentWithoutBOM,"UTF-8"); System.out.println(strWithoutBOM); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static byte[] removeBom(byte[] all, byte[] bom) { return Arrays.copyOfRange(all, bom.length, all.length); } }


HOW2J公众号,关注后实时获知布最新的教程和优惠活动,谢谢。


问答区域    
2019-10-16 Demo_数字对应的中文
JOYLOVE



Demo_数字对应的中文
package 作业练习;

import java.io.IOException;

//找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字
public class Demo_数字对应的中文 {
    public static void main(String[] args) {
        // 将16进制数转换为字节
        byte b1 = (byte) 0xE5;
        byte b2 = (byte) 0xB1;
        byte b3 = (byte) 0x8C;

        byte[] b = new byte[3];
        b[0]=b1;
        b[1]=b2;
        b[2]=b3;
        try {
            String s = new String(b,"UTF-8");
            System.out.printf("E5,B1,8C对应的UTF-8编码的汉字是:%s",s);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2019-10-14 交作业-移除BOM
小企业哒



使用FileInputStream fis = new FileInputStream(f) 而不使用InputStreamReader isr = new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")) 因为下面的语句限制了只能创建char数组,后面就无法通过for遍历文件中数据的16进制,因为需要使用byte数组. Arrays.copyOfRange(array, bom.length, array.length) 之前没有另建方法而是直接使用Arrays.copyOfRange(array, 3, array.length),这样不易后期修改代码,按站长说的另建方法会更好. for (byte b : array) { int i = b & 0xff; System.out.print(Integer.toHexString(i) + " "); } byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性. byte类型(8位) 转换到 int类型(32位) 会在高24位补1 但是计算机存储数据机制:正数存储的二进制原码,负数存储的是二进制的补码.补码是负数的绝对值反码加1 byte类型(8位) 转换到 int类型(32位) 因为会在高24位补1,所以此时其二进制补码其实已经不一致了 如:byte b = -47 其二进制 绝对值原码为 0010 1111 反码为 1101 0000 补码为 1101 0001 此时byte类型转换到int类型 ,会在高位补1 即为 1111 1111 1111 1111 1111 1111 1101 0001 此时二进制补码已经不一致了 b&0xff可以将高24位变为0,低8位保持原样,即为 0000 0000 0000 0000 0000 0000 1101 0001 此时二进制补码一致了 参考资料:https://www.cnblogs.com/think-in-java/p/5527389.html
public class HomeWork2 {

	// 练习-移除BOM
	// 如果用记事本根据UTF-8编码保存汉字就会在最前面生成一段标示符,这个标示符用于表示该文件是使用UTF-8编码的。
	// 找出这段标示符对应的十六进制,并且开发一个方法,自动去除这段标示符
	public static void main(String[] args) {

		File f = new File("d:\\Dota\\Coded\\bom.txt");

		// 创建输入流
		try (FileInputStream fis = new FileInputStream(f)) {
			// 创建字符数组,长度为源文件长度
			byte[] array = new byte[(int) f.length()];
			// 读取源文件的内容
			fis.read(array);
			// 将数组中的数据转换为UTF-8编码格式的字符串
			String str = new String(array, "UTF-8");

			System.out.println("文件中的字符为: " + str + "\r\n");
			System.out.println("字符: \'绫\' 的在编码方式UTF-8下的十六进制值是:\r\n" + "e7 bb ab");
			System.out.println("\r\n文件中全部数据的16十六进制是:");

			// 通过for遍历源文件中全部数据的16进制
			for (byte b : array) {
				int i = b & 0xff;
				System.out.print(Integer.toHexString(i) + " ");
			}

			System.out.println("\r\n\r\n其中多出来的: ef bb bf就是我们去要去除的BOM\r\n");
			// 创建BOM数组,其长度为3
			byte[] bom = new byte[3];
			// 调用方法去除BOM
			byte[] arrayWithoutBOM = removeBOM(array, bom);

			System.out.println("去除BOM后文件中数据的16进制是:");

			// 再次通过for遍历源文件中去除BOM后数据的16进制
			for (byte b : arrayWithoutBOM) {
				int i = b & 0xff;
				System.out.print(Integer.toHexString(i) + " ");
			}

			// 将数组中的数据转换为UTF-8编码格式的字符串
			String strWithoutBOM = new String(arrayWithoutBOM, "UTF-8");
			System.out.print("\r\n\r\n去除BOM后文件中的字符为: " + strWithoutBOM);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 设计去除BOM的方法
	private static byte[] removeBOM(byte[] array, byte[] bom) {
		return Arrays.copyOfRange(array, bom.length, array.length);
	}
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2019-10-14 交作业-移除BOM
2019-10-14 交作业-数字对应的中文
2019-09-06 移除BOM
2019-09-06 数字对应中文
2019-08-22 作业:移除BOM
2019-08-20 使用UTF-8读取没有?BOM标识符
2019-08-16 作业:找出编码对应的汉字
2019-08-05 步骤8示例代码
2019-08-02 移除bom的问题
2019-07-16 作业1、2
2019-07-12 为什么得到的还是“中”字呢?
2019-06-27 为什么这里不用int i = b&0x000000ff;
2019-06-27 为什么 int i = b&0x000000ff; //只取16进制的后两位
2019-06-27 图像里面的三位分别是谁?
2019-06-27 关于 步骤 5 : 一个汉字使用不同编码方式的表现 的问题
2019-05-28 查找字符串
2019-05-27 ZZ交作业 中文问题 移除BOM
2019-05-27 ZZ交作业 StringBuffer性能比较
2019-05-27 ZZ交作业 数字对应的中文
2019-05-23 看了答案有疑问,如果强转byte值溢出了咋办不就不准了吗?来个大神
2019-05-20 全是问号。。。
2019-04-19 交作业(二),欢迎暂时没思路的朋友来交流参考
2019-04-19 交作业(一),欢迎暂时没思路的朋友来交流参考
2019-04-12 问什么中文后面会有方块?
2019-03-25 byte[] all = new byte[(int) f.length()];这里有必要强转int么?如果不强转有可能发生什么问题?
2019-02-22 步骤九我的结果怎么不对
2019-01-23 交作业 数字对应的中文
2019-01-13 作业2
2019-01-06 作业,难度突然飙升...
2018-12-06 为何我的Eclipse打印不出?号?
2018-11-16 交作业
2018-11-15 第二题读UTF8文件
2018-10-12 作业
2018-10-11 作业
2018-10-03 UTF-8的BOM标记是否恒定为“?”且为一个字符 我这样做是不是也可以?
2018-09-09 识别出来是是“屌”?
2018-08-14 我这样做第一题行么(⊙o⊙)…
2018-08-12 都是使用byte最简单粗暴的进行解决
2018-08-12 主要是int类型转成byte的后再转 utf-8这里卡了很久也在网上查找很多资料
2018-08-02 我的为啥默认UTF-8呢 但是之前练习存入数据也是乱码啊
2018-06-18 b&0xff是什么意思
2018-05-31 关于步骤5的分享
2018-05-31 进制转换需要知道吗?
2018-04-25 稍改进下的作业2
2018-04-19 作业
2018-03-31 https://blog.csdn.net/wangyang1354/article/details/52290461
2018-03-29 谁翻译下这几句代码的意思,完全看不懂,,,
2018-03-25 交作业
2018-02-22 既然都知道了bom是三个固定的数,那就直接把得到的数组Arrays.copyOfRange一个新数组叶可以啊
2017-11-26 老大,为啥我这把“中”从GBK放到UTF-8和示例不一样啊
2017-10-27 奇怪的问题
2017-08-27 这里的char[(int)f.length()],f.length()指的是有多少字节,而我们应该把char数组长度设置为字符数吧
2017-08-03 中文亂碼
2017-06-06 新手提问,多多关照
2017-04-06 在实际做项目时用的是gbk还是utf-8呢
2017-03-21 请问为什么我的显示和文档中的内容不符呢?
2017-02-18 步骤5中为什么是 b&0xff
2017-01-24 为什么我的跟你们的不一样
2016-12-07 为什么cmd下和eclipse下的输出不一样
2016-06-26 byte和char的区别




提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-I/O-中文问题 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 881555796
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: http://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图