字符流
InputStream类和OutputStream类在读写文件时操作的都是字节,如果希望程序中操作字符,使用这两个类就不太方便,为此JDK提供了字符流。同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer,其中,Reader是字符输入流,用于从某个源设备读取字符。Writer是字符输出流,用于向某个目标设备写入字符。Reader和Writer作为字符流的顶级父类,也有许多子类。
字符流操作文件
程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符,便可以使用字符输出流FileReader,通过此流可以从关联的文件中读取一个或一组字符。
首先在项目当前目录下新建文本文件“reader.txt”并在其中输入字符“itcast”,然后创建一个使用字符输入流FileReader读取文件中字符的类。
需要注意的是,字符输入流的read()方法返回的是int类型的值,如果想获得字符就需要进行强制类型转换,如上面代码第十行将变量ch转为char类型再打印。
如果向文件中写入字符就需要使用FileWriter类,该类是Writer的一个子类。
程序运行后会在当前目录下生成一个名称为“writer.txt”的文件,打开此文件会看到以下内容
FileWriter同OutputStream一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,首先会清空文件中的内容,再进行写入。如果想在文件末尾追加数据,同样需要调用重载的构造方法。
包装流可以通过对一个已存在的流进行包装来实现数据读写功能,利用包装流可以有效地提高读写数据的效率。字符流同样提供了带缓冲区的包装流,分别是BufferedReader和BufferedWriter,其中,BufferedReader用于对字符输入流进行包装,BufferedWriter用于对字符输出流进行包装。需要注意的是,在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。下面来学习如何使用这两个包装流实现文件的拷贝。
其中,readLine()方法会逐个读取字符,当读到回车符'\r'或换行符'\n'时会将读到的字符作为一行的内容返回。
需要注意的是。由于字符缓冲流内使用了缓冲区,在循环中调用了BufferedWriter的write()方法写入字符时,这些字符首先会被写入缓冲区,当缓冲区写满或调用close()方法时,缓冲区中的字符才会被写入目标文件。因此在循环结束时一定要调用close()方法,否则极有可能会导致部分存在缓冲区中的数据没有被写入目标文件。
转换流
前面的IO流可以分为字节流和字符流,有时字节流和字符流之间也需要进行转换。在JDK中提供了两个类可以将字节流转换为字符流,它们分别是InputStreamReader和OutputStraemWriter。
InputStreamWriter是Reader的子类,它可以将一个字节输入流装换成字符输入流,方便直接读取字符。OutputStreamWriter是Write的子类,它可以将一个字节输出流转换为字符输出流,方便直接写入字符。
实现了字节流和字符流之间的转换,将字节流转换为字符流,从而实现直接对字符的读写。需要注意的是,在使用转换流时,只能针对操作文本文件的字节流进行转换,如果字节流操作的是一张图片,此时转换为字符流就会造成数据丢失。
File类
IO流可以对文件的内容进行读写操作,在应用程序中还会经常对文件本身进行一些常规操作,例如创建一个文件、删除或者重命名某个文件、判断硬盘上某个文件是否存在、查询文件最后修改时间等。针对文件的这类操作,JDK中提供了一个File类,该类封装了一个路径,并提供了一系列方法用于操作该路径所指向的文件。
File类用于封装一个路径,这个路径可以是从系统盘符开始的绝对路径,也可以是相对于当前目录而言的相对路径。File类内部封装的路径可以指向一个文件,也可以指向一个目录,在File类中提供了针对这些文件或目录的一些常规操作。具体可以自己查看API。
首先,在当前目录下创建一个文件“example001.txt”并在文件中输入内容“itcast”,然后创建一个使用File类常用方法的类,来查看文件的相应内容。
遍历目录下的文件
File类中String[ ] list() 列出指定目录的全部内容,只是列出名称
创建了一个File对象,并指定了一个路径,通过调用File的isDirectory()方法判断路径指向的是否为存在目录,如果存在就调用list()方法,获得一个String类型的数组names,数组中包含这个目录下所有文件的文件名。接着通过循环遍历数组names,依次打印出每个文件的文件名。
虽然上面实现遍历一个目录下所有文件的功能,然而有时程序只是需要得到指定类型的文件,如获取指定目录下所有的“.java”文件。针对这种需求,File类中提供了一个重载的list(FilenameFilter filter)方法,该方法接收一个FilenameFilter类型的参数。FilenameFilter是一个接口,被称为文件过滤器,当中定义了一个抽象方法accept(File dir,String name)。在调用list()方法时,需要实现文件过滤器FilenameFilter,并在accept()方法中做出判断,从而获得指定类型的文件。
步骤分析list(FilenameFilter filter)方法的工作原理
①调用list()方法传入FilenameFilter文件过滤器对象
②取出当前File对象所代表目录下的所有子目录和文件
③对于每一个子目录或文件,都会调用文件过滤器对象的accept(File dir,String name)方法,并把代表当前目录的File对象以及这个子目录或文件的名字作为参数dir和name传递给方法
④如果accept()方法返回true,就将当前遍历的这个子目录或文件添加到数组中,如果返回false,则不添加。
接下来通过一个案例来演示如何遍历指定目录下所有扩展名为“.txt”的文件。
有时候在一个目录下,除了文件,还有子目录,如果想得到所有子目录下的File类型对象,list()方法显然不能满足要求,这时就需要使用File类提供的另一个方法listFiles()。listFiles()方法返回一个对象数组,当对数组中的元素进行遍历时,如果元素中还有子目录需要遍历,则需要使用递归。
定义了一个静态方法fileDir(),该方法接收一个表示目录的File对象。在方法中,首先通过调用listFiles()方法把该目录下所有的子目录和文件存到File类型的数组files中,接着遍历数组files,并对当前遍历的File对象进行解析。如果是目录就重新调用fileDir()方法进行递归,如果是文件就直接打印输出文件的路径,这样该目录下的所有文件就被重新遍历出来了。
删除文件及目录
首先在D盘中创建一个名称为test的文件夹,然后在文件夹中创建一个文本文件,接下来创建一个使用delete()方法删除文件夹的类。
File类的delete()方法只能删除一个指定的文件,加入File对象代表目录,并且目录下包含子目录或文件,则File类的delete()方法不允许对这个目录进行直接删除。这种情况下,需要通过递归的方法将整个目录以及其中的文件全部删除。
网友评论