Java文件IO

Posted 爱敲代码的三毛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java文件IO相关的知识,希望对你有一定的参考价值。

文章目录


Java中的文件操作

在Java中,一般谈到文件,都是指一个存在磁盘上的文件(狭义的文件),抛开Java,站在系统的角度来看,操作系统在管理很多软件资源和硬件设备的时候,都是把这些东西抽象成了一个一个的文件了,这也是系统中典型的"一切皆文件"的思想。

狭义的文件,可以分为两类

  1. 普通文件
  2. 目录文件(文件夹)

在Java标准库java.io下提供了一个File类来对文件(包括目录)进行了抽象描述,注意:有File对象并不表示File对象的文件一定存在。

File

常用构造方法

构造方法说明
File(File parent,String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent,String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示

绝对路径:可以简单理解为带有盘符的

相对路径:基于当前位置的路径一般以 . 或 … 开头,.\\表示当前路径,…\\表示上一级路径

方法

返回值类型方法名说明
StirnggetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
booleandeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[]list返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleananWrite()判断用户是否对文件有可写权限

所谓的文件移动(剪切粘贴),对于操作系统来说,其实是一个非常高效的操作。每个文件都有个属性,这个属性里就包括了文件的路径,移动操作其实只是修改了文件的路径属性而已。

所谓的文件复制(复制粘贴),对于操作系统来说,很可能是一个非常低效的操作。就需要把文件的内容都读读出来,然后再拷贝一份,写入磁盘中。如果文件比较大,复制粘贴的开销就比较大了

经典面试题

给你一个list方法,如何遍历一个目录中所有的文件(包含子目录中的文件)

public class TestDemo 
    private static List<String> result = new ArrayList<>();
    public static void printAllFile(String path) 
        File file = new File(path);
        //判读这个路径是否真实存在
        if (file.exists()) 
            File[] files = file.listFiles();
            for (File f : files) 
                //如果是普通文件
                if (f.isFile()) 
                    result.add(f.getName());
                 else if (f.isDirectory()) 
                    //如果是目录文件
                    printAllFile(f.getPath());
                 else 
                    //其它文件..(管道)
                
            
        
    
    public static void main(String[] args) 
        printAllFile("./");
        for (String s : result) 
            System.out.println(s);
        
    

文件内容的读写——数据流

在Java标准库中,读写文件相关的类,有很多。

InputStream/FileInputStream

文件读取操作,按照字节为单位进行读文件

OutputStream/FileOutputStream

文件写入操作,按照字节为单位进行写文件

InputStream

返回值类型方法名说明
intread()读取一个字节的数据以整数形式放回,返回-1代表已经读取完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读取到的数量,返回-1代表已经读取完了
intread(byte[] b,int off,int len)最多读取 len - off 个字节的数据到 b 中,放在从off开始,返回实际读取的数量,-1代表已经读取完毕
voidclose()关闭字节流

注意:使用流对象,读写完文件之后,一定要及时关闭,如果没有关闭,就可能造成资源泄露

FileInputStream

构造方法说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流
public class Main 
    public static void main(String[] args) throws IOException 
        //这种写法更好
        try (InputStream inputStream = new FileInputStream(".\\\\.\\\\.\\\\test.txt")) 
            while (true) 
                int len = inputStream.read();
                if (len == -1) 
                    break;
                
                System.out.printf("%c",len);
            
        catch (IOException e) 
            e.printStackTrace();
        
    
    public static void main1(String[] args) throws IOException 
        InputStream inputStream = null;
        try 
            //创建实例就相当与在打开文件
            inputStream = new FileInputStream("./././test.txt");
            //通过逐个字节的方式把文件内容读取出来
           while (true) 
               //read无参版本默认每次读取一个字节
               //无参版本的read。返回值就是每次读取的字节
               //这个返回值就是 0~255
               int len = inputStream.read();
               //如过读到文件末尾(EOF)就会返回 -1
               if (len == -1) 
                   break;
               
               System.out.printf("%c",len);
           
         catch (IOException e) 
            e.printStackTrace();
        finally 
            try 
                //关闭
                inputStream.close();
             catch (IOException e) 
                e.printStackTrace();
            
        
    

InputStream只是一个抽象类,要使用还需要具体的实现类,关于InputStream实现类有很多,基本可以认为不同的输入设备上都可以对应一个InputStream类,我们现在只关心从文件中读取,所有使用FileInputStream。它继承于InputStream。

注意:如果FileInputStream的创建写到 try()的括号里,就不需要手动close了,等代码执行完毕后会自动close.只要实现了Closeable接口就可以放到try里.

还可以利用byte数组来读取文件内容

public static void main(String[] args) 
        try (InputStream inputStream = new FileInputStream("./././test.txt")) 
            while (true) 
                byte[] bytes = new byte[1024];
                int len = inputStream.read(bytes);
                if (len == -1) 
                    break;
                
                String s = new String(bytes,0,len);
                System.out.println(s);
            
         catch (IOException e) 
            e.printStackTrace();
        
    

利用Scanner进行字符读取

上面的读取方法,直接使用InputStream进行读取是比较麻烦的,这个时候就可以使用常用的Scanenr类来进行读取

构造方法说明
Scanner(InputStream is, String charset)使用 charset 字符集进行 is 的扫描读取
public static void main(String[] args) 
        //如果要从文件读取出中文,借出 Scanner 就可以从文件中读取
        try (InputStream inputStream = new FileInputStream("./././test.txt"))
            //这中写法就不用调用 Scanner的 close 方法
            try (Scanner sc = new Scanner(inputStream,"utf-8")) 
                while (sc.hasNext()) 
                    String str = sc.next();
                    System.out.print(str);
                
            
         catch (IOException e) 
            e.printStackTrace();
        

    

OutputStream

返回值方法说明
voidwrite(int b)文件写入,指定字节数据
voidwrite(byte[] b)将b这个字符数组中的全部数据写入输出流中
intwrite(byte[] b,int off,int len)将 b 这个字符数组中从 off 开始的数据写入 输出流中,一共写 len 个
voidclose()闭此输出流并释放与此流相关联的任何系统资源
voidfluch()强制刷新缓冲区

我们知道I/O的速度是很慢的,所以,大多的OutputStream为了减少设备操作的次数,为了提高效率,就可以减少直接访问磁盘的次数。使用缓冲区,就能很好的解决这个问题。

缓冲区其实是一段内存空间(这个内存是OutputStream里自带的),当我们使用write方法来写数据的时候,并不是直接把数据写到磁盘上,而是先放到缓冲区(内存中),如果缓冲区满了,或者手动调用flush,猜真的会把数据写到磁盘上。

内存和磁盘之间的缓冲区,往往是一个内存空间

CPU和内存之间,其实也有缓冲区(L1,L2,L3,cache)

代码实例:

public static void main(String[] args) 
        //一旦按照 OutputStream 的方式打开文件,就会把文件的原来的内容给清空
        try (OutputStream outputStream = new FileOutputStream("./././test.txt")) 
            /* 写入一个字符
            outputStream.write('h');
            outputStream.write('e');*/
            //按照字节来写入
            byte[] buffer = new byte[] 'a','b','c','d';
            //outputStream.write(buffer);
            //按照字符串来写入
            String s = "你好世界 !";
            //将String 转换成 byte数组,指定字符串为 UTF-8
            outputStream.write(s.getBytes("UTF-8"));

            //刷新缓冲区
            outputStream.flush();
        catch (IOException e) 
            e.printStackTrace();
        
    

PrintWriter

PrintWriter 类中提供了我们熟悉的 print/println/printf 方法

public static void main(String[] args) 

        try (OutputStream outputStream = new FileOutputStream("./././test.txt")) 
            //利用 PrintWriter 类来包装一下 OutputStream 然后可以更方便的进行读写
            try (PrintWriter writer = new PrintWriter(outputStream)) 
                writer.println("这是第一行");
                writer.println("这是第二行");
                writer.println(200+300);
                writer.println("这一行是数字"+1024);
            
            outputStream.flush();
        catch (IOException e) 
            e.printStackTrace();
        
    

按字符读取文件(FileReader)

public static String readFile(String filePath) 
        StringBuilder result = new StringBuilder();
        try (FileReader fileReader = new FileReader(filePath)) 
            while (true) 
                //返回的是一个字符,但是不可能有负数,用int才能装下
                int ch = fileReader.read();
                if (ch == -1) 
                   break;
                
                result.append((char)ch);
            
         catch (IOException e) 
            e.printStackTrace();
        
        return result.toString();
    

练习

练习1:指定一个目录,扫描这个目录,找到文件名中包含了指定字符的文件.并提示用户是否要删除这个文件,根据用户的输入决定是否删除.

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
//指定一个目录,扫描这个目录,找到文件名名中包含了指定字符的文件,并让用户选择是否要删除文件
public class TestDemo1 
    public static void main(String[] args) 
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入一个绝对路径 -> ");
        String rootPath = sc.next();
        //判断该路径是否存在
        File fileDir = new File(rootPath);
        if (!fileDir.exists()) 
            System.out.println("你输入的路径不正确,程序退出!");
        
        //输入要查找的一个关键字
        System.out.println("请输入一个查找的关键字:");
        String keyWord = sc.next();

        //递归查找
        List<File> results = new ArrayList<>();
        KeyWordFindFile(fileDir,keyWord,results);

        //遍历结果判断是否需要删除
        for (File file : results) 
            System.out.println("是否删除文件(Y/N)"+file.getName());
            String input = sc.next();
            if (input.equals("Y")) 
                file.delete();
                System.out.println("删除成功");
            
        
    

    /**
     * 递归查找文件名中包含关键字的文件
     * @param rootPath File对(绝对路径)
     * @param fileNameKeyWord 查找的关键字
     * @param result list
     */
    private static void KeyWordFindFile(File rootPath,String fileNameKeyWord,List<File> result) 
        File[] files = rootPath.listFiles();

        for (File file : files) 
            if (file.isDirectory()) 
                //如果是目录就递归
                KeyWordFindFile(file,fileNameKeyWord,result);
             else if (file.isFile()) 
                //判断文件名是否包含关键字
                if (file.getName().contains(fileNameKeyWord)) 
                    result.add(file);
                
            
        
    

练习2:把文件复制到指定目录下

//把指定文件复制到另外一个路径,一定是文件
    public static void main(String[] args) throws IOException 
        System.out.print("请输要复制文件的绝对路径 -> ");
        Scanner sc = new Scanner(System.in);
        String originalPath = sc.next();
        //判断是否是文件
        File originalFile = new File(originalPath);
        if (!originalFile.isFile()) 
            System.out.println("输入文件绝对路径错误程序退出");
            return;
        
        System.out.print("请输入文件要复制到的绝对路径(指定文件名) -> ");
        String targetPath = sc.next();
        File targetFile = new File(targetPath);
        //判断文件是否已经存在
        if (targetFile.exists()) 
            System.out.println("文件已存在,是否替换(Y/N)");
            String input = sc.next();
            if (input.equals("N")) 
                System.out.println("复制取消");
                return;
            
        
        //开始复制
        try (InputStream inputStream = new FileInputStream(originalFile);
             OutputStream outputStream = new FileOutputStream(targetFile)) 
            //一次性最多读取1024个字节
            byte[] bytes = new byte[1024];
            while (true) 
                int len = inputStream.read(bytes);
                if (len == -1) 
                    break;
                
                //读多少写多少
                outputStream.write(bytes,0,len);
            
            //不flush也是可以的,在close后会自动flush
            outputStream.flush();
         catch (IOException e) 以上是关于Java文件IO的主要内容,如果未能解决你的问题,请参考以下文章

Java 流(Stream)文件(File)和IO

流Stream 文件File 流IO

java从零开始之Java StreamFileIO

java从零开始之Java StreamFileIO

Java流(Stream)文件(File)和IO

java包