Hi,大家好,我是抢老婆酸奶的小肥仔。
我们在操作文件时,需要将其转化成各种流来进行操作,然而很多时候我们并不知道各种流的作用,也不清楚流与流之间的相互转换。下面来具体说说各种流及流之间的相互转换。
希望对大家有所帮助。
1、I/O流
I/O流:即InputStream/OutputStream的缩写,即计算机调度数据写入写出的过程。
1.1 分类
I/O可以根据不同维度进行分类,主要根据:1、流向 2、操作颗粒度 3、角色来进行划分。
1.1.1 按照流向分类
输出流
:只能向其写入数据,主要使用:OutputStream和Writer为基类。输入流
:只能从中读取数据,主要使用:InputStream和Reader为基类。
1.1.2 按照操作颗粒度分类
字节流
:以字节为单元,即byte[],8位字节,主要使用:InputStream、OutputStream为基类。字符流
:以字符为单元,即char[],16位字符,主要使用:Writer、Reader为基类。
1.1.3 按照角色分类
节点流
:即对端点进行读写数据,例如:磁盘,网络等处理流
:即对已存在的流的连接和封装,通过封装流的功能来调用实现数据读写。例如:缓存流。
1.2 原理
java提供了一个记录指针来记录流的读取情况。
当创建一个流对象时,记录指针就会标记流的开始位置,每当程序从InputStream或Reader里面取出一个或多个字节或字符时,记录指针也会自动向后移动。我们在写入文件时就会使用,如下代码。
int temp;
byte[] bt = new byte[1024*10];
while((temp = bais.read(bt))!= -1) {
fos.write(bt,0,temp);
}
2、常用流及使用
java为我们提供了很多流的子类,我们常用的也就那么几个,例如FileInputStream等,下面我们来汇总看下各流及其使用。
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
基类 | InputStream | OutputStream | Reader | Writer |
文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
字符串 | StringReader | StringWriter |
2.1 文件流操作
从上面表格中可以看到,文件操作输入流使用:FileInputStream/FileReader
,文件输出流使用:FileOutputStream/FileWriter
。
我们看看实现代码:
创建一个test.txt文件,并随意写入内容。例如:在D:\file下创建test.txt,输入:今晚打老虎。
2.1.1 FileInputStream读取文件内容
public static void main(String[] args) {
String filePath = "D:\file\test.txt";
FileInputStream fis = null;
try {
//使用FileInputStream读取文件信息
fis = new FileInputStream(filePath);
//一次性将文件内容读出
byte[] bytes = new byte[fis.available()];
int item = 0;
while ((item = fis.read(bytes)) != -1){
String str = new String(bytes, 0, item);
System.out.println("读取文件内容:"+str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关流
try {
if (Objects.nonNull(fis)) {
fis.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
注 :在创建byte数组时,如果文件保存的是中文,则需要指定available(),即返回此输入流在不受阻塞情况下能读取的字节数(该方法在本地时不会遇到问题,如果用于网络操作,则会出现问题,这是因为网络存在间断性,文件内容分几次传输,从而导致 available()数值不固定), 否则读出的文件内容有问题。例如:将byte大小指定5,则一次读取5个字节,则结果如图:
方法:
方法 | 描述 |
---|---|
read() | 从文件中读取一个字节 |
read(byte[] array) | 从文件中读取字节并存储到array中 |
read(byte[] array,int start,int length) | 从文件中读取从start开始,长度为length的字节保存到array中 |
available() | 一次性读取文件可用字节数 |
skip(long n) | 跳过指定的字节数 |
close() | 关闭文件输入流,如果不关闭流,这个流就会一直占用,会导致文件句柄被占用,可能导致资源泄露,导致CPU和RAM占用居高,甚至打不开新的文件进行读写。 |
finalize() | 确保close()方法被调用 |
2.1.2 FileReader读取文件内容
public static void main(String[] args) {
String filePath = "D:\file\test.txt";
FileReader fr = null;
try {
//使用FileReader读取文件数据
File file = new File(filePath);
fr = new FileReader(file);
char[] chars = new char[1024];
int frLen = 0;
while ((frLen = fr.read(chars)) != -1){
String str = new String(chars,0,frLen);
System.out.println("FileReader读取文件内容:"+str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(fr)) {
fr.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果
方法:
方法 | 描述 |
---|---|
read() | 从文件中读取一个字符 |
read(char[] array) | 从文件中读取字符并存储到array中 |
read(char[] array,int start,int length) | 从文件中读取从start开始,长度为length的字符保存到array中 |
mark() | 标记流中已读数据的位置,即记录指针的位置 |
skip(long n) | 跳过指定的字节数 |
close() | 关闭字符流 |
2.1.3 FileOutputStream写入内容
public static void main(String[] args) {
String filePath = "D:\file\test.txt";
FileOutputStream fout = null;
try {
//使用FileOutputStream输入内容到文件
String content = "只是输入的内容";
fout = new FileOutputStream(filePath,Boolean.FALSE);
fout.write(content.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(fout)) {
fout.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
打开文件,可以看到内容被修改成:只是输入的内容。
在创建FileOutputStream流时,我们可以通过设置该构造方法中的append参数值来指定是追加文件内容还是重写文件。true:表示追加文件内容,false:表示重写文件内容。
2.1.4 FileWriter写入内容
public static void main(String[] args) {
String filePath = "D:\file\test.txt";
FileWriter fw = null;
try {
//使用FileWriter写入文件内容
String appendContent = "FileWriter写入的数据";
fw = new FileWriter(filePath,Boolean.FALSE);
fw.append(appendContent);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(fw)) {
fw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
文件的内容被覆盖,在创建FileWriter时跟FileOutputStream一样,可以指定对文件内容的写入方式。
2.2 数组流操作
数组流是字节数组流,主要是解决频繁读写文件,效率较低的问题,它会将内容以字节的形式保存到内存中。它将所有数据全部缓存到自身,然后一次性将数据输出。
2.2.1 ByteArrayInputStream读取文件内容
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
ByteArrayInputStream bais = null;
try {
//使用FileInputStream读取文件信息
//一次性将文件内容读出
byte[] bytes = new byte[1024*10];
int item = 0;
bais = new ByteArrayInputStream(FileUtils.readFileToByteArray(new File(filePath)));
while ((item = bais.read(bytes)) != -1){
String str = new String(bytes, 0, item);
System.out.println("读取文件内容:"+str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(bais)) {
bais.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
ByteArrayInputStream构建的时候提供了两种构造方法:ByteArrayInputStream(byte buf[])
或ByteArrayInputStream(byte buf[], int offset, int length)
。都是将byte转换成ByteArrayInputStream。
ByteArrayInputStream提供的方法参见2.1.1。
2.2.1 CharArrayReader读取文件内容
在创建CharArrayReader时,是将char[]进行转化的,因此我们在读取文件内容时,则需要将文件内容转化成char[]。方法如下:
private static char[] fileToChar(String filePath){
FileReader fr = null;
try {
fr = new FileReader(filePath);
char[] chars = new char[1024];
int frLen = 0;
StringBuilder sb = new StringBuilder();
while ((frLen = fr.read(chars)) != -1){
sb.append(new String(chars,0,frLen));
}
return sb.toString().toCharArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(fr)) {
fr.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
CharArrayReader读取文件内容,代码:
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
CharArrayReader car = null;
try {
//CharArrayReader读取文件内容
char[] chars = fileToChar(filePath);
assert chars != null;
car = new CharArrayReader(chars);
int charNum = 0;
while ((charNum = car.read(chars)) != -1){
String str = new String(chars, 0, charNum);
System.out.println("CharArrayReader:读取文件内容:"+str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(bais)) {
bais.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
2.2.3 ByteArrayOutputStream写入文件内容
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
ByteArrayOutputStream baos = null;
FileOutputStream fos = null;
try {
//ByteArrayOutputStream写入文件内容
baos = new ByteArrayOutputStream();
baos.write("ByteArrayOutputStream写入的文件内容".getBytes(StandardCharsets.UTF_8));
fos = new FileOutputStream(filePath);
baos.writeTo(fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(baos)) {
baos.close();
}
if (Objects.nonNull(fos)) {
fos.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
从上面代码中,首先将内容转存到ByteArrayOutputStream的缓冲区,然后写入FileOutputStream实现写入文件内容。
writeTo(OutputStream outSt)
:将字节数组流的全部内容写入指定的输出流中。
2.2.3 CharArrayWriter写入文件内容
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
CharArrayWriter caw = null;
FileWriter fw = null;
try {
//CharArrayWriter写入文件内容
caw = new CharArrayWriter();
caw.write("这是测试CharArrayWriter");
fw = new FileWriter(filePath);
caw.writeTo(fw);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (Objects.nonNull(caw)) {
caw.close();
}if (Objects.nonNull(fw)) {
fw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
2.3 缓冲流操作
缓冲流它的作用与数组流设计初衷一样,也是为了提高数据输出效率而设计的。它是缓存一部分数据后,一次一次的输出。
2.3.1 BufferedInputStream读取文件内容
BufferedInputStream是继承了FileInputStream,也提供了两个构造方法创建BufferedInputStream,BufferedInputStream(InputStream in)\ BufferedInputStream(InputStream in, int size)
。
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
BufferedInputStream bis = null;
try{
bis = new BufferedInputStream(new FileInputStream(filePath));
int itm = 0;
byte[] bytes = new byte[bis.available()];
while ((itm = bis.read(bytes)) != -1){
System.out.println("读取文件内容:" + new String(bytes,0,itm));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (Objects.nonNull(bis)) {
bis.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
我们将文件转换成FileInputStream,然后在转换成BufferedInputStream实现文件内容读取。
2.3.2 BufferedReader读取文件内容
BufferedReader读取文件内容与FileReader,只不过需要将Reader转换成BufferedReader,然后遍历读取内容。
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
BufferedReader br = null;
try{
br = new BufferedReader(new FileReader(filePath));
int itm2 = 0;
char[] chars = new char[1024];
while ((itm2 = br.read(chars)) != -1){
System.out.println("BufferedReader读取文件内容:" + new String(chars,0,itm2));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (Objects.nonNull(br)) {
br.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
2.3.3 BufferedOutputStream写入文件内容
BufferedOutputStream集成了FilterOutputStream,也提供了两个构造方法创建BufferedInputStream,BufferedOutputStream(OutputStream out)\ BufferedOutputStream(OutputStream out, int size)
。
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
BufferedOutputStream bdos = null;
try{
//BufferedOutputStream写入文件内容
bdos = new BufferedOutputStream(new FileOutputStream(filePath));
bdos.write("BufferedOutputStream写入文件内容".getBytes(StandardCharsets.UTF_8));
bdos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (Objects.nonNull(bdos)) {
bdos.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果:
2.3.3 BufferedWriter写入文件内容
public static void main(String[] args) {
String filePath ="D:\file\test.txt";
BufferedWriter bw = null;
try{
//BufferedWriter写入文件内容
bw = new BufferedWriter(new FileWriter(filePath));
bw.write("BufferedWriter写入文件内容".toCharArray());
bw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (Objects.nonNull(bw)) {
bw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
上面就是常用到的流对文件进行的操作,其实各流对文件的操作方法基本一致,只是效率不同而已,只要知道一种,其他依葫芦画瓢就好。
3、IO转换
我们在日常应用中,常常会使用到各种流的转换,例如:File转InputStream等等,下面我们来看看各种流之间的转换。
3.1 File转InputStream/OutputStream
//file转InputStream
InputStream is = new FileInputStream(file);
//file转OutputStream
OutputStream os = new FileOutputStream(file);
在appache提供了一个FileUtils工具类,也可以对文件进行转换。
//file转InputStream
FileInputStream fis = FileUtils.openInputStream(file);
//file转OutputStream
FileOutputStream fos = FileUtils.openOutputStream(file);
3.2 InputStream/OutputStream转byte[]
//InputStream转byte[]
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[is.available()];
is.read(bytes);
is.close();
//OutputStream转byte[]
OutputStream os = new FileOutputStream(file);
byte[] bytes = new byte[1024 * 10];
os.write(bytes);
os.close();
在appache提供了一个IOUtils工具类,也可以将InputStream转换成byte[]。
byte[] bytes = IOUtils.toByteArray(is);
3.3 String转byte[]/char[]
String content = "231212121212";
byte[] bytes1 = content.getBytes(StandardCharsets.UTF_8);
char[] chars1 = content.toCharArray();
3.4 File转char[]
FileReader reader = new FileReader(file);
char[] chars = new char[1024];
reader.read(chars);
3.5 Outputstream 转 Inputstream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray());
3.6 byte[]转InputStream/OutputStream
String content = "测试一波";
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
//byte[]转InputStream
InputStream input = new ByteArrayInputStream(bytes);
//byte[]转OutputStream
OutputStream os = new ByteArrayOutputStream();
os.write(bytes);
os.flush();
上述就是我对IO的总结,可能不是特别深入,希望对大家有帮助,在开发过程中能提高效率。谢谢!也期待大家在评论区发表自己的理解,相互学习。