Java IO原理
流的分类
一、流的分类:
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
二、流的体系结构:
InputStream & Reader
InputStream
Reader
OutputStream & Writer
OutputStream
Writer
三、输入、输出的标准化过程
输入过程:
① 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
② 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
③ 具体的读入过程:
创建相应的byte[ ] 或 char[ ]
④关闭流资源
说明:程序中出现的异常需要使用 try-catch-finally处理。
输出过程:
① 创建File类的对象,指明写出的数据的来源。(不要求此文件一定要存在)
② 创建相应的输出流,将File类的对象作为参数,传入流的构造器中
③ 具体的读入过程:
write(char[ ]/byte[ ] buffer,0,len)
④关闭流资源
说明:程序中出现的异常需要使用 try-catch-finally处理。
节点流(或文件流)
读数据的操作:
/**
* @author acoffee
* @create 2020-10-18 18:19
*/
public class FileReaderWinterTest {
/*
* 将day26下的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");//相较于工程文件
//2.提供具体的流
fr = new FileReader(file);
int data;
while ((data = fr.read()) != -1) {
System.out.print((char) data);//helloworld!!
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//流的关闭
if (fr != null) {// 这里使用if语句去判断是因为如果fr = new FileReader(file);
// 这个语句出现了异常,程序就会直接出去,从而导致对象是不能实例化
// 所以就不存在fr对象了,然后去执行finally中的代码,
// 所以如果我们以上来就去fr.close()的话就会报异常所以
// 我们就应该先去判断对象是否实例化。 fr.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]);//helloworld!!rld
// }
//正确的写法
// for (int i = 0; i < len; i++) {
// System.out.print(cbuf[i]);//helloworld!!
// }
//方式二:
//错误的方法:同方式一的错误方法
// String str = new String(cbuf);
// System.out.print(cbuf);//helloworld!!rld
//正确的写法
String str = new String(cbuf, 0, len);
System.out.print(str);//helloworld!!
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写入数据的操作
/*
* 从内存中写出数据到硬盘的文件里
* 说明:
* 1.输出操作,对应的File可以不存在。
* File对应的硬盘的文件如果不存在:在输出的过程中,会自动创建文件
* File对应的硬盘的文件如果存在:
* 如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖
如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
* */
@Test
public void testFielWinter() {
FileWriter fileWriter = null;
try {
//1.提供File类的对象,指明写出到的文件
File file = new File("hello.txt");
//2.提供FileWinter的对象,用于数据的写出
//这里写true就不会原来文件的内容进行覆盖,会跟到后面写
//这里如果写false会对文件进行覆盖,跟不写的情况一样
fileWriter = new FileWriter(file,true);
//3.写出的操作
fileWriter.write("I have a dream\n");
fileWriter.write("you need to have a dream");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
try {
if (fileWriter != null){
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
读取和写出的操作(复制文本)
@Test
public void testFileReaderFileWinter(){
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
fileReader = new FileReader(srcFile);
fileWriter = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;
while ((len = fileReader.read(cbuf)) != -1){
//每次写出len个字符
fileWriter.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4.关闭流资源
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述的字符流是不能处理图片文件的
字节流处理图片的操作
@Test
public void testFileInputOutputStream() throws IOException {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
File file = new File("爱情与友情.jpg");
File file1 = new File("爱情与友情2.jpg");
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(file1);
byte[] bytes = new byte[5];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fileInputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
指定路径
//指定路径下的文件的复制
public void copyFile(String secFile,String DestFile){
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
File file = new File(secFile);
File file1 = new File(DestFile);
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(file1);
byte[] bytes = new byte[5];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fileInputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyFile(){
long start = System.currentTimeMillis();
String srcPath = "C:\\Users\\acoffee\\Desktop\\M35DIIQRON9BL91%SVLTVAE.png";
String destPath = "C:\\Users\\acoffee\\Desktop\\M35DIIQRON9BL91%SVLTVAE1.png";
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间为:" + (end - start));//160
}
缓冲流
缓冲流(提高文件的读写效率)
内部提供了缓冲区,存入缓冲区中达到了最大值,一次性的读出.所以能提高效率。flush( )方法刷新缓冲区。
/**
* 处理流之一缓冲流的使用
* <p>
* 1.缓冲流:
* BufferInputStream
* BufferOnputStream
* BufferReader
* BufferWinter
*
* 作用:
* 1.内部提供了缓冲区,存入缓冲区中达到了最大值,一次性的读出.
*
* @author acoffee
* @create 2020-10-18 21:34
*/
public class BufferdTest {
/*
* 实现非文本文件的复制
* */
@Test
public void BufferedStreamTest() {
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
//1.造文件
File srcFile = new File("爱情与友情.jpg");
File destFile = new File("爱情与友情3.jpg");
//2.造流
//2.1造节点流
FileInputStream fileInputStream = new FileInputStream(srcFile);
FileOutputStream fileOutputStream = new FileOutputStream(destFile);
//2.2造缓冲流
bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//3.复制的细节:读取、写入
byte[] bytes = new byte[5];
int len;
while ((len = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,在关闭内层的流(像脱衣服一样)
try {
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//说明:关闭外层流的同时,内层也会自动的进行关闭。关于内层刘的关闭在这里我们可以省略。
// fileOutputStream.close();
// fileInputStream.close();
}
}
与字节流的效率对比(160 - 31)
//实现文件复制的方法
public void copyFileWithBuffered(String srcFile,String destFile){
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
//1.造文件
File srcFile1 = new File(srcFile);
File destFile1 = new File(destFile);
//2.造流
//2.1造节点流
FileInputStream fileInputStream = new FileInputStream(srcFile);
FileOutputStream fileOutputStream = new FileOutputStream(destFile);
//2.2造缓冲流
bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//3.复制的细节:读取、写入
byte[] bytes = new byte[5];
int len;
while ((len = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,在关闭内层的流(像脱衣服一样)
try {
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyFile(){
long start = System.currentTimeMillis();
String srcPath = "C:\\Users\\acoffee\\Desktop\\M35DIIQRON9BL91%SVLTVAE.png";
String destPath = "C:\\Users\\acoffee\\Desktop\\M35DIIQRON9BL91%SVLTVAE1.png";
copyFileWithBuffered(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间为:" + (end - start));//31
}
字符缓冲流
/*
* 使用BufferedReader和BufferedWriter实现文本文件的复制
*
* */
@Test
public void testBufferedReaderBufferedWriter() throws IOException {
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
bufferedReader = new BufferedReader(new FileReader(new File("dbcp.txt")));
bufferedWriter = new BufferedWriter(new FileWriter(new File("dcbp1.txt")));
//读写操作
// char[] chars = new char[1024];
// int len;
// while ((len = bufferedReader.read(chars)) != -1){
// bufferedWriter.write(chars,0,len);
// }
//方式二:使用String
String data;
while ((data = bufferedReader.readLine()) != null){
//bufferedWriter.write(data+"\n");//data中不包含换行符,需要手动换行
bufferedWriter.write(data);
bufferedWriter.newLine();//换行
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("爱情与友情.jpg");
fos = new FileOutputStream("爱情与友情secret.jpg");
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
//字节数组进行修改
//错误的
// for (byte b : buffer){
// b = (byte)(b ^ 5);
// }
//正确的
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^ 5);//加密异或5
// 解密在异或5就可以了
//异或的异或就是本身
}
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
练习(缓冲流):
1.第一题
//图片的加密
@Test
public void test() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("爱情与友情.jpg");
fos = new FileOutputStream("爱情与友情secret.jpg");
byte[] buffer = new byte[20];
int len;
while ((len = fis.read(buffer)) != -1) {
//字节数组进行修改
//错误的
// for (byte b : buffer){
// b = (byte)(b ^ 5);
// }
//正确的
for (int i = 0; i < len; i++) {
buffer[i] = (byte) (buffer[i] ^ 5);//加密异或5
// 解密在异或5就可以了
//异或的异或就是本身
}
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
第二题:获取文本上每个字符出现的次数
提示:遍历文本的每一个字符;字符及出现的次数保存在Map中;将Map中数据 写入文件
@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();
}
}
}
}
转换流
图解:
//关于上述图解的代码实现
//综合使用InputStreamReader和OutputStreamWriter
//需要使用try-catch-finally
@Test
public void test1() throws IOException {
//1.造文件,造流
File file = new File("dbcp_gbk.txt");
File file1 = new File("dbcp.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file);
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();
}
练习:
问题:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续 进行输入操作,直至当输入“e”或者“exit”时,退出程序。
package exer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author acoffee
* @create 2020-10-20 9:53
*/
public class OtherStreamTest {
//从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续
// 进行输入操作,直至当输入“e”或者“exit”时,退出程序。
//方式一:使用Scanner实现,调用next()返回一个字符串
//方式二:使用System.in实现。System.in → 转换流 → BufferedReader 的 readLine()
public static void main(String[] args) {
BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while (true) {
String data = br.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
System.out.println("程序结束");
break;
}
String upperCase = data.toUpperCase();
System.out.println(upperCase);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
问题:创建一个名为MyInput的程序。包含从键盘读取int, double, float, boolean, short, byte和字符串值的方法。(相当于创建一个Scanner方法)
package com.atguigu.java;
// MyInput.java: Contain the methods for reading int, double, float, boolean, short, byte and
// string values from the keyboard
import java.io.*;
public class MyInput {
// Read a string from the keyboard
public static String readString() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Declare and initialize the string
String string = "";
// Get the string from the keyboard
try {
string = br.readLine();
} catch (IOException ex) {
System.out.println(ex);
}
// Return the string obtained from the keyboard
return string;
}
// Read an int value from the keyboard
public static int readInt() {
return Integer.parseInt(readString());
}
// Read a double value from the keyboard
public static double readDouble() {
return Double.parseDouble(readString());
}
// Read a byte value from the keyboard
public static double readByte() {
return Byte.parseByte(readString());
}
// Read a short value from the keyboard
public static double readShort() {
return Short.parseShort(readString());
}
// Read a long value from the keyboard
public static double readLong() {
return Long.parseLong(readString());
}
// Read a float value from the keyboard
public static double readFloat() {
return Float.parseFloat(readString());
}
}
打印流
数据流
/*
3. 数据流
3.1 DataInputStream 和 DataOutputStream
3.2 作用:用于读取或写出基本数据类型的变量或字符串
练习:将内存中的字符串、基本数据类型的变量写出到文件中。
注意:处理异常的话,仍然应该使用try-catch-finally.
*/
@Test
public void test3() throws IOException {
//1.
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
//2.
dos.writeUTF("刘一");
dos.flush();//刷新操作,将内存中的数据写入文件
dos.writeInt(23);
dos.flush();
dos.writeBoolean(true);
dos.flush();
//3.
dos.close();
}
/*
将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。
注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!
*/
@Test
public void test4() throws IOException {
//1.
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
//2.
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("isMale = " + isMale);
//3.
dis.close();
}
每日一练
1. 说明流的三种分类方式
流向:输入流、输出流
数据单位:字节流、字符流
流的角色:节点流、处理流
2. 写出4个IO流中的抽象基类,4个文件流,4个缓冲流
InputStream FileXxx BufferedXxx
OutputStream
Reader
Writer
InputStreamReader:父类Reader
异常: XxxException XxxError
RandomAccessFile
3. 字节流与字符流的区别与使用情境
字节流:read(byte[] buffer) / read() 非文本文件
字符流:read(char[] cbuf) / read() 文本文件
4. 使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(“a.jpg”)));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(“b.jpg”)));
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer))!= -1){
bos.write(buffer,0,len);
}
bos.close();
bis.close();
//此时的异常应该使用try-catch-finally处理。
5.转换流是哪两个类,分别的作用是什么?请分别创建两个类的对象。
InputStreamReader:将输入的字节流转换为输入的字符流。 解码
OutputStreamWriter:将输出的字符流转换为输出的字节流。编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(“a.txt”),”utf-8”);
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“b.txt”),”gbk”);