Java文件操作

Java文件操作

IO文件操作

文件系统操作

创建文件

删除文件

重命名文件

创建目录

判断文件权限

文件内容操作--数据流

例子:扫描指定目录

例子:进行普通文件的复制

示例:找到名称或者内容中包含指定字符的所有普通文件


什么是文件?

这里的文件是指硬盘中的文件。比如:aaa.txt文件就是一个在硬盘中存储的文件

什么是路径?

  • 绝对路径:从根节点到目标文件

  • 相对路径:从任意节点到目标文件

注意: \ 在Windows中适用 ,在代码中需要进行转义及“\\”

在 liunx 上可能存在,一个文件,有两个不同的路径能找到它。

Windows 上可以认为,路径 和 文件 是一一对应的。

路径就相当于一个文件的 “身份标识”

文件类型:

  • 文本文件:ASCII字符组成(表示范围:0-127)(补充:java中默认的字符编码是Unicode)

ASCII码很多:具体链接:http://c.biancheng.net/c/ascii/

  • 二进制文件:没有任何字符集的限制

       Txt 文本文件, .java / .c 文本文件

.class 二进制文件,.exe 二进制文件 ,jpg MP3 也是二进制文件

硬盘和内存的区别:

  1. 速度:内存比硬盘快的多

  2. 空间:内存空间比硬盘小

  3. 成本:内存比硬盘贵(现在很便宜了)

  4. 持久化:内存掉点后数据丢失,硬盘还存在

  5. 注意:JavaSE + 数据结构 都是对内存的处理

IO文件操作

I:input

O:output

文件系统操作

创建文件

签名

说明

File(File parent, String child)

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

File(String pathname)

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

File(String parent, String child)

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File file = new File("..\\hello-world.txt"); // 并不要求该文件真实存在 
        System.out.println(file.getParent()); 
        System.out.println(file.getName()); 
        System.out.println(file.getPath()); 
        System.out.println(file.getAbsolutePath()); 
        System.out.println(file.getCanonicalPath()); 
   } 
}

修饰符及返回值类型

方法签名

说明

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

String[]

list()

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

File[]

listFiles()

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File file = new File("hello-world.txt"); 
        System.out.println(file.exists()); 
        System.out.println(file.isDirectory()); 
        System.out.println(file.isFile()); 
        System.out.println(file.createNewFile()); 
        System.out.println(file.exists()); 
        System.out.println(file.isDirectory()); 
        System.out.println(file.isFile()); 
        System.out.println(file.createNewFile()); 
   } 
}

删除文件

修饰符及返回值类型

方法签名

说明

boolean

delete()

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

void

deleteOnExit()

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File file = new File("some-file.txt");
        System.out.println(file.exists()); 
        System.out.println(file.createNewFile()); 
        System.out.println(file.exists()); 
        System.out.println(file.delete()); 
        System.out.println(file.exists()); 
   } 
}

重命名文件

修饰符及返回值类型

方法签名

说明

boolean

renameTo(File dest)

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File file = new File("some-file.txt"); // 要求 some-file.txt 得存在,可以是普通文件,可以是目录 
        File dest = new File("dest.txt");   // 要求 dest.txt 不存在 
        System.out.println(file.exists()); 
        System.out.println(dest.exists()); 
        System.out.println(file.renameTo(dest)); 
        System.out.println(file.exists()); 
        System.out.println(dest.exists()); 
   } 
}

创建目录

修饰符及返回值类型

方法签名

说明

boolean

mkdir()

创建 File 对象代表的目录

boolean

mkdirs()

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File dir = new File("some-dir"); 
        System.out.println(dir.isDirectory()); 
        System.out.println(dir.isFile()); 
        System.out.println(dir.mkdir()); 
        System.out.println(dir.isDirectory()); 
        System.out.println(dir.isFile()); 
   } 
}

判断文件权限

修饰符及返回值类型

方法签名

说明

boolean

canRead()

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

boolean

canWrite()

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

public class Main { 
    public static void main(String[] args) throws IOException { 
        File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象 
        System.out.println(file.exists()); 
        System.out.println(file.canRead()); 
        System.out.println(file.canWrite()); 
        System.out.println(file.exists()); 
   } 

文件内容操作--数据流

针对文本内容,Java中提供了一组类-->"字符流" :Reader,Writer

1. 

Reader ---> FileReader

Writer---> FileWriter

通过read方法读文件,一次读取一个char(字符)或者char[](字符数组的长度)

通过write方法写文件,一次写一个 char(字符)或者char[](字符数组的长度)

2. 通过Scanner 读取字符

构造方法

说明

Scanner(InputStream is, String charset)

使用 charset 字符集进行 is 的扫描读取

public class Main { 
    public static void main(String[] args) throws IOException { 
        try (InputStream is = new FileInputStream("hello.txt")) { 
           try (Scanner scanner = new Scanner(is, "UTF-8")) { 
               while (scanner.hasNext()) { 
                   String s = scanner.next(); 
                   System.out.print(s); 
               } 
           } 
       } 
   } 
}


  1. 针对字节内容,Java中提供了一组类-->"字节流" :InputStream,OutputStream

读操作:对内容进行读操作(InputStream,Reader)

修饰符及返回值类型

方法签名

说明

int

read()

读取一个字节的数据,返回 -1 代表已经完全读完了

int

read(byte[] b)

最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表以及读完

int

read(byte[] b, int off, int len)

最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表实际读完了

void

close()

关闭字节流

注意:

InputStream 只是一个抽象类,要使用还需要具体的实现类:

基本可以认为不同的输入设备都可以对应一个 InputStream 类,从文件中读取,使用FileInputStream

FileInputStream 概述

签名

说明

FileInputStream(File file)

利用 File 构造文件输入流

FileInputStream(String name)

利用文件路径构造文件输入流

重点注意:close()

在使用完后一定要将流关闭,否则会有其他操作是的文件中的数据发生改变,并且不断的吃文件资源,使得系统奔溃。(通常这种错误,不会立即显示,但是当文件描述符吃完后就会发生错误,就像一个定时炸弹)

什么是文件描述符?

进程,是使用PCB这样的结构来表示:

  1. PID

  2. 内存指针

  3. 文件描述符表:

    1. 记载了当前进程都打开那些文件,每次打开一个文件,都会在文件描述符表申请到一个位置,这个表可以理解成一个数组,而描述符就是这个数组的下标。数组的元素为这个文件在内核中的结构体(C语言)的表示。

public class Main { 
    public static void main(String[] args) throws IOException { 
        try (InputStream is = new FileInputStream("hello.txt")) { 
            byte[] buf = new byte[1024]; 
            int len; 
            while (true) { 
                len = is.read(buf); 
                if (len == -1) { 
                    // 代表文件已经全部读完 
                    break; 
               } 
                 
                for (int i = 0; i < len; i++) { 
               System.out.printf("%c", buf[i]); 
               }
         } 
       } 
   } 
} 

注意:读取的内容为中文的话,且字符集为UTF-8,每个汉字三个字节。Unicode和utf8,是两种不同的编码表,即使是一个汉字字符,得到的数值也是不同的。


写操作:对内容进行写操作(OutputStream,Writer)

修饰符及返回值类型

方法签名

说明

void

write(int b)

写入要给字节的数据

void

write(byte[] b)

将 b 这个字符数组中的数据全部写入 os 中

int

write(byte[] b, int off, int len)

将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个

void

flush()

调用 flush(刷新)操作,将数据刷到设备中

重要:

flush():

I/O 的速度是很慢的,所以,大多的 OutputStream 为 了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

跟 InputStream 一样,OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。现在还是写入文件中, 所以使用 FileOutputStream

public class Main { 
    public static void main(String[] args) throws IOException { 
        try (OutputStream os = new FileOutputStream("output.txt")) { 
           String s = "Nothing"; 
            byte[] b = s.getBytes();//这里的方法里面也可以选择字符集 
            os.write(b); 
           
            // 不要忘记 flush 
            os.flush(); 
       } 
   } 
}

同样的对对于字符的写入:

利用 OutputStreamWriter 进行字符写入,需要搭配PrintWriter进行使用

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

OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8"); // 告诉它,我们的字符集编码是 utf-8 的 
PrintWriter writer = new PrintWriter(osWriter); 
// 接下来我们就可以方便的使用 writer 提供的各种方法了 
writer.print("Hello"); 
writer.println("你好"); 
writer.printf("%d: %s\n", 1, "没什么"); 
// 不要忘记 flush 
writer.flush();

例子:扫描指定目录

并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件

public class Main { 
    public static void main(String[] args) throws IOException { 
        Scanner scanner = new Scanner(System.in); 
        System.out.print("请输入要扫描的根目录(绝对路径 OR 相对路径): "); 
        String rootDirPath = scanner.next(); 
        File rootDir = new File(rootDirPath); 
        if (!rootDir.isDirectory()) { 
            System.out.println("您输入的根目录不存在或者不是目录,退出"); 
            return; 
       } 
        System.out.print("请输入要找出的文件名中的字符: "); 
        String token = scanner.next(); 
        List<File> result = new ArrayList<>(); 
        // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历 
        scanDir(rootDir, token, result);
       System.out.println("共找到了符合条件的文件 " + result.size() + " 个,它们分别是"); 
        for (File file : result) { 
            System.out.println(file.getCanonicalPath() + "   请问您是否要删除该文件y/n"); 
            String in = scanner.next(); 
            if (in.toLowerCase().equals("y")) { 
                file.delete(); 
           } 
       } 
   } 
    private static void scanDir(File rootDir, String token, List<File> result) { 
        File[] files = rootDir.listFiles(); 
        if (files == null || files.length == 0) { 
            return; 
       } 
        for (File file : files) { 
            if (file.isDirectory()) { 
                scanDir(file, token, result); 
           } else { 
                if (file.getName().contains(token)) { 
                    result.add(file.getAbsoluteFile()); 
               } 
           } 
       } 
   } 
} 

例子:进行普通文件的复制

public class Main { 
    public static void main(String[] args) throws IOException { 
        Scanner scanner = new Scanner(System.in); 
         
        System.out.print("请输入要复制的文件(绝对路径 OR 相对路径): "); 
        String sourcePath = scanner.next(); 
        File sourceFile = new File(sourcePath); 
        if (!sourceFile.exists()) { 
            System.out.println("文件不存在,请确认路径是否正确"); 
            return; 
       } 
        if (!sourceFile.isFile()) { 
            System.out.println("文件不是普通文件,请确认路径是否正确"); 
            return; 
       } 
        System.out.print("请输入要复制到的目标路径(绝对路径 OR 相对路径): ");
        String destPath = scanner.next(); 
        File destFile = new File(destPath); 
        if (destFile.exists()) { 
            if (destFile.isDirectory()) { 
                System.out.println("目标路径已经存在,并且是一个目录,请确认路径是否正确"); 
                return; 
           } 
            if (destFile.isFile()) { 
                System.out.println("目录路径已经存在,是否要进行覆盖?y/n"); 
                String ans = scanner.next(); 
                if (!ans.toLowerCase().equals("y")) { 
                    System.out.println("停止复制"); 
                    return; 
               } 
           } 
       } 
        try (InputStream is = new FileInputStream(sourceFile)) { 
            try (OutputStream os = new FileOutputStream(destFile)) { 
                byte[] buf = new byte[1024]; 
                int len; 
                while (true) { 
                    len = is.read(buf); 
                    if (len == -1) { 
                        break; 
                   } 
                    os.write(buf, 0, len); 
               } 
                 
                os.flush(); 
           } 
       } 
        System.out.println("复制已完成"); 
   } 
}

示例:找到名称或者内容中包含指定字符的所有普通文件

扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)

public class Main { 
    public static void main(String[] args) throws IOException { 
        Scanner scanner = new Scanner(System.in); 
        System.out.print("请输入要扫描的根目录(绝对路径 OR 相对路径): "); 
        String rootDirPath = scanner.next();     
        File rootDir = new File(rootDirPath); 
        if (!rootDir.isDirectory()) { 
            System.out.println("您输入的根目录不存在或者不是目录,退出"); 
            return; 
       } 
        System.out.print("请输入要找出的文件名中的字符: "); 
        String token = scanner.next(); 
        List<File> result = new ArrayList<>(); 
        // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历 
        scanDirWithContent(rootDir, token, result); 
        System.out.println("共找到了符合条件的文件 " + result.size() + " 个,它们分别是"); 
        for (File file : result) { 
            System.out.println(file.getCanonicalPath()); 
       } 
   } 
    private static void scanDirWithContent(File rootDir, String token,  
List<File> result) throws IOException { 
        File[] files = rootDir.listFiles(); 
        if (files == null || files.length == 0) { 
            return; 
       } 
        for (File file : files) { 
            if (file.isDirectory()) { 
                scanDirWithContent(file, token, result); 
           } else { 
                if (isContentContains(file, token)) { 
                    result.add(file.getAbsoluteFile()); 
               } 
           } 
       } 
   } 
    // 我们全部按照utf-8的字符文件来处理 
    private static boolean isContentContains(File file, String token) throws 
IOException { 
        StringBuilder sb = new StringBuilder(); 
        try (InputStream is = new FileInputStream(file)) { 
            try (Scanner scanner = new Scanner(is, "UTF-8")) { 
                while (scanner.hasNextLine()) { 
                    sb.append(scanner.nextLine()); 
                    sb.append("\r\n"); 
               } 
           } 
       } 
        return sb.indexOf(token) != -1; 
   } 
}

注意:

这里有一个细节需要注意,以上的案例都是在文件大小较小的情况的实行的。诺是出现一个文件大小大于内存的大小,如何解决?

比如内存只有 1G 而文件有 4G ,理论上一次可以读取 1G 的内容,可是,诺是有些字符在第一个G 和 第二个 G的分界点,此时就会导致一个问题,字符被夹断了。所以我们在读取时就不能一次读取 1G 的内容,而是读取小于 1G 内容,比如:第一次读取 1G的内容,第二次 从1G-1K的位置开始读取,每次读取都会重复 (每次-1)*1k 的内容

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值