上篇文章,小编分享了文件的一些基础知识。

那么小编接着来分享下关于文件以及在java中如何操作文件IO的吧。

文件类型

文件也是有类型分类的,一般分为二进制文件和文本文件。

那么什么又是二进制文件和文本文件呢?

二进制文件:

是以计算机直接处理的二进制格式存储数据。这样的文件不是为了直接阅读而设计的,而是为了计算机软件之间的数据交换和存储。

比如,我用记事本打开一些图片或者音视频文件的时候,会显示乱码,此时就是一个二进制文件

文本文件:

是以一种可供人类可读的形式存储数据的文件。通常包含字符(字母、数字、符号),这些字符通过特定的编码标准(ASCLL、UTF-8)进行编码。

简单来说它是能被人类读懂的。

比如

那么有了文件之后,我们肯定是要对它”动手动脚“的,所以接下来分享的是文件的操作。

那么对应文件的操作,分为这两大类

文件系统的操作、文件内容的操作。

文件系统的操作:

关于一些文件的创建、目录的创建、目录的创建和删除、重命名文件、判断文件是否存在等等。

文件内容的操作:

主要是读文件和写文件。

那么在java中呢,也是提供了一个标准库类对文件系统进行操作。

java.io.File

接着来看看File常见属性、构造方法、方法吧

修饰符及类型

属性

说明

static String

pathSeparator

依赖于系统的路径分隔符,String类型的表示

static char

pathSeparator

依赖系统的路径分隔符,char类型的表示

那么这里值得注意的是,分隔符,Windows:\

Linux/mac系统:/

构造方法:

签名

说明

File(File parent,String child)

根据父目录+孩子文件路径,创建一个新的File实例

File(String pathname)

根据文件路径创建一个新的File实例,路径可以是绝对路径或者相对路径

File(String parent,String child)

根据父目录+孩子文件路径,创建一个新的File实例,父目录用路径表示

注意,创建的文件实例,里面的文件路径可以是不存在。

方法:

修饰符及返回值类型

方法签名

说明

String

getParent()

返回File对象的父目录文件路径

String

getName()

返回File对象的纯文件名称

String

getPath()

返回File对象的文件路径

String

getAbsolutePath()

返回File对象的绝对路径

String

getCanonicalPath()

返回File对象的修饰过的绝对路径

boolean

exists()

判断File对象描述的文件是否真实存在

boolean

isDirectory()

判断File对象代表的文件是否是一个目录

boolean

isFile()

判断File对象代表的文件是否是一个普通文件

boolean

createNewFile()

根据File对象,自动创建一个空文件。成功创建后返回true

boolean

delete()

根据File对象,删除该文件。成功删除后返回true

void

deleteOnExit()

根据File对象,标注文件将被删除,删除动作会到JVM运行结束时才会进行

String[]

list()

返回File对象代表的目录下的所有文件名

File[]

listFiles()

返回File对象代表的目录下的所有文件,以File对象表示

boolean

mkdir()

创建File对象代表的目录

boolean

mkdirs()

创建File对象代表的目录,如果必要,会创建中间目录

boolean

renameTo(File dest)

进行文件改名,也可以视为我们平时的剪切、粘贴操作

boolean

canRead()

判断用户是否对文件有可读权限

boolean

canWrite()

判断用户是否对文件有可写权限

举个例子来示范下如何使用的吧

文件系统操作

创建文件

首先这是没有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){

        }
    }

那么到这里,就分享完这几个类的操作。

但还是要说明一点的是这几个类都是先打开文件在先的。

不打开文件就无从谈其他操作。

打开完文件最后才进行读写文件,最后关闭文件。

那么小编就暂时分享到这里!

完!

文章作者: 南汐
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 www.phblog.cloud
JavaSE JavaSE
喜欢就支持一下吧