目录
一、什么是数据流
对照我们的现实生活,可以想象成流水。
流的输入(读)和流的输出(写),针对于内存来说的,从设备包括硬盘,网卡的数据进入内存叫输入(Input),从内存把数据写到硬盘,网卡叫输出(Output) 。
Java中针对input和Output提供了两个抽象类
- InputStream输入流,从文件中把数据读取到内存。
- OutputStream输出流,把数据从内存输出到文件。
二、InputStream概述
2.1、方法
修饰符以及返回值类型 | 方法签名 | 说明 |
int | read() | 读取一个字节的数据,返回-1代表已经完全读完了. |
int | read(byte[] b) | 最多读取b.length自己的数据到b中,返回实际读到的数量;-1代表已经读完了. |
int | read(byte[] b,int off,int len) | 最多读取len-off字节的数据到b中,放在off开始,返回实际读到的数量;-1代表已经读完了. |
void | close() | 关闭字节流 |
2.2、说明
InputStream只是一个抽象类,要使用还是需要具体的实现类。关于InputStream的实现类有很多,基本可以认为不同的输入设备都可以对应一个InputStream类,本篇博客只讲从文件中读取,所以使用FileInputStream。
三、FileInputStream概述
3.1、构造方法
签名 | 说明 |
FileInputStream(File file) | 利用File对象构造文件输入流 |
FileInputStream(String name) | 利用文件路径构造文件输入流 |
示例1
import java.io.FileInputStream;
import java.io.IOException;
/**
* 创建一个输入流读取文件内容
*/
public class Exe_01 {
public static void main(String[] args) throws IOException {
FileInputStream inputStream=new FileInputStream("C:/Temp/Hello.txt");
//创建一个byte数组,来接收每次读取的内容,扩大缓冲区的容量
byte[] bytes=new byte[1024];
//循环读取
while(true){
//获取每次读取的长度
int len=inputStream.read(bytes);
//如果等于-1,就说明读完了
if(len==-1){
break;
}
//打印读取的内容
for (int i = 0; i < len; i++) {
System.out.print(bytes[i]+",");
}
}
//关闭数据流
inputStream.close();
}
}
3.2、利用Scanner进行字符串读取,简化操作
上述例子中,我们看到了对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是 Scanner 类 。
示例2
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
public class Exe_05 {
public static void main(String[] args) throws IOException {
//创建一个流
FileInputStream inputStream=new FileInputStream("C:/Temp/基尼太美.txt");
//使用Scanner简化操作
Scanner sc=new Scanner(inputStream,"UTF-8");
//循环读取
while(true){
//判断
if(!sc.hasNextLine()){
break;
}
String str=sc.nextLine();
System.out.println(str);
}
sc.close();
inputStream.close();
}
}
四、OutputStream概述
4.1、方法
修饰符及返回值类型 | 方法签名 | 说明 |
void | write(int b) | 写入要给字节的数据 |
void | write(byte[] b) | 将b这个字符数组中的数据全部写道os中 |
int | write(byte[] b,int off,int len) | 将b这个字符数组从off开始的数据写到os中,一共写len个 |
void | close() | 关闭字节流 |
void | flush() | 重要:I/O的速度是很慢的,所以,大多的OutputStream为了减少设备操作的次数,在写数据的时候都会将数据先暂时的写入到内存的一个指定区域里面,知道该区域满了或者其它指定条件时才真正的将数据写入到设备中,这个区域就叫缓冲区。但这样会造成一个结果,就是我们写的数据很有可能一部分留在缓冲区中。所以需要在最后或者合适的位置,调用flush(刷新)操作,将数据刷到设备中。 |
说明
OutputStream同样只是一个抽象类,要使用还需要具体的实现类。跟上述一样本篇博客只讲从文件中读取,所以使用FileOutputStream。
示例3
import java.io.FileOutputStream;
import java.io.IOException;
public class Exe_02 {
public static void main(String[] args) throws IOException {
//创建一个输出流
FileOutputStream outputStream=new FileOutputStream("C:/Temp/基尼太美.txt");
//在文件中写入一些内容
outputStream.write(189);
outputStream.write(188);
outputStream.write(187);
outputStream.write(186);
//刷新一下缓冲区
outputStream.flush();
//关闭流
outputStream.close();
}
}
运行结果
import java.io.FileInputStream;
import java.io.IOException;
public class Exe_03 {
public static void main(String[] args) throws IOException {
FileInputStream inputStream=new FileInputStream("C:/Temp/基尼太美.txt");
while(true){
int data=inputStream.read();
if(data==-1){
//返回-1,读取结束
break;
}
System.out.println(data);
}
//关闭流
inputStream.close();
}
}
示例4
import java.io.FileWriter;
import java.io.IOException;
/**
* 基于字符的输出流
*/
public class Exe_04 {
public static void main(String[] args) throws IOException {
FileWriter fileWriter=new FileWriter("C:/Temp/基尼太美.txt");
fileWriter.write("Cause I got a crush on you who you");
//换行符需要手动添加
fileWriter.write("\n");
fileWriter.write("你是我的我是你的谁");
fileWriter.write("\n");
fileWriter.write("再多一眼看一眼就会爆炸");
fileWriter.write("\n");
fileWriter.write("再近一点靠近点快被融化");
//清空缓冲区
fileWriter.flush();
//关闭流
fileWriter.close();
}
}
4.2、PrinterWriter简化写操作
示例5
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
*PrinterWriter简化写操作
*/
public class Exe_06 {
public static void main(String[] args) throws IOException {
//创建一个输出流
FileOutputStream outputStream=new FileOutputStream("C:/Temp/基尼太美.txt");
//根据输出流,创建一个PrinterWriter
PrintWriter printWriter=new PrintWriter(outputStream);
//写数据,自带换行符
printWriter.println("Cause I got a crush on you who you");
printWriter.println("再近一点靠近点快被融化");
printWriter.println("再多一眼看一眼就会爆炸");
printWriter.println("你是我的我是你的谁");
//关闭流
printWriter.close();
outputStream.close();
}
}
五、小程序练习
示例1
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件 。
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Exe_07 {
public static void main(String[] args) throws IOException {
//让用户输入一个路径
System.out.println("请输入一个文件路径");
Scanner sc=new Scanner(System.in);
//获取路径字符串
String rootPath=sc.nextLine();
//根据字符串创建一个File对象
File rootFile=new File(rootPath);
//判断路径是否有效
if(!rootFile.exists()){
System.out.println("对不起,您输入的路径是无效路径!!!");
return;
}
//判断是不是一个有效目录
if(!rootFile.isDirectory()){
System.out.println("对不起,你输入的路径不是一个有效目录");
return;
}
//让用户输入一个要找的关键字
System.out.println("请输入你要删除的关键字");
String keyWords=sc.nextLine();
//判断输入是否为空
if(keyWords==null){
System.out.println("关键字不能为空");
return;
}
//扫描文件
scan(rootFile,keyWords);
}
private static void scan(File rootFile, String keyWords) throws IOException {
//先获取root目录下的所有子文件以及目录
File[] files=rootFile.listFiles();
//判断数据是否为空
if(files==null||files.length==0){
//不做任何操作
return;
}
//遍历数组检查操作
for (File item:files) {
//检查file对象是否是文件还是目录
if(item.isFile()){
//检查文件名是否包含关键字
String fileName=item.getName();
if(fileName.contains(keyWords)){
//找到了,询问用户是否需要删除文件
System.out.println("找到了"+item.getCanonicalFile()+",包含关键字"+keyWords+",是否删除文件(Y/N)?");
//接收用户请求
Scanner scanner=new Scanner(System.in);
String result=scanner.nextLine();
//处理操作
if(result.toLowerCase().equals("y")){
//删除文件
item.delete();
System.out.println("删除文件"+item.getCanonicalFile()+"成功!!!");
}else{
return;
}
}
}else{
//如果目标文件是一个目录,递归区扫描目录文件
scan(rootFile,keyWords);
}
}
}
}
示例2
普通文件的复制
import java.io.*;
import java.util.Scanner;
public class Exe_08 {
public static void main(String[] args) {
//让用户输入一个路径
System.out.println("请输入源文件路径");
Scanner sc=new Scanner(System.in);
//获取路径字符串
String sourcePath=sc.nextLine();
//根据字符串创建一个File对象
File sourceFile=new File(sourcePath);
//判断文件是否有效
if(!sourceFile.exists()){
System.out.println("源文件无效,请重新输入!!");
return;
}
//获取目标输入的目标文件路径
System.out.println("请输入要复制到的目标文件路径");
String targetPath=sc.nextLine();
// 根据目标路径创建File对象
File targetFile=new File(targetPath);
// 判断目标路径是否有效
if (targetFile.exists()) {
System.out.println("目标文件已存在,请重新输入.");
return;
}
// 判断目标文件的父目录必须存在
if(!targetFile.getParentFile().exists()){
System.out.println("目标文件的目录不正确");
return;
}
//创建输入输出流
try {
InputStream inputStream=new FileInputStream(sourceFile);
OutputStream outputStream=new FileOutputStream(targetFile);
//读取源文件内容
byte[] bytes=new byte[1024];
while(true){
//获取读取的长度和内容
int len = inputStream.read(bytes);
if(len==-1){
break;
}
//把读取的文件写入目标文件中,从0~len
outputStream.write(bytes, 0, len);
}
//刷新缓冲区
outputStream.flush();
//打印日志
System.out.println("复制成功"+sourceFile.getCanonicalFile()+"--->"+targetFile.getCanonicalFile());
//关闭流
inputStream.close();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}