[JavaEE] file operation and IO


Column introduction: JavaEE from entry to advanced

Topic source: leetcode, Niuke, Jianzhi offer.

Creation goal: record the learning process of learning JavaEE

I hope to help others while improving myself, and make progress together with everyone and grow up with each other.

Academic qualifications represent the past, ability represents the present, and learning ability represents the future! 


Table of contents

1. Know the file

1.1 The files in the computer have a narrow sense and a broad sense:

1.2 Tree structure organization and directory

1.3 File path (Path)

1.4 Text files and binary files 

2. Manipulating files in Java--File class

2.1 Properties

2.2 Construction method

2.3 Method

3. Reading and writing of file content - data flow

3.1 Overview of InputStream

3.2 Overview of FileIntputStream

3.3 Overview of OutputStream

3.4 Use Scanner to read characters.

4. Small program exercises


1. Know the file

1.1 The files in the computer have a narrow sense and a broad sense:

  • A file in a narrow sense refers to an I/O device for persistent storage such as a hard disk. When saving data, it will be divided into independent units. These independent units are abstracted into the concept of files.
  • Files in a broad sense refer to the fact that many hardware devices and software resources are abstracted into files in the operating system and managed in a unified manner. For example, the network card, a hardware device, is usually operated as a file in network programming. .

In addition to the data content in the file, there is also some information such as: file name, file type, file size, etc. that do not exist as file data. We call this information the meta-information describing the file.


1.2 Tree structure organization and directory

With the increasing number of files, how to efficiently manage files is on the agenda. In order to organize files according to the hierarchical structure, the application of tree storage structure was born. In this way, a file specially used to store management information was born. , which is what we often call folder (folder) and directory (directory).

 What is saved in the folder and directory is the meta information we mentioned earlier.


1.3 File path (Path)

How to locate the only file we are looking for in the file is the current problem to be solved. From the perspective of the tree structure, each node of the tree is a path from a root to the node. This description method is called Called the absolute path of the file (absolute path). 

1). Absolute path style:

Path starting with c: d: drive letter

2). Relative path:

Based on the current working directory, find the specified path.

If the working directory is different, the same file is located, and the relative path is written differently.

For example: navigate to d:/tmp/111

If the working directory is d:/ relative path writing ./tmp/111

If the working directory is d:/tmp relative path writing./111

If the working directory is d:/tmp/222/111, the relative path is written as ../111 (.. means the parent directory of the current directory)

If the working directory is d:/tmp/222/bbb/111, the relative path is written as ../../111


1.4 Text files and binary files 

The distinction between file types in the Windows system is very detailed, word, exe, pictures, video, audio, source code, dynamic library...These different files can be classified as text files and binary files as a whole.

  • Text file : The stored string is composed of characters, and each character is represented by a number. These characters must be legal, and they are all data within the code table of our specified character encoding.
  • Binary files : There are no restrictions, you can store any data you want.

How to judge text files and binary files?

A simple and rude way is to open it with Notepad, because Notepad is a text file, if it is opened with garbled characters, it is a binary file, and if it is not garbled, it is a text file.


2. Manipulating files in Java--File class

Java's operations on files can be divided into:

  • 1. For file system operations. (file creation, deletion, renaming)
  • 2. Operations on file content. (reading and writing of files)

In the Java standard library, a File class is provided , which is specially used for file system operations. Note! Having a File object does not mean that there is an actual file/directory.

2.1 Properties

pathSeparator is a static variable in File, which means / or \ according to the specific system.

modifiers and types Attributes illustrate
static String pathSeparator System-dependent path separator, represented by String type
static char pathSeparator system-dependent path separator, char type representation

2.2 Construction method

sign illustrate
File(File parent , String child) Create a new File instance according to the parent directory + still file path
File(String pathname)

Create a new File instance according to the file path, the path can be an absolute path

or a relative path

 The second is the most common construction method.


2.3 Method

The File method is relatively simple, and the function of the method can be known through the method name.

Modifiers and return types method signature illustrate
String getParent() Returns the file path of the parent directory of the File object
String getName() Returns the plain file name of the FIle object
String getPath() Returns the file path of the File object (may be absolute or relative)
String getAbsolutePath() Returns the absolute path of the File object
String getCanonicalPath() Returns the absolute path decorated by the File object
boolean isDirectory() Determines whether the file of the File object code is a directory
boolean exists() Determine whether the file described by the File object actually exists
boolean isFile() Determine whether the file represented by the File object is an ordinary file
boolean createNewFile() Automatically create an empty file according to the File object, and return true after successful creation
boolean delete() According to the File object, delete the file and return true after successful deletion
void deleteOnExit()

According to the File object, the standard file will be deleted, and the deletion operation will not be performed until the end of the JVM operation.

String[] list() Returns all file names under the directory represented by the File object
File[] listFiles() Return all files under the directory represented by the File object, expressed as a File object
boolean mkdir() Create the directory represented by the File object
boolean mkdirs() Create the directory represented by the File object, creating intermediate directories if necessary
boolean renameTo(File dest) rename the file
boolean canRead() Determine whether the user has read permission on the file
boolean canWrite() Determine whether the user has writable permissions on the file

Example 1: Test the features and differences of the get series

 public static void main(String[] args) throws IOException {
        File file = new File("d:/text.txt");
        System.out.println(file.getName());
        System.out.println(file.getParent());
        System.out.println(file.getPath());
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalFile());
    }

 Example 2: Judgment of ordinary files

 public static void main(String[] args) throws IOException {
        File file = new File("d/text.txt");
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }

Example 3: Observe the phenomenon of deleteOnExit

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());
        file.deleteOnExit();
        System.out.println(file.exists());

    }

The file will be deleted only after the program finishes running. Similar to writing a word document, the system will automatically open a temporary version of the word document to save the temporary data. When the word document is suddenly closed, the temporary word document will also be closed. When you open it for the first time, you will be asked if you want to restore the edited content in the temporary file.

Example 4: Watch directory creation

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


3. Reading and writing of file content - data flow

For the file content, use the "stream object" to operate, and compare the data in the file content to the water flow. This is because reading/writing a file is similar to receiving/filling water, water can be received/filled in any ml, and data can be in any byte read/write.

The stream objects of the Java standard library are divided into two categories in terms of types:

  • 1. Byte stream: InputStream OutputStream is read in units of characters.
  • 2. Character stream: Reader Writer reads in units of characters.

The use of these classes is very fixed, and the core is four operations.

  • 1. Open the file. (construct object)
  • 2. Close the file. (close)
  • 3. Read files. (read) => for InputStream/Reader
  • 4. Write a file. (writer) => for OutputStream/Writer

Tips: InputStream/OutputStream/Reader/Writer are all abstract classes, they cannot be directly new and need specific implementation classes. There are many specific implementation classes, we only care about reading and writing from files now, so use FileInputStream / FileOutputStream / FileReader / FileWriter .


3.1 Overview of InputStream

method:

modifiers and return type method signature illustrate
int read() Read a byte of data, return -1 to indicate that it has been read
int read(byte[] b)

Read at most b.length bytes of data into b, and return the actually read data

Amount of data; -1 means read finished

int read(byte[] b,int off,int len) Read at most len-off bytes of data into b, starting from off, return the actual number read; -1 means read is finished.
void close close byte stream

3.2 Overview of FileIntputStream

Construction method:

sign illustrate
FileInputStream(File file) Use File to construct a file input stream
FileInputStream(String name) Construct input stream from file path

Example 1: read reads the first version of the file

Returns -1 after reading.

public static void main(String[] args) throws IOException {
        //创建InputStream对象时,使用绝对路径和相对路径都可以,也可以使用File对象
        InputStream inputStream = new FileInputStream("./text.txt");
        //进行读操作
        while (true){
            int b = inputStream.read();
            if(b == -1){
                //读完完毕
                break;
            }
            System.out.println((byte)b);
        }
        inputStream.close();
    }

The content in the file is "hello"

Read result:


Example 2: read reads the second version of the file

read reads the second version of the file and needs to prepare a byte array in advance, and then pass the byte array as a parameter to the read method, so that read can fill in the array internally. (The parameter here is equivalent to the output parameter)

 public static void main(String[] args) throws IOException {
        //创建InputStream对象时,使用绝对路径和相对路径都可以,也可以使用File对象
        InputStream inputStream = new FileInputStream("./text.txt");
        while (true){
            byte[] buffer = new byte[1024];
            int len = inputStream.read(buffer);
            System.out.println("len:" +len);
            if(len == -1){
                break;
            }
            //此时读到的结果就放到了 buffer中
            for (int i = 0; i < len; i++) {
                System.out.printf("%x\n",(byte)buffer[i]);
            }
        }
        inputStream.close();
    }

The content of the copy is "Hello"

 Test Results:

Tips: 由于硬盘的大小是有限的 , 我们不可能等到所有数据都读取到硬盘中再去处理 , 通常是一边读取一边处理 , 如果不这么做读满硬盘后 , 下一轮数据会覆盖上一轮数据.


两种读取方式的比较:

第二次读取版本需要一个字节数组作为缓冲区 , 这样做的目的是提高IO操作的效率 , 单次IO操作需要访问硬盘IO设备 , 如果像第一次读取版本 , 频繁的访问IO设备 , 会耗时更多.因此如果能缩短IO的次数就能提高程序整体的效率.

  • 第一个版本的代码是一次读取一个字节 , 循环的次数较高 , read的次数也很高.
  • 第二个版本的代码是一次读取1024个字节 , 循环次数降低了很多 , read的次数也变少了.

3.3 OutputStream概述

修饰符及返回值类型 方法签名 说明
void write(int b) 写入要给字节的数据
void write(byte[] b) 将 b 个字符数组中的数据全部写入 os 中
void

write(byte[] b,int off,

int len)

将 b 这个字符数组中从off 开始的数据写入 os 中,一共写len个
void close() 关闭字节流
void flush() 刷新缓冲区

OutputStream 同样也是一个抽象类 , 要使用还需具体的实现类 , 我们此时只关心文件的读写 , 所以使用FileOutputStream.

代码测试:

public static void main(String[] args) throws IOException {
       OutputStream outputStream = new FileOutputStream("./text.txt");
       outputStream.write(97);
       outputStream.write(98);
       outputStream.write(99);
       outputStream.write(100);
       outputStream.close();
    }

对于 OutputStream 来说 , 默认情况下 , 打开一个文件 , 会先清空文件的内容再进行写操作.


如何区分 InputSream和 OutputSream?

input 和 output的方向是以CPU为中心来判断的.

  • 数据朝着CPU的方向流向 , 就是输入. 所以就把数据从硬盘读到内存这个过程称为 input.
  • 数据远离CPU的方向流向 , 就是输出 , 所以把数据从内存到硬盘 , 这个过程称为 output.


close()操作的作用

close 操作的作用不仅仅是关闭文件释放资源还有刷新缓冲区.

在内核中使用PCB这样的数据结构来表示进程 , 一个线程对应一个PCB , 一个进程可对应一个也可对应多个PCB. PCB中有一个重要的属性 , 文件描述符表  , 相当于一个数组记录了该进程打开了哪些文件(如果一个进程里有多个线程多个PCB , 那么这些PCB共用一个文件描述符表) , 

 每次打开文件操作 , 就会在文件描述符表中申请一个位置 , 把这个信息放进去. 每次关闭文件也会把这个文件描述符表对应的表项给释放掉.如果没有及时释放文件 , Java中虽然有垃圾回收机制 , 但这个垃圾回收机制并不一定及时 , 那么就意味着文件描述符表可能会被占满 , 占满之后再打开文件就会打开失败.

使用 try-with-resource 简化close 写法.

这个写法虽然没有显示的写 close , 实际上只要 try 语句执行完毕 , 就可以自动执行到 close.

try ( OutputStream outputStream = new FileOutputStream("./text.txt");){
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        } 

Tips: 不是随便一个对象放入 try() 中就可以释放 , 得实现Closeable接口的类. 


3.4 利用 Scanner 进行字符读取.

上述例子中我们可以看到对字符类型直接使用 InputStream 进行读取是非常困难的 , 所以使用 Scanner类可以更加简洁的完成该操作.

构造方法 说明
Scanner(InputStream is,String charset) 使用 charset 字符集进行 is 的扫描读取

文件内容:

public static void main(String[] args) throws IOException {
        try(InputStream inputStream = new FileInputStream("./text.txt")) {
            Scanner scanner = new Scanner(inputStream,"UTF-8");
            System.out.println(scanner.next());
        }catch (IOException e){
            e.printStackTrace();
        }
    }


4.小程序练习

示例一:

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

类似于如下操作:

public class ThreadDemo9 {
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
        //让用户输入一个指定搜索目录
        System.out.println("请输入搜索路径: ");
        String basePath = scanner.next();
        //针对用户输入进行简单判定
        File root = new File(basePath);
        if (!root.isDirectory()) {
            // 路径不存在或者只是一个普通文件 , 此时无法进行搜索
            System.out.println("输入路径有误");
            return;
        }

        // 再让用户输入一个要删除的文件名
        System.out.println("请输入要删除的文件名: ");
        // 此处使用 next 而 不要使用 nextLine
        String nameToDelete = scanner.next();

        // 针对指定的路径进行扫描 , 递归操作
        // 先从root目录出发
        // 先判断当前目录里,是否包含咋们要删除的文件,如果是,就删除,否则跳过下一个.
        // 如果这里包含了一些目录 , 再针对目录进行递归.
        scanDir(root, nameToDelete);

    }

    private static void scanDir(File root, String nameToDelete) {
        System.out.println("[sanDir]" + root.getAbsolutePath());
        // 1.列出当前路径下包含的内容
        File[] files = root.listFiles();//相当于看了一下目录中有啥?
        if (files == null) {
            // 空目录
            return;
        }
        // 2.遍历当前列出结果
        for (File file : files) {
            if (file.isDirectory()) {
                // 如果是目录进一步递归
                scanDir(file, nameToDelete);
            } else {
                if (file.getName().contains(nameToDelete)) {
                    System.out.println("确认是否要删除" + file.getAbsolutePath() + " 嘛?");
                    String choice = scanner.next();
                    if (choice.equals("y") || choice.equals("Y")) {
                        file.delete();
                        System.out.println("删除成功!");
                    } else {
                        System.out.println("删除失败!");
                    }
                }
            }
        }
    }
}

示例二

进行普通文件的复制

public static void main(String[] args) throws IOException {
        // 输入两个路径
        // 源 和 目标
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要拷贝哪个文件? ");
        String srcPath = scanner.next();
        System.out.println("请输入要拷贝到哪个地方? ");
        String destPath = scanner.next();

        File srcFile = new File(srcPath);
        if (!srcFile.isFile()) {
            // 如果不是一个文件或该文件不存在
            //此时不做任何操作
            System.out.println("您当前输入的源路径有误");
            return;
        }
        File destFile = new File(destPath);
        if (destFile.isFile()) {
            //如果已存在也不能进行拷贝操作
            System.out.println("您输入的目录路径有误");
            return;
        }
        //进行拷贝操作
        try (InputStream inputStream = new FileInputStream(srcFile);
             OutputStream outputstream = new FileOutputStream(destFile)) {
            // 进行读文件操作
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    break;
                }
                outputstream.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Tips:OutputStream 在写文件时如果文件不存在 , 就会自动创建 , 但 InputStream 不行会抛出异常. 

示例三:

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

public class ThreadDemo11 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的目录: ");
        String dir = scanner.next();
        File rootDir = new File(dir);
        if(!rootDir.isDirectory()){
            System.out.println("输入目录错误 退出!");
            return;
        }
        System.out.println("请输入要找出的文件名中的字符");
        String token = scanner.next();
        List<File> ret = new ArrayList<>();
        //因为文件是树形结构 , 所以我们使用深度优先遍历
        scanDirWithContent(rootDir,token,ret);
        System.out.println("共找到"+ret.size()+"个符合条件的文件,它们分别是: ");
        for (File file:ret){
            System.out.println(file.getCanonicalFile());
        }
    }

    private static void scanDirWithContent(File rootDir, String token, List<File> ret) throws IOException {
        File[] files = rootDir.listFiles();
        if (files.length == 0||files == null){
            return;
        }
        for (File file:files){
            if(file.isDirectory()){
                scanDirWithContent(file,token,ret);
            }else{
                if(isContentContain(file,token)){
                    ret.add(file.getAbsoluteFile());
                }
            }
        }
    }

    private static boolean isContentContain(File file, String token) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (InputStream is = new FileInputStream(file);
             Scanner scanner = new Scanner(is)){
             while (scanner.hasNextLine()){
                 sb.append(scanner.nextLine());
//                 sb.append("/r/n");
             }
        }
        return sb.indexOf(token) !=-1;
    }
}

 


Guess you like

Origin blog.csdn.net/liu_xuixui/article/details/128676310