目录
针对文件系统进行操作
针对文件内容进行操作
java针对文件操作可以分为两种:1)针对文件系统进行操作,如创建文件,删除文件,创建目录,重命名文件等。
2)针对文件内容进行操作,如读文件,写文件等。
针对文件系统进行操作
我们可以使用File类对文件系统进行操作。
java标准库提供了File类(出自java.io包里,和输入输出相关的内容就在这个包里)表示一个文件,进一步的通过File提供的方法,就可以进行文件系统操作了。
我们可以通过以下方法创建File类的对象:
File f = new File("./test.txt");
File中提供了很多文件操作的方法:
演示获取文件路径:
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File f = new File("./test.txt");
// f.createNewFile();
System.out.println(f.getParent());
System.out.println(f.getPath());
System.out.println(f.getName());
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());
}
}
注:当我们在java中运行程序的时候,基准目录是什么呢?此时基准目录是项目所在目录。
关于deleteOnExit方法:有些程序运行过程中,会产生“临时文件”,就需要在进程结束之后,自动删除掉,这样的情况就可以使用deleteOnExit方法来实现。
针对文件内容进行操作
文件内容操作就是对文件中的具体内容进行操作,如文件打开,关闭,读写。java中可以通过“流”(stream)这样的一组类,进行上述操作。“流”的方式读写文件方式非常灵活,我们这里就使用“文件流”。
java中,用来操作文件内容的“流”是一组类。
java中的文件流可以分为两组:
1)字节流 : 以字节为单位,读写数据的。 如InputStream , OutputStream 。操纵的是二进制文件。
2)字符流: 以字符为单位,读写数据的。如Reader, Writer。操纵的是文本文件。
使用的核心步骤大体如下:
1、通过构造方法,打开文件。
2、通过read方法读文件内容。
3、通过write方法写文件内容。
4、通过close方法关闭文件。
由于InputStream是一个抽象类,我们不能直接new它,但是我们可以对它的子类FileInputStream和FIleOutputStream创建对象,就是文件输入输出流。
FileInputStream
先讲FileInputStream类的使用,首先创建一个对象,也就是打开一个文件
InputStream inputStream = new FileInputStream("/test.txt");
如果文件存在,打开成功,接下来就可以读取文件内容。有以下三个方法:
第一个方法:无参数,一次读取一个字节,读取到的内容通过返回值来表示。注意,此处返回的值是int类型,原因如下:1)为了能够表示读到文件末尾的情况,当返回-1时,就代表读到文件末尾了。
2)为了确保读到的数据都是正数。对于字节这样的数据来说,是不需要进行算术运算的。比如,有一个图片,图片里由很多的字节构成,每个字节都有特定的含义,对于这里的字节进行加减乘除是没有意义的。所以,“字节”这样的概念,本身应该是“无符号的”,此处通过int 类型就可以确保读出来的字节都是整数,按照“无符号”的方式来处理了。
第二个方法:带有一个参数 byte[]
在使用这个方法时,我们要先创建一个byte类型的数组,利用数组作为容器来读取文件数据。
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
System.out.println("n = "+ n);
if(n ==-1){
break;
}
for(int i= 0; i< n;i++){
System.out.printf("0x%x",bytes[i]);
}
System.out.println();
先准备好空的数组,方法执行完毕之后,就会把读到的数据填写到byte数组中。一次读取多少数据,取决于数组的大小。返回值int表示这次实际读取到多少字节了。
为何要用数组?
数组相当于文件内容的容器,第一种read方法,一次只读一个字节,操作频次高,但是一次读的东西少。(1次读1个字节,和一次读1kb个字节,开销是差不多的)而如果我们创建一个1024大小的数组,一次就可读1024个字节。低效操作次数少了,一次读的东西更多,整体的速度就更快了。相当于嗑瓜子,一次丢一个瓜子皮,一次对100个瓜子皮,开销是差不多的。
这种写法,在java中不太常见,是用参数来作为函数的返回结果,称为“输出型函数”。
第二个方法,读到的数据是往整个数组填充。而第三个方法是指定填充数组的一部分,指从offset下标开始,最多填充len这么长。
有的时候,可能需要一个数组里面不同的部分表示不同含义的数据。可能第一次read,填充第一部分,第二次read,填充第二部分。
注:千万别忘了在操作完文件之后,关闭文件。因为在打开文件的时候,会在操作系统内核,PCB结构体中,给“文件描述符表”(就是一个顺序表)添加一个元素,这个元素表示当前打开的文件的相关信息。但是文件描述符表长度存在上限,并且是不能扩容的。当进行close的时候,就会释放这个文件描述符表上对应的元素。一旦打开文件而不关闭,就会使文件描述符表被占满,一旦被占满之后,尝试再次打开,就会打开文件失败(其他操作,如网络通信也可能受到影响)。
我们可以使用以下方法避免:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo8 {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("./test.txt")){
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
for (int i = 0; i < n; i++) {
System.out.printf("0x%x ",bytes[i] );
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
把流对象的创建写到try()里之后,此时,代码执行出了try{}时,就会自动调用inputStream的close了。注意,务必要实现Closeable接口的类,才能够放到try()里。
OutputStream
OutputStream的用法和InputStream基本一致,只是核心操作从read变成了write。
第一个方法:一次write一个字节,参数是int类型。
第二个方法:一次write若干个字节,会把参数数组里所有的字节都写入文件中。
第三个方法:一次write若干字节,把数组从offset下标开始,连续写len个字节。
次数写的这几个数字,就是abcd的ascii值。
注意,按照默认写方式打开文件的时候,会把文件原有的内容清空掉。(不是write清空的,而是打开操作清空的)。但是如果使用“追加写”方式打开,就可以了。
只需要创建文件对象的时候,将append参数设为true即可。这样新写入的数据就会追加在之前写的内容末尾。
以上关于文件类,希望对你有所帮助。