IO流
掌握:
字符流:FileReader,FileWriter,
字节流:FileInputStream, FileOutputStream
缓冲流:
字符: BufferedReader,BudderesWriter
字节:BufferedInputStream,BufferedOutputStream
转换流:
字符-->字节:OutputStreamWriter
字节-->字符:InputStreamReader
对象流:
之前我们建立一个文件的时候可以使用File类来创建,但是文件或者目录创建完毕之后怎么对其中的内容进行修改呢?这个时候就要使用流来进行文件内容的操作
Java IO流的原理
IO流就是为了让文件内部与程序交互的
IO流分为输入流和输出流
输入(Input)
输入input:读取外部数据(磁 盘、光盘等存储设备的数据)到 程序(内存)中。
输出(output)
输出output:将程序(内存) 数据输出到磁盘、光盘等存储设 备中。
何为输入输出:
输入输出要看站在谁的角度 ,要是站在内存/程序的角度 站在程序/内存的角度看是写入数据还是读取数据
当网络或者文件向程序/内存中写入数据就是input(输入)
当外部的网络或者文件向程序/内存中读取数据就是output(输出)
流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
一: 流的分类
1: 操作数据单位: 字节流 字符流
2: 数据的流向 :输入流 输出流
3: 流的角色: 节点流,处理流
流处理的选择
非文本的: 图片或者语音等按照字节流处理(字节流是底层0101) 文本的: 按照字符处理(char:a b c )
本身的底层有节点流(字节流或者字符流)处理数据的时候可以在其外部包裹一层处理流来加速其传输
流的体系结构
体系结构
抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BufferedReader
Writer FileWriter BufferedWriter
文件存放的位置
如果是再Test中写的代码读取的相对文件的位置就是当前项目下Project下,
流的创建与使用
流创建的步骤
File对象可以理解为流的抓手,主要时为流进行文件抓捕的操作,问流选取文件,然后流再操作文件
1: 创建File对象,需要创建File对象来指定要操作的文件 2: 创建流,把要操作的文件提供给具体的流 3: 文件数据的操作,使用流对具体的文件内容中的数据进行输入输出的操作 4: 流的关闭,虽然Java本身有垃圾回收机制,但是对于物理连接,如:数据库连接,输入输出流,Scocket连接时无法回收关闭的
eg:
Read的操作
File的实例化确保文件存在
FileReader流的实例化
读入的操作
流资源的关闭
Writer
1: 提供File类的修对象,指明写出到的文件(确定要出书的文件)
2: 提供具体的流操作对象,并将要操作的文件给流对象
3: 数据的输出
4: 流的关闭
流在具体操作文件的注意点:
说明点: Read: 1: read()的理解:返回读入的一个字符,如果达到文件末尾就会返回-1 2:异常的处理: 为了保证流资源一定要关闭 ,要使用try{}catch{}finally{} 在抛出异常的时候尽量不要用throw因为在中间执行流的操作还没有关闭流的时候报错,那么就会造成内存的溢出之类的,所以要用try{}catch{}finally{} 3: 读入的文件一定要存在,否则就会报:FileNotFoundException Write: 1:如果写出的操作文件不存在不会报错,并不会报异常 2: File对应的硬盘文件不存在,在输出的过程中会自动创建此文件 File对应的文件存在: 如果流使用的构造器:FileWriter(file,false)/FileWriter(file)是对原有文件的覆盖 如果流使用的构造器:FileWriter(file,true)/FileWriter(file)不是对原有文件覆盖,而是对原有文件的追加
字符流 : FileReader() 与FileWriter()
Reader() int read() 读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个 字节的Unicode码),如果已到达流的末尾,则返回 -1 int read(char[] cbuf) 将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。 int read(char[] cbuf,int off,int len) 将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字 符。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。 public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。 Writer() void write(int c) 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即 写入0 到 65535 之间的Unicode码。 void write(char[] cbuf) 写入字符数组。 void write(char[] cbuf,int off,int len) 写入字符数组的某一部分。从off开始,写入len个字符 void write(String str) 写入字符串。 void write(String str,int off,int len) 写入字符串的某一部分。 void flush() 刷新该流的缓冲,则立即将它们写入预期目标。 public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。
char[] 存放元素
取出的数据如果放在数组中,如果数组指定是5 你取8个字符eg [a,b,c,d,e,f,g,h] 每次取5个 char [] ch = new char[5],再第二次取数值的时候显示的是:
[f,g,h,d,e] 因为第一次取的是[a,b,c,d,e]第二次取值是还放在这个数据内的,但是一共有8个第二次只取3个还有个没有没有覆盖
Reader()读取文件内容 (char数据的操作)
@Test public void TestFive() { FileReader fr = null; // 将会抛出异常,先抛出throw Alt+Enter try { //1 创建File对象指定要操作的文件 File fileOne = new File("fileTestOne"); //2 : 创建Io流对象并获取File抓手指定的文件 fr = new FileReader(fileOne); //3 对文件进行操作 int data = fr.read(); // read()会获取问价中的内容个数,当获取的为空时就是-1, 也会抛出 这个时候也先用throw Alt+Enter // 4 对获取的内容进行打印 while (data != -1){ // 当获取到信息时 System.out.print((char)data); // 因为data这个时候是取出的字符的个数 所以要转化为字符 data = fr.read(); // 继续取上面只是取了一次,这是不停的取 } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ fr.close(); // 关闭流 } } // 操作完成之后就对上面的异常进行统一的try异常处理 ,选中上面所有的按 Ctrl+Alt+T 进行统一的放入try中 // 将关闭流的操作放入上面的finally中 }
2
@Test public void TestTwo(){ FileReader fr = null; // 没有创建流 try { // 1: File对象的创建,指定要操作的文件 File fileOne = new File("FileOO"); //2:流对象的创建,并将要File对象指定的操作的文件给流 fr = new FileReader(fileOne); // 这一步会抛出异常我们要使用异常处理,异常处理的时候尽量用try{}catch{}不会阻挡下面的流程 int data = fr.read(); // read():返回读入的字符个数。如果达到文件末尾,返回-1 while (data != -1){ System.out.print((char) data); // 因为这个时候data是字符的个数 要转化为字符输出 data = fr.read(); } } catch (IOException e) { e.printStackTrace(); } finally { // 流的关闭 try { if(fr != null){ // 因为流对象创建了才会关闭 最初进来的流是null不用关闭,因为没有创建流对象 fr.close(); // 流的关闭 } } catch (IOException e) { e.printStackTrace(); } } }
@Test public void TestEight(){ FileReader frr= null; try { File file = new File("fileTestOne"); frr = new FileReader(file); int data; while ((data = frr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { if(frr != null){ try { frr.close(); } catch (IOException e) { e.printStackTrace(); } } }
/* 将day09下的hello.txt文件内容读入程序中,并输出到控制台 说明点: 1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理 3. 读入的文件一定要存在,否则就会报FileNotFoundException。 */ @Test public void testFileReader(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt");//相较于当前Module //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的一个字符。如果达到文件末尾,返回-1 //方式一: // int data = fr.read(); // while(data != -1){ // System.out.print((char)data); // data = fr.read(); // } //方式二:语法上针对于方式一的修改 int data; while((data = fr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { //4.流的关闭操作 // try { // if(fr != null) // fr.close(); // } catch (IOException e) { // e.printStackTrace(); // } //或 if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
使用read的重载方法
@Test public void TestNine() throws IOException { FileReader frr = null; try { // File 抓手获取到要操作的文件 File file = new File("fileTestOne"); // 2 流的创建 对抓手File类获取的文件进行操作 frr = new FileReader(file); // 3: 读入的操作 // read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char [] cbuf = new char[5]; // 创建长度为5的数组 int len = frr.read(cbuf); // read(char [] ) 长度为5的数组 一次获取五个字符,然后当获取的时空仍然是-1 while (len != -1){ // System.out.print(len); // len是数组的长度 数组的内容要遍历 for (char s:cbuf ) { System.out.print(s); } len = frr.read(cbuf); } } catch (IOException e) { e.printStackTrace(); } finally { if(frr != null){ try { frr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
//对read()操作升级:使用read的重载方法 @Test public void testFileReader1() { FileReader fr = null; try { //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //方式一: //错误的写法 // for(int i = 0;i < cbuf.length;i++){ // System.out.print(cbuf[i]); // } //正确的写法 // for(int i = 0;i < len;i++){ // System.out.print(cbuf[i]); // } //方式二: //错误的写法,对应着方式一的错误的写法 // String str = new String(cbuf); // System.out.print(str); //正确的写法 String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void TestTen() { FileReader fileReader = null; try { File fileOne = new File("fileTestOne"); fileReader = new FileReader(fileOne); char [] buf = new char[5]; // 数组用来存放读出的字符元素 int len;// 读出的字符的个数 while ((len = fileReader.read(buf)) != -1){ // 方式一 // for (int i = 0; i < len; i++) { // System.out.print(buf[i]); // } // 方式二 String s = new String(buf,0,len); System.out.print(s); } } catch (IOException e) { e.printStackTrace(); } finally { if(fileReader != null){ try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
int len是表示读出了多少个字符个数,到达文件末尾就是-1,用来判断是否到了文件末尾,char/byte数组用来存放读出的元素
从内存中写出数据到硬盘中
FileWriter()
说明: 1. 输出操作,对应的File可以不存在的。并不会报异常 2. File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。 File对应的硬盘中的文件如果存在: 如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖 如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
new FileWriter(File, false) /new FilwWriterr(File) 是对文件的覆盖
new FileWriter(File,True)是对文件的内容的追加
@Test public void TestEleven() { FileWriter fw = null; try { // 1: 提供File类的对象让File抓手类指明到要操作的文件 File file = new File("WriteTestOne"); // 2: 创建FileWriter流类 用来操作数据 fw = new FileWriter(file); // 3:写出数据的操作 fw.write("I'm a super man\n"); fw.write("get out"); } catch (IOException e) { e.printStackTrace(); } finally { if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void TestTwelve() throws IOException { FileWriter fw = null; try { // 指明要输出的文件 File file = new File("WriteTestOne"); // 流 fw = new FileWriter(file,true); // 追加 int len; fw.write("\n"); fw.write("老王在你家"); fw.write("赶紧回家去"); } catch (IOException e) { e.printStackTrace(); } finally { if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
文件的写入写出功能
@Test public void testFileReaderFileWriter() { FileReader fr = null; FileWriter fw = null; try { //1.创建File类的对象,指明读入和写出的文件 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); //不能使用字符流来处理图片等字节数据 // File srcFile = new File("爱情与友情.jpg"); // File destFile = new File("爱情与友情1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); //3.数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while((len = fr.read(cbuf)) != -1){ //每次写出len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关闭流资源 //方式一: // try { // if(fw != null) // fw.close(); // } catch (IOException e) { // e.printStackTrace(); // }finally{ // try { // if(fr != null) // fr.close(); // } catch (IOException e) { // e.printStackTrace(); // } // } //方式二: try { if(fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } }
以上只能处理字符不可以处理字节
流的读写注意点:
当流是先读后写的话 流的关闭可以不分先后 ,因为此时文件已经存在 ,因为是读的话文件必须存在
而先写后读的话就要文件可以不存在等写文件时候创建, 创建完写完数据之后要关闭流 写入流操作完文件之后 这个时候读文件才能获取到数据
eg:
/* 先写入数据后读取 */ @Test public void TestSixteen(){ FileReader fr = null; FileWriter fw = null; try { fw = new FileWriter(new File("helloOne.txt")); fw.write("欢迎您来到湾区旅游"); fw.close(); //因为是先写后读的 所以要先写完再去读 那就要先把写的动作全部做完 fr = new FileReader(new File("E:\\Tool\\Java\\BasiceCodes\\basiceCodeMay\\helloOne.txt")); int len; while ((len = fr.read()) != -1){ System.out.print((char)len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fr != null){ fr.close(); } } catch (IOException e) { e.printStackTrace(); } } }
字节流:
FileInputStream : 读取字节流
FileOutputStream : 输出字符流
FileInputStream
int read() 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因 为已经到达流末尾而没有可用的字节,则返回值 -1。 int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已 经到达流末尾而没有可用的字节,
则返回值 -1。否则以整数形式返回实际读取 的字节数。 int read(byte[] b, int off,int len) 将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取 的字节也可能小于该值。
以整数形式返回实际读取的字节数。如果因为流位于 文件末尾而没有可用的字节,则返回值 -1。 public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。
FileOutputStream
void write(int b) 将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写 入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的。 void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是:应该 与调用 write(b, 0, b.length) 的效果完全相同。 void write(byte[] b,int off,int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 public void flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立 即写入它们预期的目标。 public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。
字节流的使用和字符流一样
读取文件 1.建立一个流对象,将已存在的一个文件加载进流。
FileReader fr = new FileReader(new File(“Test.txt”)); 2.创建一个临时存放数据的数组。
char[] ch = new char[1024]; 3.调用流对象的读取方法将流中的数据读入到数组中。
fr.read(ch); 4. 关闭资源。
fr.close(); 写入文件 1.创建流对象,建立数据存放文件
FileWriter fw = new FileWriter(new File(“Test.txt”)); 2.调用流对象的写入方法,将数据写入流
fw.write(“atguigu-songhongkang”); 3.关闭流资源,并将流中的数据清空到文件中。
fw.close();
eg:
复制一个图片
方式一
@Test public void TestTwo(){ FileInputStream fs = null; FileOutputStream fw = null; try { File fileOne = new File("byteOne.jpg"); File fileTwo = new File("byteTwo.jpg"); fs = new FileInputStream(fileOne); fw = new FileOutputStream(fileTwo); byte[] bytes = new byte[1024]; int len; while ((len = fs.read(bytes)) != -1){ fw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } } 方式二: @Test public void TestThree() { FileInputStream fs = null; FileOutputStream fw = null; try { // 创造抓手File对象指定操作文件 File fileOne = new File("byteOne.jpg"); // 指定要操作的文件 File fileTwo = new File("byteTwo.jpg"); // 指定要复制出的文件 // 创造流 fs = new FileInputStream(fileOne); // 创造读取的字节流 fw = new FileOutputStream(fileTwo); // 创造输出流 // 操作字节 int len; while ((len = fs.read()) != -1){ fw.write(len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
复制非文本文件的方法
// 复制图片方法 public void FileInputOutputStream(String fileS, String fileW){ FileInputStream fs = null; FileOutputStream fw = null; try { File fileOne = new File(fileS); File fileTwo = new File(fileW); // FileInputStream fs = new FileInputStream(fileOne); // FileOutputStream fw = new FileOutputStream(fileW); fs = new FileInputStream(new File(fileS)); fw = new FileOutputStream(new File(fileW)); int len; byte [] bytes = new byte[1024]; while ((len = fs.read(bytes)) != -1){ fw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestFour(){ long start = System.currentTimeMillis(); // 开始时间 FileInputOutputStream("byteTwo.jpg", "byteThree.jpg"); long end = System.currentTimeMillis(); // 结束时间 System.out.println("复制共计耗时" + (end - start)); // 共计耗时 }
节点流(文件流)使用的注意点
定义文件路径时,注意:可以用“/”或者“\\”。 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文 件将被覆盖。 如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖, 在文件内容末尾追加内容。 在读取文件时,必须保证该文件已存在,否则报异常。 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt 字符流操作字符,只能操作普通文本文件。最常见的文本文 件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文 本文件。
缓冲流:
什么是缓冲流?
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类 时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为: BufferedInputStream 和 BufferedOutputStream BufferedReader 和 BufferedWriter
就是为了加速流的传输而存在的流,将会提高其包裹的流的速度
缓冲流能提速的原因 :
提高读写速度的原因:内部提供了一个缓冲区
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从 文件中读取8192个(8Kb),
存在缓冲区中,直到缓冲区装满了,才重新从文件中 读取下一个8192个字节数组。
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也 会相应关闭内层节点流
flush()方法的使用:手动将buffer中内容写入文件
如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷 新缓冲区,关闭后不能再写出
缓冲流的使用:
@Test public void TestOne(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { // 指定文件 File fileOne = new File("byteOne.jpg"); File fileTwo = new File("byteFive.jpg"); // 造操作流 FileInputStream fs = new FileInputStream(fileOne); FileOutputStream fw = new FileOutputStream(fileTwo); // 造缓冲流 bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bw.write(bytes); } } catch (IOException e) { e.printStackTrace(); } finally { // 资源关闭 ,流的关闭 我们只需要关闭外部的缓冲流即可 因为缓冲流关闭 内部的流也随着关闭 try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
缓冲流和不适用缓冲流对比
使用缓冲流:
public void BufferTestIo(String fileOne, String fileTwo){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream(fileOne); FileOutputStream fw = new FileOutputStream(fileTwo); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestTwo(){ long start = System.currentTimeMillis(); // BufferIo("byteOne.jpg","byteThree.jpg"); BufferTestIo("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4","E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制One.mp4"); long end = System.currentTimeMillis(); System.out.println("共计耗时"+(end-start)); // 共计耗时830 }
单纯的字节流:
@Test public void TestFour(){ long start = System.currentTimeMillis(); // 开始时间 FileInputOutputStream("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4", "E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制Two.mp4"); long end = System.currentTimeMillis(); // 结束时间 System.out.println("复制共计耗时" + (end - start)); // 复制共计耗时896 }
可以看出缓冲流是提高的效率的 所以以后要多使用缓冲流
缓冲流处理 文本文件
@Test public void TestTwo() { long start = System.currentTimeMillis(); // BufferIo("byteOne.jpg","byteThree.jpg"); BufferTestIo("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4", "E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制One.mp4"); long end = System.currentTimeMillis(); System.out.println("共计耗时" + (end - start)); } public void BufferFileIo(String fileOne, String fileTwo) { BufferedReader br = null; BufferedWriter bw = null; try { FileReader fs = new FileReader(fileOne); FileWriter fw = new FileWriter(fileTwo); br = new BufferedReader(fs); bw = new BufferedWriter(fw); int len; while ((len = br.read()) != -1) { bw.write(len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestFour() { long start = System.currentTimeMillis(); BufferFileIo("fileTestOne", "fileTestTwo"); long end = System.currentTimeMillis(); System.out.println("时间为"+(end - start)); }
其中有两个参数read. Line()一行一行的读取 write.newLine()换行操作
缓冲流练习:
1:
分别使用节点流:FileInputStream、FileOutputStream和缓冲流: BufferedInputStream、BufferedOutputStream实现文本文件/图片/视频文件的 复制。并比较二者在数据复制方面的效率
2: 实现图片加密操作。 提示:
@Test public void TestSix(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream("byteThree.jpg"); FileOutputStream fw = new FileOutputStream("byteThreeScree.jpg"); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[20]; // 因为要对内部的每一个字节加密而不是整体的加密所以要用数组 while ((len = bs.read(bytes) ) != -1){ for (int i = 0; i < len ; i++) { //对字节的每一个都进行加密 bytes[i] = (byte) (bytes[i] ^ 5); } bw.write(bytes); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } // 解密 @Test public void TestSeven(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream("byteThreeScree.jpg"); FileOutputStream fw = new FileOutputStream("yteThreeScreeOne.jpg"); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] by = new byte[20]; while ((len = fs.read(by)) != -1){ for (int i = 0; i < len ; i++) { by[i] = (byte) (by[i] ^ 5); } bw.write(by); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
3:
获取文本上每个字符出现的次数
提示:遍历文本的每一个字符;字符及出现的次数保存在Map中;将Map中数据 写入文件
/* 说明:如果使用单元测试,文件相对路径为当前module 如果使用main()测试,文件相对路径为当前工程 */ @Test public void testWordCount() { FileReader fr = null; BufferedWriter bw = null; try { //1.创建Map集合 Map<Character, Integer> map = new HashMap<Character, Integer>(); //2.遍历每一个字符,每一个字符出现的次数放到map中 fr = new FileReader("dbcp.txt"); int c = 0; while ((c = fr.read()) != -1) { //int 还原 char char ch = (char) c; // 判断char是否在map中第一次出现 if (map.get(ch) == null) { map.put(ch, 1); } else { map.put(ch, map.get(ch) + 1); } } //3.把map中数据存在文件count.txt //3.1 创建Writer bw = new BufferedWriter(new FileWriter("wordcount.txt")); //3.2 遍历map,再写入数据 Set<Map.Entry<Character, Integer>> entrySet = map.entrySet(); for (Map.Entry<Character, Integer> entry : entrySet) { switch (entry.getKey()) { case ' ': bw.write("空格=" + entry.getValue()); break; case '\t'://\t表示tab 键字符 bw.write("tab键=" + entry.getValue()); break; case '\r':// bw.write("回车=" + entry.getValue()); break; case '\n':// bw.write("换行=" + entry.getValue()); break; default: bw.write(entry.getKey() + "=" + entry.getValue()); break; } bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关流 if (fr != null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } if (bw != null) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
转换流:
转换流的作用:
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流: InputStreamReader:将InputStream转换为Reader OutputStreamWriter:将Writer转换为OutputStream * InputStreamReader:将一个字节的输入流转换为字符的输入流 * OutputStreamWriter:将一个字符的输出流转换为字节的输出流
字节流中的数据都是字符时,转成字符流操作更高效。
很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。
InputStreamReader
实现将字节的输入流按指定字符集转换为字符的输入流。 需要和InputStream“套接”。 构造器
public InputStreamReader(InputStream in) public InputSreamReader(InputStream in,String charsetName)
如:
Reader isr = new InputStreamReader(System.in,”gbk”);
OutputStreamWriter
实现将字符的输出流按指定字符集转换为字节的输出流。 需要和OutputStream“套接”。 构造器 public OutputStreamWriter(OutputStream out) public OutputSreamWriter(OutputStream out,String charsetName)
@Test public void TestOne(){ InputStreamReader isr = null; try { FileInputStream fs = new FileInputStream("fileTestOne"); isr = new InputStreamReader(fs,"UTF-8"); char [] chars = new char[20]; int len; while ((len = isr.read(chars)) != -1){ String s = new String(chars,0,len); System.out.println(s); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(isr != null){ isr.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestTwo(){ InputStreamReader isr = null; OutputStreamWriter osw = null; try { FileInputStream fs = new FileInputStream("fileTestOne"); FileOutputStream fw = new FileOutputStream("fileTestFour"); isr = new InputStreamReader(fs); osw = new OutputStreamWriter(fw); int len; char [] chars = new char[20]; while ((len = isr.read(chars)) != -1){ String s = new String(chars,0,len); // 转化为字符串 osw.write(s); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(isr != null){ isr.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(osw != null){ osw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
综合使用InputStreamReader和OutputStreamWriter
@Test public void test2() throws Exception { //1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.读写过程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.关闭资源 isr.close(); osw.close(); }
Practices:
1. 说明流的三种分类方式
流向:输入流、输出流
数据单位:字节流、字符流
流的角色:节点流、处理流
2. 写出4个IO流中的抽象基类,4个文件流,4个缓冲流
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BudderedReader
Writer FileWriter BufferedWriter
Io父类 | 基础处理流 | 缓冲流 | 转换流 | ||
InputStream | FileInputStream | BufferedInputStream | 操作字符 | InputStreamReader | 将字节转化为字符 char----> String |
OutputStream | FileOutputStream | BufferedOutputStream | 操作字符 | OutputStreamWriter | 将字符串转化为字符写入程序中 String ---> char |
Reader | FileReader | BufferedReader | 操作字符串(文本文件) | ||
Writer | FileWriter | BufferedWriter | 操作字符串(文本文件) |
3. 字节流与字符流的区别与使用情境
字节流:read(byte[] buffer) / read() 非文本文件 字符流:read(char[] cbuf) / read() 文本文件
4. 使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
@Test public void TestOne(){ BufferedInputStream bs = null; BufferedOutputStream bp = null; try { FileInputStream fs = new FileInputStream("a.jpg"); // 字节流操作文件 FileOutputStream fo = new FileOutputStream("b.jpg"); bs = new BufferedInputStream(fs); // 缓冲流加速 bp = new BufferedOutputStream(fo); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bp.write(bytes,0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bp != null){ bp.close(); } } catch (IOException e) { e.printStackTrace(); } } }
5: 转换流是哪两个类,分别的作用是什么?请分别创建两个类的对象。
InputStreamReader 字节流--->字符流 将输入的字节流转换为输入的字符流。 解码 outputStreamWriter 字符流---->字节流 将输出的字符流转换为输出的字节流。编码 InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”utf-8”); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“b.txt”),”gbk”);
.
------------恢复内容开始------------
掌握:
字符流:FileReader,FileWriter,
字节流:FileInputStream, FileOutputStream
缓冲流:
字符: BufferedReader,BudderesWriter
字节:BufferedInputStream,BufferedOutputStream
转换流:
字符-->字节:OutputStreamWriter
字节-->字符:InputStreamReader
对象流:
之前我们建立一个文件的时候可以使用File类来创建,但是文件或者目录创建完毕之后怎么对其中的内容进行修改呢?这个时候就要使用流来进行文件内容的操作
Java IO流的原理
IO流就是为了让文件内部与程序交互的
IO流分为输入流和输出流
输入(Input)
输入input:读取外部数据(磁 盘、光盘等存储设备的数据)到 程序(内存)中。
输出(output)
输出output:将程序(内存) 数据输出到磁盘、光盘等存储设 备中。
何为输入输出:
输入输出要看站在谁的角度 ,要是站在内存/程序的角度 站在程序/内存的角度看是写入数据还是读取数据
当网络或者文件向程序/内存中写入数据就是input(输入)
当外部的网络或者文件向程序/内存中读取数据就是output(输出)
流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
一: 流的分类
1: 操作数据单位: 字节流 字符流
2: 数据的流向 :输入流 输出流
3: 流的角色: 节点流,处理流
流处理的选择
非文本的: 图片或者语音等按照字节流处理(字节流是底层0101) 文本的: 按照字符处理(char:a b c )
本身的底层有节点流(字节流或者字符流)处理数据的时候可以在其外部包裹一层处理流来加速其传输
流的体系结构
体系结构
抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BufferedReader
Writer FileWriter BufferedWriter
流的创建与使用
流创建的步骤
File对象可以理解为流的抓手,主要时为流进行文件抓捕的操作,问流选取文件,然后流再操作文件
1: 创建File对象,需要创建File对象来指定要操作的文件 2: 创建流,把要操作的文件提供给具体的流 3: 文件数据的操作,使用流对具体的文件内容中的数据进行输入输出的操作 4: 流的关闭,虽然Java本身有垃圾回收机制,但是对于物理连接,如:数据库连接,输入输出流,Scocket连接时无法回收关闭的
eg:
Read的操作
File的实例化确保文件存在
FileReader流的实例化
读入的操作
流资源的关闭
Writer
1: 提供File类的修对象,指明写出到的文件(确定要出书的文件)
2: 提供具体的流操作对象,并将要操作的文件给流对象
3: 数据的输出
4: 流的关闭
流在具体操作文件的注意点:
说明点: Read: 1: read()的理解:返回读入的一个字符,如果达到文件末尾就会返回-1 2:异常的处理: 为了保证流资源一定要关闭 ,要使用try{}catch{}finally{} 在抛出异常的时候尽量不要用throw因为在中间执行流的操作还没有关闭流的时候报错,那么就会造成内存的溢出之类的,所以要用try{}catch{}finally{} 3: 读入的文件一定要存在,否则就会报:FileNotFoundException Write: 1:如果写出的操作文件不存在不会报错,并不会报异常 2: File对应的硬盘文件不存在,在输出的过程中会自动创建此文件 File对应的文件存在: 如果流使用的构造器:FileWriter(file,false)/FileWriter(file)是对原有文件的覆盖 如果流使用的构造器:FileWriter(file,true)/FileWriter(file)不是对原有文件覆盖,而是对原有文件的追加
字符流 : FileReader() 与FileWriter()
Reader() int read() 读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个 字节的Unicode码),如果已到达流的末尾,则返回 -1 int read(char[] cbuf) 将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。 int read(char[] cbuf,int off,int len) 将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字 符。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。 public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。 Writer() void write(int c) 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即 写入0 到 65535 之间的Unicode码。 void write(char[] cbuf) 写入字符数组。 void write(char[] cbuf,int off,int len) 写入字符数组的某一部分。从off开始,写入len个字符 void write(String str) 写入字符串。 void write(String str,int off,int len) 写入字符串的某一部分。 void flush() 刷新该流的缓冲,则立即将它们写入预期目标。 public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。
char[] 存放元素
取出的数据如果放在数组中,如果数组指定是5 你取8个字符eg [a,b,c,d,e,f,g,h] 每次取5个 char [] ch = new char[5],再第二次取数值的时候显示的是:
[f,g,h,d,e] 因为第一次取的是[a,b,c,d,e]第二次取值是还放在这个数据内的,但是一共有8个第二次只取3个还有个没有没有覆盖
Reader()读取文件内容 (char数据的操作)
@Test public void TestFive() { FileReader fr = null; // 将会抛出异常,先抛出throw Alt+Enter try { //1 创建File对象指定要操作的文件 File fileOne = new File("fileTestOne"); //2 : 创建Io流对象并获取File抓手指定的文件 fr = new FileReader(fileOne); //3 对文件进行操作 int data = fr.read(); // read()会获取问价中的内容个数,当获取的为空时就是-1, 也会抛出 这个时候也先用throw Alt+Enter // 4 对获取的内容进行打印 while (data != -1){ // 当获取到信息时 System.out.print((char)data); // 因为data这个时候是取出的字符的个数 所以要转化为字符 data = fr.read(); // 继续取上面只是取了一次,这是不停的取 } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ fr.close(); // 关闭流 } } // 操作完成之后就对上面的异常进行统一的try异常处理 ,选中上面所有的按 Ctrl+Alt+T 进行统一的放入try中 // 将关闭流的操作放入上面的finally中 }
2
@Test public void TestTwo(){ FileReader fr = null; // 没有创建流 try { // 1: File对象的创建,指定要操作的文件 File fileOne = new File("FileOO"); //2:流对象的创建,并将要File对象指定的操作的文件给流 fr = new FileReader(fileOne); // 这一步会抛出异常我们要使用异常处理,异常处理的时候尽量用try{}catch{}不会阻挡下面的流程 int data = fr.read(); // read():返回读入的字符个数。如果达到文件末尾,返回-1 while (data != -1){ System.out.print((char) data); // 因为这个时候data是字符的个数 要转化为字符输出 data = fr.read(); } } catch (IOException e) { e.printStackTrace(); } finally { // 流的关闭 try { if(fr != null){ // 因为流对象创建了才会关闭 最初进来的流是null不用关闭,因为没有创建流对象 fr.close(); // 流的关闭 } } catch (IOException e) { e.printStackTrace(); } } }
@Test public void TestEight(){ FileReader frr= null; try { File file = new File("fileTestOne"); frr = new FileReader(file); int data; while ((data = frr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { if(frr != null){ try { frr.close(); } catch (IOException e) { e.printStackTrace(); } } }
/* 将day09下的hello.txt文件内容读入程序中,并输出到控制台 说明点: 1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理 3. 读入的文件一定要存在,否则就会报FileNotFoundException。 */ @Test public void testFileReader(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt");//相较于当前Module //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的一个字符。如果达到文件末尾,返回-1 //方式一: // int data = fr.read(); // while(data != -1){ // System.out.print((char)data); // data = fr.read(); // } //方式二:语法上针对于方式一的修改 int data; while((data = fr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { //4.流的关闭操作 // try { // if(fr != null) // fr.close(); // } catch (IOException e) { // e.printStackTrace(); // } //或 if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
使用read的重载方法
@Test public void TestNine() throws IOException { FileReader frr = null; try { // File 抓手获取到要操作的文件 File file = new File("fileTestOne"); // 2 流的创建 对抓手File类获取的文件进行操作 frr = new FileReader(file); // 3: 读入的操作 // read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char [] cbuf = new char[5]; // 创建长度为5的数组 int len = frr.read(cbuf); // read(char [] ) 长度为5的数组 一次获取五个字符,然后当获取的时空仍然是-1 while (len != -1){ // System.out.print(len); // len是数组的长度 数组的内容要遍历 for (char s:cbuf ) { System.out.print(s); } len = frr.read(cbuf); } } catch (IOException e) { e.printStackTrace(); } finally { if(frr != null){ try { frr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
//对read()操作升级:使用read的重载方法 @Test public void testFileReader1() { FileReader fr = null; try { //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //方式一: //错误的写法 // for(int i = 0;i < cbuf.length;i++){ // System.out.print(cbuf[i]); // } //正确的写法 // for(int i = 0;i < len;i++){ // System.out.print(cbuf[i]); // } //方式二: //错误的写法,对应着方式一的错误的写法 // String str = new String(cbuf); // System.out.print(str); //正确的写法 String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void TestTen() { FileReader fileReader = null; try { File fileOne = new File("fileTestOne"); fileReader = new FileReader(fileOne); char [] buf = new char[5]; // 数组用来存放读出的字符元素 int len;// 读出的字符的个数 while ((len = fileReader.read(buf)) != -1){ // 方式一 // for (int i = 0; i < len; i++) { // System.out.print(buf[i]); // } // 方式二 String s = new String(buf,0,len); System.out.print(s); } } catch (IOException e) { e.printStackTrace(); } finally { if(fileReader != null){ try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
int len是表示读出了多少个字符个数,到达文件末尾就是-1,用来判断是否到了文件末尾,char/byte数组用来存放读出的元素
从内存中写出数据到硬盘中
FileWriter()
说明: 1. 输出操作,对应的File可以不存在的。并不会报异常 2. File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。 File对应的硬盘中的文件如果存在: 如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖 如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
new FileWriter(File, false) /new FilwWriterr(File) 是对文件的覆盖
new FileWriter(File,True)是对文件的内容的追加
@Test public void TestEleven() { FileWriter fw = null; try { // 1: 提供File类的对象让File抓手类指明到要操作的文件 File file = new File("WriteTestOne"); // 2: 创建FileWriter流类 用来操作数据 fw = new FileWriter(file); // 3:写出数据的操作 fw.write("I'm a super man\n"); fw.write("get out"); } catch (IOException e) { e.printStackTrace(); } finally { if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void TestTwelve() throws IOException { FileWriter fw = null; try { // 指明要输出的文件 File file = new File("WriteTestOne"); // 流 fw = new FileWriter(file,true); // 追加 int len; fw.write("\n"); fw.write("老王在你家"); fw.write("赶紧回家去"); } catch (IOException e) { e.printStackTrace(); } finally { if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
文件的写入写出功能
@Test public void testFileReaderFileWriter() { FileReader fr = null; FileWriter fw = null; try { //1.创建File类的对象,指明读入和写出的文件 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); //不能使用字符流来处理图片等字节数据 // File srcFile = new File("爱情与友情.jpg"); // File destFile = new File("爱情与友情1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); //3.数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while((len = fr.read(cbuf)) != -1){ //每次写出len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关闭流资源 //方式一: // try { // if(fw != null) // fw.close(); // } catch (IOException e) { // e.printStackTrace(); // }finally{ // try { // if(fr != null) // fr.close(); // } catch (IOException e) { // e.printStackTrace(); // } // } //方式二: try { if(fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } }
以上只能处理字符不可以处理字节
流的读写注意点:
当流是先读后写的话 流的关闭可以不分先后 ,因为此时文件已经存在 ,因为是读的话文件必须存在
而先写后读的话就要文件可以不存在等写文件时候创建, 创建完写完数据之后要关闭流 写入流操作完文件之后 这个时候读文件才能获取到数据
eg:
/* 先写入数据后读取 */ @Test public void TestSixteen(){ FileReader fr = null; FileWriter fw = null; try { fw = new FileWriter(new File("helloOne.txt")); fw.write("欢迎您来到湾区旅游"); fw.close(); //因为是先写后读的 所以要先写完再去读 那就要先把写的动作全部做完 fr = new FileReader(new File("E:\\Tool\\Java\\BasiceCodes\\basiceCodeMay\\helloOne.txt")); int len; while ((len = fr.read()) != -1){ System.out.print((char)len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fr != null){ fr.close(); } } catch (IOException e) { e.printStackTrace(); } } }
字节流:
FileInputStream : 读取字节流
FileOutputStream : 输出字符流
FileInputStream
int read() 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因 为已经到达流末尾而没有可用的字节,则返回值 -1。 int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已 经到达流末尾而没有可用的字节,
则返回值 -1。否则以整数形式返回实际读取 的字节数。 int read(byte[] b, int off,int len) 将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取 的字节也可能小于该值。
以整数形式返回实际读取的字节数。如果因为流位于 文件末尾而没有可用的字节,则返回值 -1。 public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。
FileOutputStream
void write(int b) 将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写 入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的。 void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是:应该 与调用 write(b, 0, b.length) 的效果完全相同。 void write(byte[] b,int off,int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 public void flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立 即写入它们预期的目标。 public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。
字节流的使用和字符流一样
读取文件 1.建立一个流对象,将已存在的一个文件加载进流。
FileReader fr = new FileReader(new File(“Test.txt”)); 2.创建一个临时存放数据的数组。
char[] ch = new char[1024]; 3.调用流对象的读取方法将流中的数据读入到数组中。
fr.read(ch); 4. 关闭资源。
fr.close(); 写入文件 1.创建流对象,建立数据存放文件
FileWriter fw = new FileWriter(new File(“Test.txt”)); 2.调用流对象的写入方法,将数据写入流
fw.write(“atguigu-songhongkang”); 3.关闭流资源,并将流中的数据清空到文件中。
fw.close();
eg:
复制一个图片
方式一
@Test public void TestTwo(){ FileInputStream fs = null; FileOutputStream fw = null; try { File fileOne = new File("byteOne.jpg"); File fileTwo = new File("byteTwo.jpg"); fs = new FileInputStream(fileOne); fw = new FileOutputStream(fileTwo); byte[] bytes = new byte[1024]; int len; while ((len = fs.read(bytes)) != -1){ fw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } } 方式二: @Test public void TestThree() { FileInputStream fs = null; FileOutputStream fw = null; try { // 创造抓手File对象指定操作文件 File fileOne = new File("byteOne.jpg"); // 指定要操作的文件 File fileTwo = new File("byteTwo.jpg"); // 指定要复制出的文件 // 创造流 fs = new FileInputStream(fileOne); // 创造读取的字节流 fw = new FileOutputStream(fileTwo); // 创造输出流 // 操作字节 int len; while ((len = fs.read()) != -1){ fw.write(len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
复制非文本文件的方法
// 复制图片方法 public void FileInputOutputStream(String fileS, String fileW){ FileInputStream fs = null; FileOutputStream fw = null; try { File fileOne = new File(fileS); File fileTwo = new File(fileW); // FileInputStream fs = new FileInputStream(fileOne); // FileOutputStream fw = new FileOutputStream(fileW); fs = new FileInputStream(new File(fileS)); fw = new FileOutputStream(new File(fileW)); int len; byte [] bytes = new byte[1024]; while ((len = fs.read(bytes)) != -1){ fw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(fs != null){ fs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(fw != null){ fw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestFour(){ long start = System.currentTimeMillis(); // 开始时间 FileInputOutputStream("byteTwo.jpg", "byteThree.jpg"); long end = System.currentTimeMillis(); // 结束时间 System.out.println("复制共计耗时" + (end - start)); // 共计耗时 }
节点流(文件流)使用的注意点
定义文件路径时,注意:可以用“/”或者“\\”。 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文 件将被覆盖。 如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖, 在文件内容末尾追加内容。 在读取文件时,必须保证该文件已存在,否则报异常。 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt 字符流操作字符,只能操作普通文本文件。最常见的文本文 件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文 本文件。
流获取字符或者字节使用数组的注意点
创建读FileReader/ FileInputStream取流中的存储数组长度的不确定性
因为在读取到数组的时候是最数组内的每一个位置进行覆盖的,如果我们指定一个一个 char [] chars = new char[10]; 那么就是每次最多存放10个字节,后面的只能对前面的进行覆盖 因为在读取文本或者非文本文件时我们会使用数组来存放读取的内容,当指定这个数据如果为10的长度时,
那么有可能会出现读取的字符或者文本不是10的倍数,如果一个非文本中有34个字节,最后的4个字节是 abcd,
最初的10个字节是 [1, 2, 3, 4, 5, 6, 7 ,8, 9, 10],那么在最后的读取的时候就是会出现覆盖,
最后一次存放的数组会显示,[a,b,c,d, 5, 6, 7, 8, 9, 10], 这个时候就能看出数组内的后到的元素是对前面元素的覆盖,
如果这样显示的 最后一次的输出就会是不正确的,那么就要指定大的数组,如果数组指定的非常大的话那么又对内存的开销很大的
ByteArrayInputStream与ByteArrayOutputStream
ByteArrayInputStream 可以将字节数组转化为输入流 。
ByteArrayOutputStream可以捕获内存缓冲区的数据,转换成字节数组
。
ByteArrayInputStream
字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。创建字节数组输入流对象有以下几种方式。
接收字节数组作为参数创建:
构造方法
public ByteArrayInputStream(byte buf[]) public ByteArrayInputStream(byte buf[], int offset, int length)
也就是ByteArrayInputStream中只能放入byte类型的数组
@Test public void TestOne(){ ByteArrayInputStream bis = null; try { byte [] bytes = "hello".getBytes(); bis = new ByteArrayInputStream(bytes); int len; while((len = bis.read()) != -1){ System.out.print((char)len); } } finally { if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
ByteArrayOutputStream
字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。创建字节数组输出流对象有以下几种方式。
构造器
OutputStream bOut = new ByteArrayOutputStream(); 创建一个32字节(默认大小)的缓冲区。 OutputStream bOut = new ByteArrayOutputStream(int a); 创建一个大小为n字节的缓冲区。
缓冲流:
什么是缓冲流?
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类 时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为: BufferedInputStream 和 BufferedOutputStream BufferedReader 和 BufferedWriter
就是为了加速流的传输而存在的流,将会提高其包裹的流的速度
缓冲流能提速的原因 :
提高读写速度的原因:内部提供了一个缓冲区
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从 文件中读取8192个(8Kb),
存在缓冲区中,直到缓冲区装满了,才重新从文件中 读取下一个8192个字节数组。
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也 会相应关闭内层节点流
flush()方法的使用:手动将buffer中内容写入文件
如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷 新缓冲区,关闭后不能再写出
缓冲流的使用:
@Test public void TestOne(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { // 指定文件 File fileOne = new File("byteOne.jpg"); File fileTwo = new File("byteFive.jpg"); // 造操作流 FileInputStream fs = new FileInputStream(fileOne); FileOutputStream fw = new FileOutputStream(fileTwo); // 造缓冲流 bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bw.write(bytes); } } catch (IOException e) { e.printStackTrace(); } finally { // 资源关闭 ,流的关闭 我们只需要关闭外部的缓冲流即可 因为缓冲流关闭 内部的流也随着关闭 try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
缓冲流和不适用缓冲流对比
使用缓冲流:
public void BufferTestIo(String fileOne, String fileTwo){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream(fileOne); FileOutputStream fw = new FileOutputStream(fileTwo); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bw.write(bytes,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestTwo(){ long start = System.currentTimeMillis(); // BufferIo("byteOne.jpg","byteThree.jpg"); BufferTestIo("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4","E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制One.mp4"); long end = System.currentTimeMillis(); System.out.println("共计耗时"+(end-start)); // 共计耗时830 }
单纯的字节流:
@Test public void TestFour(){ long start = System.currentTimeMillis(); // 开始时间 FileInputOutputStream("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4", "E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制Two.mp4"); long end = System.currentTimeMillis(); // 结束时间 System.out.println("复制共计耗时" + (end - start)); // 复制共计耗时896 }
可以看出缓冲流是提高的效率的 所以以后要多使用缓冲流
缓冲流处理 文本文件
@Test public void TestTwo() { long start = System.currentTimeMillis(); // BufferIo("byteOne.jpg","byteThree.jpg"); BufferTestIo("E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制.mp4", "E:\\Tool\\WeiChat\\WeChat Files\\All Users\\机制One.mp4"); long end = System.currentTimeMillis(); System.out.println("共计耗时" + (end - start)); } public void BufferFileIo(String fileOne, String fileTwo) { BufferedReader br = null; BufferedWriter bw = null; try { FileReader fs = new FileReader(fileOne); FileWriter fw = new FileWriter(fileTwo); br = new BufferedReader(fs); bw = new BufferedWriter(fw); int len; while ((len = br.read()) != -1) { bw.write(len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestFour() { long start = System.currentTimeMillis(); BufferFileIo("fileTestOne", "fileTestTwo"); long end = System.currentTimeMillis(); System.out.println("时间为"+(end - start)); }
其中有两个参数read. Line()一行一行的读取 write.newLine()换行操作
缓冲流练习:
1:
分别使用节点流:FileInputStream、FileOutputStream和缓冲流: BufferedInputStream、BufferedOutputStream实现文本文件/图片/视频文件的 复制。并比较二者在数据复制方面的效率
2: 实现图片加密操作。 提示:
@Test public void TestSix(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream("byteThree.jpg"); FileOutputStream fw = new FileOutputStream("byteThreeScree.jpg"); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] bytes = new byte[20]; // 因为要对内部的每一个字节加密而不是整体的加密所以要用数组 while ((len = bs.read(bytes) ) != -1){ for (int i = 0; i < len ; i++) { //对字节的每一个都进行加密 bytes[i] = (byte) (bytes[i] ^ 5); } bw.write(bytes); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } // 解密 @Test public void TestSeven(){ BufferedInputStream bs = null; BufferedOutputStream bw = null; try { FileInputStream fs = new FileInputStream("byteThreeScree.jpg"); FileOutputStream fw = new FileOutputStream("yteThreeScreeOne.jpg"); bs = new BufferedInputStream(fs); bw = new BufferedOutputStream(fw); int len; byte [] by = new byte[20]; while ((len = fs.read(by)) != -1){ for (int i = 0; i < len ; i++) { by[i] = (byte) (by[i] ^ 5); } bw.write(by); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
3:
获取文本上每个字符出现的次数
提示:遍历文本的每一个字符;字符及出现的次数保存在Map中;将Map中数据 写入文件
/* 说明:如果使用单元测试,文件相对路径为当前module 如果使用main()测试,文件相对路径为当前工程 */ @Test public void testWordCount() { FileReader fr = null; BufferedWriter bw = null; try { //1.创建Map集合 Map<Character, Integer> map = new HashMap<Character, Integer>(); //2.遍历每一个字符,每一个字符出现的次数放到map中 fr = new FileReader("dbcp.txt"); int c = 0; while ((c = fr.read()) != -1) { //int 还原 char char ch = (char) c; // 判断char是否在map中第一次出现 if (map.get(ch) == null) { map.put(ch, 1); } else { map.put(ch, map.get(ch) + 1); } } //3.把map中数据存在文件count.txt //3.1 创建Writer bw = new BufferedWriter(new FileWriter("wordcount.txt")); //3.2 遍历map,再写入数据 Set<Map.Entry<Character, Integer>> entrySet = map.entrySet(); for (Map.Entry<Character, Integer> entry : entrySet) { switch (entry.getKey()) { case ' ': bw.write("空格=" + entry.getValue()); break; case '\t'://\t表示tab 键字符 bw.write("tab键=" + entry.getValue()); break; case '\r':// bw.write("回车=" + entry.getValue()); break; case '\n':// bw.write("换行=" + entry.getValue()); break; default: bw.write(entry.getKey() + "=" + entry.getValue()); break; } bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关流 if (fr != null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } if (bw != null) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
转换流:
转换流的作用:
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流: InputStreamReader:将InputStream转换为Reader OutputStreamWriter:将Writer转换为OutputStream * InputStreamReader:将一个字节的输入流转换为字符的输入流 * OutputStreamWriter:将一个字符的输出流转换为字节的输出流
字节流中的数据都是字符时,转成字符流操作更高效。
很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。
InputStreamReader
实现将字节的输入流按指定字符集转换为字符的输入流。 需要和InputStream“套接”。 构造器
public InputStreamReader(InputStream in) public InputSreamReader(InputStream in,String charsetName)
如:
Reader isr = new InputStreamReader(System.in,”gbk”);
OutputStreamWriter
实现将字符的输出流按指定字符集转换为字节的输出流。 需要和OutputStream“套接”。 构造器 public OutputStreamWriter(OutputStream out) public OutputSreamWriter(OutputStream out,String charsetName)
@Test public void TestOne(){ InputStreamReader isr = null; try { FileInputStream fs = new FileInputStream("fileTestOne"); isr = new InputStreamReader(fs,"UTF-8"); char [] chars = new char[20]; int len; while ((len = isr.read(chars)) != -1){ String s = new String(chars,0,len); System.out.println(s); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(isr != null){ isr.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void TestTwo(){ InputStreamReader isr = null; OutputStreamWriter osw = null; try { FileInputStream fs = new FileInputStream("fileTestOne"); FileOutputStream fw = new FileOutputStream("fileTestFour"); isr = new InputStreamReader(fs); osw = new OutputStreamWriter(fw); int len; char [] chars = new char[20]; while ((len = isr.read(chars)) != -1){ String s = new String(chars,0,len); // 转化为字符串 osw.write(s); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(isr != null){ isr.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(osw != null){ osw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
综合使用InputStreamReader和OutputStreamWriter
@Test public void test2() throws Exception { //1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.读写过程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.关闭资源 isr.close(); osw.close(); }
Practices:
1. 说明流的三种分类方式
流向:输入流、输出流
数据单位:字节流、字符流
流的角色:节点流、处理流
2. 写出4个IO流中的抽象基类,4个文件流,4个缓冲流
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BudderedReader
Writer FileWriter BufferedWriter
Io父类 | 基础处理流 | 缓冲流 | 转换流 | ||
InputStream | FileInputStream | BufferedInputStream | 操作字符 | InputStreamReader | 将字节转化为字符 char----> String |
OutputStream | FileOutputStream | BufferedOutputStream | 操作字符 | OutputStreamWriter | 将字符串转化为字符写入程序中 String ---> char |
Reader | FileReader | BufferedReader | 操作字符串(文本文件) | ||
Writer | FileWriter | BufferedWriter | 操作字符串(文本文件) |
3. 字节流与字符流的区别与使用情境
字节流:read(byte[] buffer) / read() 非文本文件 字符流:read(char[] cbuf) / read() 文本文件
4. 使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
@Test public void TestOne(){ BufferedInputStream bs = null; BufferedOutputStream bp = null; try { FileInputStream fs = new FileInputStream("a.jpg"); // 字节流操作文件 FileOutputStream fo = new FileOutputStream("b.jpg"); bs = new BufferedInputStream(fs); // 缓冲流加速 bp = new BufferedOutputStream(fo); int len; byte [] bytes = new byte[1024]; while ((len = bs.read(bytes)) != -1){ bp.write(bytes,0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if(bs != null){ bs.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(bp != null){ bp.close(); } } catch (IOException e) { e.printStackTrace(); } } }
5: 转换流是哪两个类,分别的作用是什么?请分别创建两个类的对象。
InputStreamReader 字节流--->字符流 将输入的字节流转换为输入的字符流。 解码 outputStreamWriter 字符流---->字节流 将输出的字符流转换为输出的字节流。编码 InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”utf-8”); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“b.txt”),”gbk”);
.
------------恢复内容结束------------