探索文件IO奥秘:操作文件IO
上篇文章,小编分享了文件的一些基础知识。
那么小编接着来分享下关于文件以及在java中如何操作文件IO的吧。
文件类型
文件也是有类型分类的,一般分为二进制文件和文本文件。
那么什么又是二进制文件和文本文件呢?
二进制文件:
是以计算机直接处理的二进制格式存储数据。这样的文件不是为了直接阅读而设计的,而是为了计算机软件之间的数据交换和存储。
比如,我用记事本打开一些图片或者音视频文件的时候,会显示乱码,此时就是一个二进制文件

文本文件:
是以一种可供人类可读的形式存储数据的文件。通常包含字符(字母、数字、符号),这些字符通过特定的编码标准(ASCLL、UTF-8)进行编码。
简单来说它是能被人类读懂的。
比如

那么有了文件之后,我们肯定是要对它”动手动脚“的,所以接下来分享的是文件的操作。
那么对应文件的操作,分为这两大类
文件系统的操作、文件内容的操作。
文件系统的操作:
关于一些文件的创建、目录的创建、目录的创建和删除、重命名文件、判断文件是否存在等等。
文件内容的操作:
主要是读文件和写文件。
那么在java中呢,也是提供了一个标准库类对文件系统进行操作。
java.io.File
接着来看看File常见属性、构造方法、方法吧
那么这里值得注意的是,分隔符,Windows:\
Linux/mac系统:/
构造方法:
注意,创建的文件实例,里面的文件路径可以是不存在。
方法:
举个例子来示范下如何使用的吧
文件系统操作
创建文件

首先这是没有test.txt文件的
那么接下来的代码就是在此目录下进行创建test.txt
public static void main(String[] args) throws IOException {
File file=new File("E://Temp_Test/test.txt");
boolean flg=file.createNewFile();
System.out.println(flg);
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}

那么这里要值得说明的是,如若是
File file=new File("./test");
这样的就是代表,当前文件是在当前项目目录下。
那么这是生成一些文件。
那么接下来介绍下目录是如何操作的
目录一般就是包含创建目录、删除目录、对目录进行改名、移动等等。
创建目录:

首先在当前项目下没有abc这个目录的,那么接下来的代码就是演示下如何创建这个目录的

当然除此之外话可以进行递归性的创建目录
代码:
public static void main(String[] args) {
File file=new File("./abc/def");
boolean flag=file.mkdirs();
System.out.println(flag);
}
那么对目录改名又是如何操作的呢
目录改名:

abc目录下只有def目录,那么对这个def目录进行改名,改成cad
public static void main(String[] args) {
File oldfile=new File("./abc/def");
File newfile=new File("./abc/cad");
oldfile.renameTo(newfile);
}
那么如何进行目录的移动呢?
目录移动:

代码:
public static void main(String[] args) {
File oldfile=new File("./abc/def");
File newfile=new File("./def");
oldfile.renameTo(newfile);
}目录一些改名、移动等操作,对于普通的文本文件而言,甚至是一些其他类型文件,操作也是差不多的
那这里值得注意的是
1.如若./abc/def是一个文件,并且不存在,那么此时./abc/def将被重命名为./def,即从原来的目录中移动到当前目录下,即和abc目录同级
2.如果 ./abc/def 是一个目录,并且 ./def 不存在,同样的情况会发生:它将被移动到当前目录下并改名为 ./def
如若是存在def目录的,那么此时同样的情况会发生:它将被移动到当前目录下并改名为 ./def
但不排除renameTo()可能失败,在某些情况下。
那么对于这个文件系统的一些操作就分享到这里,接下里分享的是对文件内容的操作。
文件内容操作
对于文档内容的操作,这是系统API提供支持的,但java对其进行了封装。
所以标准库也提供了一套API供我们使用。
这些API使用是在java.io包下。
对文件内容操作是通过文件IO流进行的。
IO的意思就是输入输出的意思。
IO流对应的是输入流、输出流。
输入流:用于从数据源(例如文件,网络连接等) 读取数据到文件
输出流:用于将数据从程序写入到某个目的地。(例如文件,网络连接等);
值得注意的是,这里的输入/输出是认为的定义。所以,不同角度看,说法也不一样的。
那么这个流到底是是什么呢?
流:是一个抽象的概念,表示数据的源头和目的地之间的管道。这个管道可以是字节序列或者字符序列。
通俗的来讲就像是水管中的水流一样,是一个数据传输的通道。
那么在Java中呢,实现IO流的类很多,可以分为两大派
字节流:
以二进制进行读取,在这情况下,读取数据的基本单位,就是字节。
InputStream
OutputStream
字符流:
以字符进行读取,在这情况下,读取数据的情况下,就是字符。
Reader
Writer
而上面讲到的四个类,它们是抽象类,真正在工作的,并不是它们,而是众多实现它们的类。
那么对于众多的类来说,区分它们也是很容易的
例如,以Reader Writer为结尾的类,是实现了Reader Writer的字符流对象
以InputStream OutputStream为结尾的类,是实现了InputStream OutputStream字节流对象。
接下来分享下如何操作这些类吧。
首先介绍的是InputStream/OutputStream
InputStream Vs OutputStream
比如我有这样的一个文本文件,在当前目录下
文件内容如下:

那么如何读取它呢?
那么实现InputStream一个子类中可以提供这样的读取的操作,
这个子类就是:FileInputStream
对于这个子类,它提供的read方法是读取文件中的内容的
一般目前常用的有三个版本的:

第一个方法是读一个字节数据,返回的是一个整数,但是这个整数是0-255范围内的。
如果读取到了文本末尾,那就会返回一个-1
那么返回的是数字如何表现回文本内容呢?
以文本举例,读取一个字节后,那我们可以通过格式化输出它,比如可以强制转换为char字符也是可以的。
第二个方法
是读取的数据是放回到一个字节数组。
而提供的参数是叫“输出型”参数。
那什么叫输出型参数呢?
就是方法里面的操作,结果会写回到给定参数中,而不是通过一个返回的参数来决定。
第三个方法
这个方法表达的是,写入到这个字节数组中,从哪里到哪里开始写入。
了解完这些,此时呢我们就可以编写这个读取文件的操作代码
public static void main(String[] args) {
try(InputStream inputStream=new FileInputStream("./test.txt")){
while (true){
int ret=inputStream.read();
if(ret==-1){
return;
}
System.out.printf("0x%x\n",ret);
}
}catch (IOException e){
e.printStackTrace();
}
}
出现这个十六进制的字符,这是我们格式化输出的结果。
我们在网上查询对应的ASCLL表,可以恢复原来的表达的意思了。
如若是读取的是中文字符,比如你好,那么此时,使用的是UTF-8编码格式
这里还有一个值得注意讲的是
使用
try(){
}catch(){
}
这样格式叫 try with resources
使用这样的格式这是为什么呢?
其实使用
try{
}catch(){
}
这样的格式也是没有问题,但是,使用完InputStream这个操作之后,那么我们最后一定要调用close()方法,关闭资源操作,但也不排除真忘了。
即使是有jvm的垃圾回收机制,但也要养成这样的习惯。
为什么需要关闭资源操作呢?
因为打开这样文件操作,会占用文件描述符表的资源。
在这个表中,系统会管理谁在使用系统资源,但你使用完InputStream()后,不会自动释放资源
需要手动释放。
此时呢,如若不主动释放资源,一直添加,而这个表呢,也是有限的空间,填满后,后续打开文件的操作就会失败,而不主动释放的资源,导致的问题呢,也就叫“文件资源泄露”
而这个try with resources 是可以执行完代码块后,就自动释放资源,不需要我们进行操作了,比较省心。
而我们要写资源到里面呢?
此时呢,就可以使用OutputStream这个类了
对于OutputStream来说,我们使用它的一个子类
FileOutputStream
这个类呢,提供的write()方法
提供了三个这样的版本

这三个方法和InputStream也是较为类似的。
第一个方法是写入一个字节
第二个方法是用一个写入好数据的数组,一整个写入到文件中
第三个方法是写入到字节数组指定范围上。
了解完这些,此时呢,也可以编写写入文件的代码了
文件原本内容如下:

代码:
public static void main(String[] args) {
try(OutputStream outputStream=new FileOutputStream("./test.txt")){
String s=new String("python");
char [] arr=s.toCharArray();
for(int i=0;i<arr.length;i++){
outputStream.write(arr[i]);
}
}catch (IOException e){
e.printStackTrace();
}
}
此时呢,我们发现,写入的python,是写入到文件里了,
还发现,之前的hello不见了。
这是因为,每次的写入文件,会重新覆盖文件内容。
那么不想覆盖的话,可以添加一个参数使其变为追加模式
OutputStream outputStream=new FileOutputStream("./test.txt",true)
就添加一个true即可。
ok,讲完这个,那么再来讲讲
Reader VS Writer
对于Reader来说,我们使用它的一个子类
FileReader
这个子类所提供的方法,有几个是目前常用的

方法里的操作和InputStream刚刚所讲到差不多的。
只不过字节,对应回这个字符了。
对于它读取文件内容来说,其代码逻辑也是差不多的
那么此时我们使用了的是read(char [] cbuf)这个方法
public static void main(String[] args) {
try(Reader reader=new FileReader("./test.txt")){
char[]arr=new char[1024];
int n=reader.read(arr);
System.out.println(n);
for(int i=0;i<n;i++){
System.out.print(arr[i]);
}
}catch (IOException e){
e.printStackTrace();
}
}
反过来,我们要是想写入内容到文件里呢?
那么Writer就可以派上用场了。
我们使用的是FileWriter这个类去实例化对象
那么这个类提供的write方法也是提供了这样版本

第一个方法就是写入一个字符
第二个方法是通过一个字符串去写入
第三个方法是写入字符串,但这个字符串写入是有范围的
第四个方法是写入一个字符数组,同时这个字符数组的写入也是有范围的。
这里我们使用的是第二个方法,同时使用的是追加模式

public static void main(String[] args) {
try(Writer writer=new FileWriter("./test.txt",true)){
writer.write("你好世界");
}catch (IOException e){
}
}
那么到这里,就分享完这几个类的操作。
但还是要说明一点的是这几个类都是先打开文件在先的。
不打开文件就无从谈其他操作。
打开完文件最后才进行读写文件,最后关闭文件。
那么小编就暂时分享到这里!
完!