IO流
IDEA中:
说明:如果大家使用JUnit中的单元测试方法测试,相对路径即为当前Module下。
如果大家使用main()测试,响度路径即为当前的Project下。
Eclipse中:
不管使用单元测试方法还是使用main()测试,相对路径都是当前的Project下。
File类的使用:路径分隔符
常用方法:
IO流原理及流的分类
字节流:byte 适用于图片,视频
字符流:char 适用于文本
字节流:就是两个节点中的流
处理流:就是在流上边再加上一个流(作用加快流的速度)
流的分类图示:
IO流体系:
读取硬盘中文文件,并打印在控制台:
package com.atguigu.java;
import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderWriterTest {
/*public static void main(String[] args) {
File file = new File("hello.txt");//相较于当前路径
System.out.println(file.getAbsoluteFile());//D:\Program Files\project\JavaSenior\hello.txt
File file1 = new File("day01\\hello.txt");
System.out.println(file.getAbsoluteFile());//D:\Program Files\project\JavaSenior\day09\hello.txt
}
*/
@Test
public void testFileReader() throws IOException {
//1.实例化File类的对象,指明要操作的文件
File file = new File("hello.txt"); //相较于当前module
//2.提供数据的流
FileReader fr = new FileReader(file);
//3.数据的读入
//read();返回读入的一个字符。如果达到文件末尾,返回-1
/* int data = fr.read();//char类型就是其实也是一个 int a=97
while (data!=-1){
System.out.print((char) data);
data = fr.read();
}*/
//方式二
//3.数据的读入
int data;
while ((data= fr.read())!=-1){
System.out.println((char) data);
}
//流的关闭
fr.close();
}
}
说明点:
1.异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理。
2.read();返回读入的一个字符。如果达到文件末尾,返回-1。
3.读入的文件一定能够要存在,否则就会报FileNotFoundException。
关于异常的捕获这里,如果在外面没有实例化就报错了(FileReader fr = null; File file = new File("hello.txt"); //相较于当前module //2.提供数据的流 fr = new FileReader(file);
)。读入的文件如果没有,那就直接出来了,所以我们这里最后要加一个判断。
模板:
后续主要是2,3不同;流的使用,输入和输出。
上图结果,为下面的输出;
正确演示:
方法一(循环的方法):
方法二(String字符串的方法):
@Test
public void testFileRead01() throws IOException {
//File类的实例化
File file = new File("hello.txt");
//流的实例化
FileReader fr = new FileReader(file);
//读入操作
//read(char[]cBuffer):返回读入cBuffer数组中的字符的个数。如果达到文件末尾,返回-1
char[] cBuffer = new char[5];
int len;
while ((len=fr.read(cBuffer))!=-1){
//方式一
//读进了几个就输出几个
/* for (int i = 0; i < len; i++) {
System.out.print(cBuffer[i]);
}
*/
//方式二
String str = new String(cBuffer,0,len);
System.out.print(str);
}
//资源的关闭
fr.close();
}
字符流写出的操作:
@Test
public void testFileWriter() throws IOException {
//1.提供File类的对象,指明要写出到的文件文件名
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
FileWriter fw = new FileWriter(file);
//3.写出的操作
fw.write("i have a dream\n");
fw.write("you need have a dream too");
//4.流资源的关闭
fw.close();
}
说明:输出操作,对应的File可以存在也可以不存在。
如果不存在,在输出的过程中,会自动创建文件。
如果存在,且//2.提供FileWriter的对象,用于数据的写出 FileWriter fw = new FileWriter(file,false);
则覆盖该文件;
如果存在,且//2.提供FileWriter的对象,用于数据的写出 FileWriter fw = new FileWriter(file,true);
则在该文件后面继续添加。
读入和写出文件代码(文件的复制):
@Test
public void fileWriterFileReader(){
FileReader fir = null;
FileWriter fow = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File fi = new File("hello.txt");
File fo = new File("helle2.txt");
//2.创建输入流和输出流的对象
fir = new FileReader(fi);
fow = new FileWriter(fo);
//3.数据的读入和写出操作
char[] cBuffer = new char[5];
int len;
while ((len=fir.read(cBuffer))!=-1){
fow.write(cBuffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4关闭流资源
try {
if (fow!=null)
fow.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fir!=null)
fir.close();
} catch (IOException e) {
e.printStackTrace();
}
}
这里是两种关闭流第二种写法,第二个流关闭有没有在finally里面try、catch一样。
对于文本文件的英语是可以用字节流去读的(但不建议),但是对于中文就不可以了有可能会出现乱码。是因为一个中文是用多个字节组成的。
结论:对于文本文件(.txt,.java,.c,.cpp),使用字符流处理;
对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt…),使用字节流处理。
图片文件的复制代码:
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
public class FileInputOutputStreamTest {
@Test
public void testFileInputOutputStream(){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File fi = new File("QQ图片20211018170403.png");
File fo = new File("QQ图片202110181704031.png");
fis = new FileInputStream(fi);
fos = new FileOutputStream(fo);
byte[] buffer = new byte[5];
int len;
while((len=fis.read(buffer))!=-1){
fos.write(buffer,0,len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
视频的复制,这里我们把复制当做一个方法封装起来。就是把上一个代码里的实参,改为形参。代码如下:
@Test
public void testCopyFile(){
String srcPath = "C:\\Users\\47641\\Desktop\\011.mkv";
String destPath ="C:\\Users\\47641\\Desktop\\012.mkv";
long start = System.currentTimeMillis();
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间"+(end-start));//复制操作花费的时间11419
}
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File fi = new File(srcPath);
File fo = new File(destPath);
fis = new FileInputStream(fi);
fos = new FileOutputStream(fo);
byte[] buffer = new byte[1024];//通常这里是写1024,这个地方写多少运行时间是有区别的。
int len;
while((len=fis.read(buffer))!=-1){
fos.write(buffer,0,len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文本文件只要不在内存中读(就是不要去操作),可以用字节流,就是直接复制。就是相当于搬运工,原原本本的样子,还是0和1.然后再拿出去
但是非文本文件就不可以用字符流复制。
缓冲流(处理流的一种)
1.缓冲流:
BufferedInputStream
BufferedOuputStream
BufferedReader
BufferedWriter
2.作用:提高流的读取、写入的速度 ;
3.处理流就是“套接”在已有的流的基础上;
4.关闭资源时,同一级别的顺序无所谓。不同级别的后开先关。(先关外层流,后关内层流)说明:关闭外层流的同时,内层流会自动关闭。关于内层流的关闭,我们可以省略。
缓冲流复制代码:
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
public class BufferedTest {
@Test
public void bufferStream(){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File("QQ图片202110181704031.png");
File destFile = new File("QQ图片202110181704034.png");
//2.造流
//2.1造节点流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//2.2造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节,读取,写入
byte[] buffer = new byte[10];//就是把buffer看做是一个小车,拿它来给两点之间运东西
int len;
while((len= bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally { //资源关闭
try {if (bos!=null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (bis!=null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流复制视频代码(与上一个的时间对比):
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
public class BufferedTest {
@Test
public void bufferStream(){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File("QQ图片202110181704031.png");
File destFile = new File("QQ图片202110181704034.png");
//2.造流
//2.1造节点流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//2.2造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节,读取,写入
byte[] buffer = new byte[10];//就是把buffer看做是一个小车,拿它来给两点之间运东西
int len;
while((len= bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally { //资源关闭
try {if (bos!=null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (bis!=null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//实现文件复制;用缓冲流
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.造流
//2.1造节点流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//2.2造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节,读取,写入
byte[] buffer = new byte[1024];//就是把buffer看做是一个小车,拿它来给两点之间运东西
int len;
while((len= bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
System.out.println("文件复制完毕");
} catch (IOException e) {
e.printStackTrace();
} finally { //资源关闭
try {if (bos!=null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (bis!=null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyFile(){
String srcPath = "C:\\Users\\47641\\Desktop\\011.mkv";
String destPath = "C:\\Users\\47641\\Desktop\\013.mkv";
long start = System.currentTimeMillis();
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间"+(end-start));//复制操作花费的时间3978
}
}
bos.flush()//这个是BufferedOutputStream()里面的刷新缓冲区的方法;有的缓冲流是没有自动刷新的。
字符流复制:
/*
使用BufferedReader和BufferedWriter实现文本文件的复制
*/
@Test
public void testBufferedReaderBufferedWriter() throws IOException {
BufferedReader br = new BufferedReader(new FileReader(new File("dbcp.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("dbcp2.txt")));
//读写操作 方法一使用char[]数组
/*char[] chars = new char[1024];
int len;
while((len= br.read(chars))!=-1){
bw.write(chars,0,len);
}*/
//方法二使用String
String data;
while((data = br.readLine())!=null){
bw.write(data);//data中不包含换行符,如果要换行就是自己加bw.write(data+“\n”);
//还有一个方法就是利用方法换行 bw.newLine();
bw.newLine();}
bw.close();
br.close();
}
}
缓冲流习题1:
图片的加密解密(就是对字节进行变换在还原):
package com.atguigu.exer;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class PicTest {
@Test
//图片加密
public void testSecurity() throws IOException {
FileInputStream fis = new FileInputStream("QQ图片20211018170403.png");
FileOutputStream fos = new FileOutputStream("QQ图片202110181704038secret.png");
byte[] buffer = new byte[1024];
int len;
while((len= fis.read(buffer))!=-1){
//加密操作(这里是用亦或)
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^5);
}
fos.write(buffer,0,len);
}
fos.close();
fis.close();
}
@Test
//图片解密
public void testSolve() throws IOException {
FileInputStream fis = new FileInputStream("QQ图片202110181704038secret.png");
FileOutputStream fos = new FileOutputStream("QQ图片202110181704038solve.png");
byte[] buffer = new byte[1024];
int len;
while((len= fis.read(buffer))!=-1){
//解密操作(这里是用亦或)亦或的亦或就是原来的
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^5);//m^n^n=m;
}
fos.write(buffer,0,len);
}
fos.close();
fis.close();
}
}
获取文本上每个字符出现的次数(遍历文本的每一个字符:字符及出现的次数保存在Map中;将Map中数据写入文件):
package com.atguigu.exer;
import org.junit.Test;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class WordTest {
@Test
public void testCountWord() throws IOException {
//1.创建一个Map集合
HashMap<Character, Integer> map = new HashMap<>();
//2.遍历每一个字符,每一个字符出现的次数放到map中
FileReader 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
BufferedWriter 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':
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();
}
fr.close();
bw.close();
}
}
备注一下集合知识:
对于Map集合类,map是用于存储元素对,即通过“键”和“值”来存储元素,每个键对应着一个值,对使用 containsKey() 和 containsValue() 遍历 HashMap 中所有元素所需时间的测试表明,containsValue() 所需的时间要长很多。 因此,如果 containsValue() 是应用程序中的性能问题,它将很快显现出来,并可以通过监测您的应用程序轻松地将其识别。 这种情况下,我相信您能够想出一个有效的替换方法来实现 containsValue() 提供的等效功能, 但如果想不出办法,则一个可行的解决方案是再创建一个 Map,并将第一个 Map 的所有值作为键。 这样,第一个 Map 上的 containsValue() 将成为第二个 Map 上更有效的 containsKey()。
然后通过将Map转成set就可以迭代。 map有一个方法叫做entrySet,这方法可以将Map的键值对的映射关系作为set集合的元素存储到Set集合当中,而这种映射关系的类型就是Entry的类型。因此可以通过使用Getkey()和getvalue()两个方法得到Set中存储的键和值。