位置:首页 > 后端 > java

Java文件IO

dearweb 发布:2021-08-27 11:21:57阅读:

所谓的 IO 即 Input(输入)/Output(输出) ,当软件与外部资源(例如:网络,数据库,磁盘文件)交互的时候,就会用到 IO 操作。而在IO操作中,最常用的一种方式就是流,也被称为IO流。

File类

File是Java在整个文件IO体系中比较基础的类,它可以实现对文件,文件夹以及路径的操作,譬如:创建文件或文件夹,获取绝对路径,判断是否存在,重命名,删除,获取当前目录下的文件或文件夹等操作。

File file = new File("example"); //相对路径
System.out.println(file.getAbsolutePath()); //获取绝对路径
System.out.println(file.getName()); //获取名称
System.out.println(file.exists()); //判断文件或文件夹是否存在
boolean result = file.mkdirs();// 把 example 当成文件夹来创建,mkdirs()为级联创建
System.out.println(result);
result = file.createNewFile();// 把 example 当成文件夹来创建
System.out.println(result);

字节,字符和编码格式

单从表现形式上对于它们可以大致这样理解:字节和字符对于系统数据而言表现形式是不同的,可以通过打开一些文件来观察,如果打开的是图片或者是可执行程序文件,那么就会看到一些类似于乱码的东西;而如果是文本文件,基本上会看到明文数据,例如“你好”,“Hello World”等。对于前一种看不懂的就是使用字节来表示的,能看的懂得就是使用字符来表示的。而字符也是通过字节来存储的,只不过,在不同的编码格式中所使用的字节数是不一样的,具体哪些字符需要多少个字节表示需要对应的编码表。例如:使用GBK编码存储汉字字符,则用2个字节来表示,但在UTF8中则使用3个字节来表示


字节流 FileOutputStream & FileInputStream 

File只是能操作文件或文件夹,但是并不能操作文件中的内容,要想操作文件的内容就需要使用文件IO流,其操作文件的内容主要有两种方式:以字节的方式和以字符的方式。

在Java中以字节流的形式操作文件内容的类主要是FileOutputStream 和 FileInputStream。 分别是 OutputStream(字节输出流) 和 InputStream(字节输入流) 抽象基类的子类,如下:

File sourceFile = new File("sourceFile.jpg"); 
File destFile = new File("destFile.jpg");
FileInputStream fis=null; // 读取源文件的流
FileOutputStream fos = null; // 输出到目标文件的流
try {
fis = new FileInputStream(sourceFile);
fos = new FileOutputStream(destFile);
byte[] bytes= new byte[1024];
int len = 0;
while((len=fis.read(bytes))!=-1) {
fos.write(bytes, 0, len);
}
}
catch(IOException ex) {}
finally {
    try { fis.close();} catch(IOException ex) {} 
    try { fos.close();} catch(IOException ex) {} 
}

在使用 FileOutputStream 和 FileInputStream 的过程中需要注意的地方:

FileInputStream 所要操作的文件必须存在,否则就会抛出异常。而 FileOutputStream 写入的目的文件则不需要存在,当不存在时会被创建,存在的时候会被覆盖,也可以使用 FileOutputStream 造函数的第二个参数,来实现追加文件内容。

在使用 FileInputStream 读取字节的时候,当读取到字节的末尾,再继续读取,无论多少次都会返回 -1,而返回值len表示本次读取了多少个字节。通常情况下每次读取1024个字节,可以达到空间和时间的平衡。但是具体情况也是需要具体分析的。

字节流是不存在缓冲区的,所以不需要使用flush操作刷新缓冲区,字节的读取和写入都是通过操作系统来实现的。

只要是流就是需要关闭的,无论是否在异常情况下都需要关闭流,防止占用系统资源,导致其他程序无法对该文件进行操作。但是在关闭流的时候也有可能会报异常,所以也需要 try...catch。

FileOutputStream 和 FileInputStream主要用来操作字节表现形式的文件,例如图片,可执行程序等。当然操作字符表现形式的文件也是没有问题的,只不过这么干不规范。

字符流 OutputStreamWriter & InputStreamReader

字符流操作的主要类为 OutputStreamWriter 和 InputStreamReader 。有时候又称它们为转换流。

File sourceFile = new File("sourceFile.txt");
File destFile = new File("destFile.txt");
FileInputStream fis= new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(destFile); 
InputStreamReader reader=null;
OutputStreamWriter writer=null;
try {
reader= new InputStreamReader(fis,"utf-8");
writer =new OutputStreamWriter(fos,"gbk");
char[] cbuf =new char[1024];
int len=0;
while((len=reader.read(cbuf))!=-1) {
System.out.println(String.copyValueOf(cbuf,0,len));
writer.write(cbuf, 0, len);
}
}
catch(IOException ex) {
try{reader.close();}catch(IOException ex) { }
try{writer.close();}catch(IOException ex) { }
}

上述示例主要实现了一个文件的复制,与字节流的使用方式不同的是,字符流的构造函数需要传递字节流和编码格式。这是因为操作文件内容都是以字节的形式来操作的。字符输入流根据编码表对字节流读取的字节转义成字符,同时也说明了传递编码表格式参数的重要性。如果被读取文件编码格式是UTF-8且不传递这个参数,那么这个参数为操作系统的默认编码表(对于Windows而言是GBK),如果默认的编码表与UTF-8不同(与系统编码表格式相同,可不传递此参数),在转义为字符的过程中就会出现问题。假如文件内容为“好”,在UTF-8中对应的字节为-10-20-30。那么就以系统的默认编码表来转义,假如默认为GBK,“好”字的编码为-50-60,由原来3个字节表示汉字,现在变成了2个字节表示汉字,又由于编码表不兼容,所以导致出现乱码。而在使用字符输出流的时候,将字符按照编码表参数转化为字节后再写入对应编码格式的文件中去。如果输出的内容是以追加的方式,那么需要保证前后两个输出文件内容的编码格式一样,否则也会出现乱码。假如之前的输出文件是GBK格式,你使用字符输出流输出的字符格式为UTF8并追加到文件中去,这个时候乱码就产生了。

综上过程,也就知道大家为什么又称FileOutputStream 和 InputStreamReader为转换流了。

传递给字符流的字节流不需要单独的进行关系,在字符流关闭的时候会调用字节流的close()方法。

FileWriter & FileReader(字符流子类)

FileWriter 和 FileReader 分别是 OutputStreamWriter 和 InputStreamReader 的子类,只不过他们是只能操作系统默认编码表的字符流。

也可以这么简单的理解: OutputStreamWriter 和 InputStreamReader 的构造函数不支持传递第二个参数,就是操作系统默认的编码表。

所以在使用上只需要注意操作的文件编码格式是否与系统默认的编码格式一致即可。既然不传递第二个参数就可以达到相同的效果,为什么还会有这个两个类呢?

因为这两个类操作简单。

File sourceFile = new File("sourceFile.txt");
File destFile = new File("destFile.txt");
BufferedWriter bw =null;
BufferedReader br =null;
try {
FileReader reader= new FileReader(sourceFile); 
FileWriter writer=new FileWriter(destFile); 
bw =new BufferedWriter(writer);
br =new BufferedReader(reader);
String line =null;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
}
}
catch(IOException ex) {}
finally {
try { bw.close();} catch(IOException ex) {} 
try { br.close();} catch(IOException ex) {} 
}

缓冲区flush

无论是使用 FileWriter & FileReader 还是 OutputStreamWriter & InputStreamReader ,在他们的内部都会存在缓冲区的,

默认大小为8192字节。如果不对流进行关闭的话,数据会继续存在缓冲区,不会存储到文件上,

除非手动调用flush方法或者是在缓冲区中写入的数据超过了缓冲区的大小,数据才会刷新到文件上。而调用close方法的内部会先调用flush刷新缓冲区。

缓冲区字节流、缓冲区字符流 BufferedOutputStream & BufferedInputStream & BufferedWriter & BufferedReader

这四个Buffered开头的类分别是为字节流和字符流提供一个合适的缓冲区来提高读写性能,尤其是在读写数据量很大的时候效果更佳显著。

其用法和不带Buffered的流没有任何区别,只不过在不带Buffered流的基础上提供了一些更加便利的方法,例如newLine(),

ReadLine()和ReadAllBytes(),他们会根据操作系统的不同添加合适的换行符,根据合适的换行符来读取一行数据和读取所有字节。

File sourceFile = new File("sourceFile.txt");
File destFile = new File("destFile.txt");
BufferedWriter bw =null;
BufferedReader br =null;
try {
FileReader reader= new FileReader(sourceFile); 
FileWriter writer=new FileWriter(destFile); 
bw =new BufferedWriter(writer);
br =new BufferedReader(reader);
String line =null;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
}
}
catch(IOException ex) {}
finally {
try { bw.close();} catch(IOException ex) {} 
try { br.close();} catch(IOException ex) {} 
}

上述的代码中有两点需要注意:

当按照行来读取字符的时候,当下一行没有内容,继续读取下一行的内容,结果会返回 null,可以此来判断文件中是否还有字符。

当读取的文件行返回为null后,仍然会执行一次循环,此时调用newLine() 会在写入的文件中多添加一个换行符,这个换行符无关紧要,可以不用考虑处理掉。

下面几个知识点就不在此进行讲解了,有兴趣的同学可以去了解一下

ObjectOutputStream & ObjectInputStream

Properties

PrintStream & PrintWriter

参考文章:https://www.cnblogs.com/u-vitamin/p/11555444.html

IO流的选择

image.png

Serializable接口(序列化)理解及自定义序列化

简单地说,就是可以将一个对象(标志对象的类型)及其状态转换为字节码,保存起来(可以保存在数据库,内存,文件等),然后可以在适当的时候再将其状态恢复(也就是反序列化)。serialization 不但可以在本机做,而且可以经由网络操作。它自动屏蔽了操作系统的差异,字节顺序等。比如,在 Windows 平台生成一个对象并序列化之,然后通过网络传到一台 Unix 机器上,然后可以在这台Unix机器上正确地重构(deserialization)这个对象。 不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。

NIO与IO的区别

1. IO是面向流的,NIO是面向缓冲区的。  

2. IO是阻塞的,NIO是非阻塞的。  

3. NIO有选择器机制,可以让一个线程来监视多个IO通道。

NIO的优点:    

- 不需要使用 read() 或者 write() 就可以处理文件内容。  

- NIO的处理效率很快。

24人点赞 返回栏目 提问 分享一波

小礼物走一波,支持作者

还没有人赞赏,支持一波吧

留言(问题紧急可添加微信 xxl18963067593) 评论仅代表网友个人 留言列表

暂无留言,快来抢沙发吧!

本刊热文
网友在读
手机扫码查看 手机扫码查看