1. 前置知识
字符集是什么?
字符集(Character Set)是一组字符的集合,它定义了可以在计算机系统中使用的所有字符。字符集可以包括字母、数字、标点符号、控制字符、图形符号等。字符集使得计算机能够存储、处理和显示各种语言和符号。
字符集的分类:
一般认识这几种:ASCII(美国标准信息交换码),GBK(汉字内码扩展规范),Unicode(统一码,也叫万国码),UTF-8(使用广泛),UTF-16
理解字符集:
1.ASCII(美国标准信息交换码):包含128个字符,主要包括英文字母、数字和一些特殊字符,使用1个字节存储一个字符,首位是0。(2^7 = 128)
2.GBK(汉字内码扩展规范):包含两万多汉字,一个汉字编码为两个字节,同时兼容ASCII编码,汉字编码首位为1。
3.Unicode(统一码,也叫万国码):还有另外一种叫法(UTF-32),就是32位二进制数也就是4个字节表示一个字符,不管是汉字还是其他符号,过于奢侈(浪费内存)。
4.UTF-8(使用广泛):采取可变长编码方案,共分为4个长度区:字节长度1,2,3,4。英文、数字等只占用1个字节(兼容ASCII码),汉字占用3个字节:具体看下图:
字符集相关的方法及用法:
字符集的编码、解码操作:(java完成了对字符的编码和解码,String提供了以下方法:)
编码:
1.byte[] getBytes():使用平台默认字符集,将String编码为字节数组存储到新的字节数组中
2.byte[] getBytes(String charsetName):使用指定字符集,将String编码为字节数组存储到新的字节数组中
解码:(String的构造器)
1.String(byte[] bytes)
2.String(byte[] bytes,String charsetName)
以下是示例图:
2. io流
什么是io流?
i就是input(叫做输入流)就是从网络或者磁盘文件等中读取数据到程序中;o就是output(叫做输出流)就是将程序中的数据写入到网络或者磁盘文件等中。
io流的分类:
按照流向分:输入流、输出流
按照流中数据的最小单元分:字节流、字符流
大类:字节输入流,字节输出流,字符输入流,字符输出流
io流有什么用?
io流一般应用场景:
记事本记事,游戏最高分存储,通信等
io流怎么用?
首先我们要知道四大类:字节输入流,字节输出流,字符输入流,字符输出流。他们其实都是抽象类,使用时需要使用其子类(实现类),下面将分别介绍两大类(字节输入输出流)的实现类及其用法:
3. FileInputStream实现类
使用步骤:
1.创建输入流管道(对象),建立与源文件的连接(构造器中写文件路径【绝对/相对】)
2.以字节形式从文件中读取数据:read()方法返回编码(可以转换类型为显而易见的类型);可以使用while循环读取大量字节
注意事项:
1.不能读取汉字,会出现乱码现象(read()只读取一个字节,在UTF-8中汉字占3个字节)
2.读取数据的性能太差
3.任何流的使用后都要关闭流,调用close方法
import java.io.FileInputStream;
import java.io.IOException;public class Main {public static void main(String[] args) throws IOException {//完成 FileInputStream实现类 的 1)使用流程 2)检查read() 和 read(byte[] buffer)的注意事项:关于汉字是否可以读取//1.FileInputStream实现类是InputStream类的一个实现类,具体是字节输入流,以字节的形式从磁盘或网络上读取数据到程序中;//read()具体的使用步骤如下://1)第一步:建立程序和源文件(获取源)的管道,也就是创建FileInputStream实现类的一个对象;//2)第二步:用对象调用read()方法,得到数据的编码,每次只能得到一个字节的数据;//3)第三步:关闭流;FileInputStream is = new FileInputStream("hellomodule\\src\\com\\yym\\test\\yjwj.txt");//后面的路径可以是:绝对路径、相对路径(当前工作目录下的)//一个一个字节读入;
// int ch = is.read();
// int ch1 = is.read();
// int ch2 = is.read();
// System.out.print((char)ch);
// System.out.print((char)ch1);
// System.out.print((char)ch2);// int b;
// while((b = is.read()) != -1){
// System.out.print((char)b);
// }//2.使用read(byte[] buffer)//1.第一步:建立程序和源文件(获取源)的管道,也就是创建FileInputStream实现类的一个对象;//2.准备byte[] buffer的大小;//3.is调用read(byte[] buffer),得到大量的编码,并返回读取的字节数;byte[] buffer = new byte[6];//读取一次读取5个字节到字节数组中;
// int len = is.read(buffer);// for (int i = 0; i < buffer.length; i++) {
// System.out.print((char)buffer[i]);
// }
// System.out.println();//for循环可以改变为String类型;//String rs = new String(buffer);// System.out.println(len);int len;while((len = is.read(buffer))!=-1){//String rs = new String(buffer);String rs = new String(buffer,0,len);System.out.println(rs);System.out.println(len);}//但是现在有一个问题是覆盖之后,会将之前读到的字节保留并一并读出,现在的目的是:想要读多少字节就倒出多少字节;//只需要使用String的另外一个构造器:String rs = new String(buffer,0,len);//3.关于能不能读汉字问题:首先read()不可以读汉字,read(byte[] buffer)可以读入汉字,但是要保证一个汉字读入的字节数为3//所以有一个问题:在英文汉字都有的而且字数不确定的文章中,无法保证读入的正确性;除非已知文件字节数大小,一次性读出。//因此:会有字符输入流解决相关问题;is.close();}
}
对于在英文汉字都有的而且字数不确定的文章中,无法保证读入的正确性的问题,有以下两种解决措施:
方法一:
方法一中,size作为参数时需要强制转换为int,原因如下:
在byte数组的定义中,大小不能超过int类型,size的类型是一个long类型,直接用会报错,所以要强制转换。一般文件不会特别大,因此可以用这样的代码;但是有100GB大小的文件,然而,内存只有32GB甚至更小,这样就不能new出一个字节很大的byte数组来一次读取文件。
方法二:直接替换定义size开始的四行代码(更方便,但是仍然没有解决文件过大时的读取问题)
4. FileOutputStream实现类
使用步骤:
1.创建输入流管道(对象),建立与源文件的连接(构造器中写文件路径【绝对/相对】)
2.以字节形式从文件中读取数据:read(byte[] buffer)方法得到编码(可以转换类型为显而易见的类型);返回读取的字节数量
注意事项:
1.不能读取汉字,会出现乱码现象(read()只读取一个字节,在UTF-8中汉字占3个字节)
2.读取数据的性能太差
3.任何流的使用后都要关闭流,调用close方法
注意事项:
1.读取多少,倒多少(buffer数组可以多次使用,但是非首次使用时会覆盖前面位置的数据,为了防止后面出现bug,非首次读取定长后一定要倒出)eg:文件内容:abc66 ,buffer大小为3 ,第一次读取:【a,b,c】,第二次读取:【6,6,c】。其实第二次读了2个字节,应该倒出2个字节的数据【6,6】.
2每次读取完毕返回-1
使用流程几乎相同,只有划红线部分:如果是true,那么就支持向文件中追加,不然默认是false,就是先清空再写入。
注意:
1.在new出一个输出流管道时,可以直接跟一个不存在的路径,会自动创建文件。
2.write方法可以写入任何内容文件,在前后文件结构相同的情况下不出现乱码。
原因:
1.write方法可以写入任何类型的字节数据,因为它本质上不关心数据的语义或结构——它只负责将字节序列写入到输出目的地。
2.字节输出流是字节为中心的,它不涉及字符编码转换。无论你传递给它的是文本数据、
图片视频还是其他任何二进制文件,他都会写入这些字节
5. 文件复制demo
字节流适合做数据的转移,如:文件复制等
使用字节输入输出流进行文件复制,适用于任何文件:
import java.io.FileInputStream;
import java.io.FileOutputStream;public class Main {public static void main(String[] args) throws Exception {//目的:完成复制文件的操作;这里举例一张图片的复制;//基本思路://1.字节输入流连通源文件//2.字节输出流连通目标文件//3.将读入的字节放到字节数组中,再写入到目标文件中FileInputStream is = new FileInputStream("C:\\Users\\11067\\Pictures\\Screenshots\\屏幕截图 2023-10-20 175018.png");FileOutputStream os = new FileOutputStream("D:\\学习文档\\屏幕截图 2023-10-20 175018.png");//1.不管文件多大,直接用数组装
// byte[] buffer = new byte[3];
// int len;
// while((len = is.read(buffer)) != -1){
// os.write(buffer,0,len);
// }
// System.out.println("复制完成!");//2.直接用readAllBytesbyte[] buffer = is.readAllBytes();os.write(buffer);System.out.println("复制完成!");os.close();is.close();}
}
6. 释放资源的方式
最初,我们会手动的用close方法释放资源对象,但是如果在释放前程序异常终止,那么资源就得不到释放,已知占用内存,此时就提出了:try-catch-finally(用于捕获异常,并且不论是否有异常,程序都可以执行finally区去释放资源对象,除非JVM终止),如:
这是一段核心代码,可见他的结构臃肿,不方便。因此,我们又提出了 try-with-resouces,使用框架: