【JavaEE】IO operation

Insert image description here

Preface

We learned basic file operations earlier, but those are only operations based on the file itself. If you want to operate the contents of the file, you need to use IO - InputStream OutputStream operation. So in this article, I will share with you knowledge about Java IO operations.

What is IO

IO is InputStream and OutputStream. We all know that input and output mean input and output, but what does the stream behind mean? Stream means flow. Since it is called "flow", it must have similar characteristics to water flow.

Insert image description here

Insert image description here
Now that we know what a "stream" is, how do we distinguish input and output? In other words, under what circumstances is it called output, and under what circumstances is it called output?

To determine whether the operation is input or output, a reference object is required. Assume that data is transferred from the CPU to the file. For the CPU, this operation is output, while for the file, this operation is input.

For file operations, streams are mainly divided into two categories:

  1. Byte stream - InputStream OutputStream
  2. Character stream - Reader Writer

Although there are two different streams, there is essentially only one byte stream, but the character stream encapsulates the bytes to a certain extent. The character stream encodes n bytes as one character.

Reader read operation

1. Create Reader class

Using Reader in Java requires java.io.Readera class, but when we want to create a Reader object, we will find that the Reader class is an abstract class and cannot be created directly.

Insert image description here
Insert image description here
Therefore, we can only create another class that implements the Reader class.

Reader reader = new FileReader("d:test1/.txt");

The process of creating a Reader is the process of opening a file. If the file does not exist, the creation will fail.
Insert image description here

2. Understand the different read methods in the Reader class

After creating the Reader object, you can use the methods in the Reader. Since we want to read the content of the inquiry, let's take a look at how to use the methods in the Reader.

Insert image description here

Modifiers and return value types method signature illustrate
int read() Read data one character at a time and return -1 to indicate that it has been completely read.
int read(char[] cbuf) Read several characters at a time until the cbuf array is filled
int rend(char[] cbuf, int off, int len) Read several characters at a time, and start the read data from the off position of cbuf, filling the length of len
void close() Close character stream

When you see the different read methods above, I wonder if you have noticed: read reads characters, why is the return type int instead of char?

Insert image description here
In Java, the reason why the read() method of the Reader class returns the int type is because the method needs to be able to represent all possible characters and special values.

In Java, the char type is used to represent characters. It is a 16-bit Unicode character that can represent 65536 different characters. However, the read() method needs to be able to return a special value to indicate the end of the stream (EOF, End of File). Therefore, if you use the char type as the return type, you cannot represent this special value.

To solve this problem, the read() method uses int type as the return type. The int type is a 32-bit integer type that can represent a larger range. In the Reader class, the read() method returns an int type value, where 0 to 65535 represents the actual characters, and -1 represents the end of the stream (EOF).

After knowing why the return type of the read() method is int instead of char, there is another question: why is the returned int range a length of two bytes to 65535 instead of a length of three bytes? Aren't the lengths of characters in different encodings different?

In fact, within the Java standard library, a lot of operations are done on character encoding.

  • If only char is used, the character set at this time is fixed to Unicode, and the characters are two bytes.
  • If String is used, the Unicode encoding of each character will be converted to utf8, and the character will be three bytes.
char[] c = {
    
    'a','b','c'};
String s = new String(c);

Each character in c is originally Unicode encoded and occupies two bytes, but when the character array is converted into a string, the encoding of each character is converted from Unicode to utf8 encoding.

char cc = s.charAt(1);

When using this operation, the characters obtained in cc will be converted from utf8 encoding to Unicode encoding.

3. Use different read methods in the Reader class

read() reads one character at a time

The return value of this method is to convert the read characters into int type.

public class Demo1 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        Reader reader = new FileReader("d:/test.txt");
        while (true) {
    
    
            //这个read方法中没有传入参数,一次只读取一个字符
            int ret = reader.read();
            if (ret == -1) break;
            char ch = (char)ret;
            System.out.println(ch);
        }
        reader.close();
    }
}

Insert image description here

read(char[] cbuf) reads multiple characters at once

The return value of this method is how many characters were read at one time

public class Demo2 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        try (Reader reader = new FileReader("d:/test.txt")) {
    
    
            while (true) {
    
    
                char[] cbuf = new char[3];
                //这个read方法的返回值是读取到的字符的个数
                int ret = reader.read(cbuf);
                if (ret == -1) break;
                for (int i = 0; i < ret; i++) {
    
    
                    System.out.println(cbuf[i]);
                }
            }
        }
    }
}

Insert image description here
The third read method is similar to this read method, except that the third method has an offset parameter. It is usually used in scenarios where multiple files are read, so we will not introduce it in detail here.

3. Close file operation

After using the file, we need to reader.close()close the file in time to prevent resource leakage. But in this case, if an exception occurs before executing this code, causing the program to terminate, then the file will not be closed, so a method needs to be used so that the entire closing operation must be executed anyway. So what method can be used to ensure that the entire shutdown operation can be executed no matter what?

try {
    
    
	//...
} finally {
    
    
	reader.close();
}

Using try - finally can guarantee that the code in finally will be executed no matter what.

But there is another way to achieve this function and be elegant. What is the right way?

try (Reader reader = new FileReader("d:/test.txt")) {
    
    
	//....
}

This kind of statement is called try-with-resourcesstatement. The characteristic of this statement is that when the object in the brackets implements closerable, after the code in {} is executed, the close()method in the object will be automatically called.

Insert image description here

public class Demo1 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        try (Reader reader = new FileReader("d:/test.txt")) {
    
    
            while (true) {
    
    
                //这个read方法中没有传入参数,一次只读取一个字符
                int ret = reader.read();
                if (ret == -1) break;
                char ch = (char)ret;
                System.out.println(ch);
            }
        }
    }
}

Why does not closing the file in the end cause resource leakage?

When we studied processes and threads earlier, we all knew that whenever a process is created, there will be a corresponding PCB, and the PCB contains a variety of attributes, such as pid, memory pointer, file descriptor table, etc., when in a When you open a file in a process, you need to allocate an element in the file descriptor table, and this element exists in an array. The length of the array is limited. If you open a file and never If you close this file, there will be more and more elements in this array until a problem occurs.

So, be sure to remember to close the file! Close the file! Close the file!

Writer writing operation

Writer writes data into a file in the form of characters. In Java, using the Writer method requires the help of java.io.Writerthe class, and this class is also an abstract class and cannot directly create objects, only its subclasses.

1. Create the Writer class

public class Demo3 {
    
    
    public static void main(String[] args) {
    
    
        try (Writer writer = new FileWriter("d:/test1.txt")) {
    
    
            
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } ;
    }
}

When creating a Writer object, if the file does not exist, no error will be reported, but the file will be automatically created.

2. Understand the different write methods in the Writer class

Insert image description here
The write method in the Writer class is relatively easy to understand, but the parameters are different. We can pass in the integer converted from the ASCII code corresponding to the character as a parameter, or we can pass in a string or character array, and when passing in a string and a character When using an array, you can also specify the offset and length.

3. Use the write method in the Writer class

public class Demo3 {
    
    
    public static void main(String[] args) {
    
    
        try (Writer writer = new FileWriter("d:/test1.txt")) {
    
    
            writer.write("要想成为Java高级工程师,就需要不断地敲代码");
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } ;
    }
}

Here, before executing the code, there is content in the file, and then after executing the code, we look at the results.
Insert image description here
Insert image description here
It can be found that the previous content of this file has been overwritten, that is to say: the write operation will overwrite the previously written content by default.

So, what should I do if I don’t want to overwrite but append to write?

If we want to implement append writing instead of overwriting when writing, we can pass one more parameter when creating the Writer object.

Insert image description here

The parameter true passed in here means append is true, which means append writing.

public class Demo3 {
    
    
    public static void main(String[] args) {
    
    
        try (Writer writer = new FileWriter("d:/test1.txt",true)) {
    
    
            writer.write("要想成为Java高级工程师,就需要不断地敲代码");
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } ;
    }
}

Insert image description here

OutputStream byte stream output

After knowing how to use Writer to write in the form of a character stream, the way OutputStream writes in the form of a byte stream is also very simple.

1. Create an Outputstream object

public class Demo4 {
    
    
    public static void main(String[] args) {
    
    
        try (OutputStream outputStream = new FileOutputStream("d:/test2.txtx")) {
    
    
            
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } ;
    }
}

Understand the write method of OutputStream

Insert image description here
When the parameter is of int type, int corresponds to the corresponding character in the ASCII code. The other two cashier methods are the same as above, so I won't explain them too much here.

Use the write method of OutputStream

public class Test {
    
    
    public static void main(String[] args) {
    
    
        try (OutputStream outputStream = new FileOutputStream("d:test.txt")) {
    
    
            outputStream.write('a');
            outputStream.write('b');
            outputStream.write('c');
            outputStream.write('d');
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Here when we write data in the form of bytes, problems may occur.

Insert image description here

test.txtWhy is there no data in the file after running the program ?

In fact, during the process of writing data, the data in the memory is not written directly into the file, but a special memory space - the buffer is also passed through during the process . When data is transferred into the buffer, the buffer does not immediately transfer the data to the file. Instead, when the data in the buffer reaches a certain size, the data is transferred to the file together. So why is there this step?

The speed of data transfer from memory to buffer is very fast, but the speed of data transfer from buffer to file is very slow. If the data is transferred to the file after receiving the data in the buffer, a lot of data needs to be transferred. This time, it is similar to: I am a water deliveryman, responsible for delivering water to residents in a community. At this time, I received a request, and I immediately brought a bucket of water to deliver water. However, during the process of delivering water, I collected a large amount of water. When there is a request for water delivery, for the second request, I can only wait for the first bucket of water to be delivered and come back to refill a bucket of water before I can continue to deliver water to the second person. This kind of efficiency is very slow and wastes manpower, so I can wait for a while at the company. Even if there is a request for water delivery, I will not go immediately. Instead, after the request reaches a certain amount, I will load the water into the delivery system. Delivering them together on the truck saves time and manpower. This is the role of the buffer zone.

Now that we know that the buffer will pass through when writing data, why is the data not finally written when writing data? This is because there is too little data in the buffer and has not yet reached the size of the data to be transferred to the file, so the buffer will not transfer the received data to the file. At this time, because the program execution ends, when the program executes After the end, the data in the buffer will be released, resulting in the data not being written to the file.

So in order to solve this problem, we need to refresh the buffer after we finish writing the data.outputStream.flush

public class Test {
    
    
    public static void main(String[] args) {
    
    
        try (OutputStream outputStream = new FileOutputStream("d:test.txt")) {
    
    
            outputStream.write('a');
            outputStream.write('b');
            outputStream.write('c');
            outputStream.write('d');
            outputStream.flush();
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Insert image description here

Writing character data to a file refers to character data, but the required parameter type is bytes, so the characters need to be converted.

public class Demo4 {
    
    
    public static void main(String[] args) {
    
    
        try (OutputStream outputStream = new FileOutputStream("d:/test2.txt")) {
    
    
            String s = "我爱中国";
            outputStream.write(s.getBytes());
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        } ;
    }
}

Insert image description here

Use PrintWriter for output

As mentioned above, we have actually completed the output work, but it is always inconvenient. We will next process the OutputStream and use the
PrintWriter class to complete the output, because the PrintWriter class provides the familiar print/println/printf methods.

public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        try (OutputStream outputStream = new FileOutputStream("d:/test.txt")) {
    
    
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.print("你好世界");
            printWriter.println("我爱Java");
            printWriter.println("我要成为Java高级工程师");
            printWriter.flush();
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Insert image description here

InputStream byte stream input

InputStream is input in the form of byte stream. In Java, using InputStream requires the help java.io.InputStreamof the InputStream class in .

1. Create the InputStream class

public class Test {
    
    
    public static void main(String[] args) {
    
    
        try (InputStream inputStream = new FileInputStream("test.txt")) {
    
    
            
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

2. Understand the read method in the InputStream class

Insert image description here
The read method in InputStream is similar to the read method in Reader class, except that the parameters passed in by the read method in InoutStream class are of byte type.

3. Use the read method in the InputStream class

public class Test {
    
    
    public static void main(String[] args) {
    
    
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
    
    
            while (true) {
    
    
                int ret = inputStream.read();
                if (ret == -1) break;
                System.out.println(ret);
            }
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Insert image description here
Insert image description here
The output is the binary form of all characters in the file.

public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
    
    
            while (true) {
    
    
                byte[] b = new byte[16];
                int n = inputStream.read(b);
                if (n == -1) break;
                for (int i = 0; i < n; i++) {
    
    
                    System.out.println(b[i]);
                }
            }
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Use Scanner to read characters

In the above example, we saw that it is very troublesome and difficult to directly use InputStream to read character types. Therefore, we
use a class that we are familiar with before to complete the work, which is the Scanner class.

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        try (InputStream inputStream = new FileInputStream("d:/test.txt")) {
    
    
            Scanner scanner = new Scanner(inputStream);
            String s = scanner.next();
            System.out.println(s);
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

When we usually use Scanner, the parameters passed in are often the System.instandard input, that is, keyboard input, and if a file is passed in, it is input from the file.

Insert image description here

Use file operations and IO to implement a function to delete specified files

After learning how to use file operations and IO operations, we will use this knowledge to implement a function to delete specified files. What are the requirements for this function? It means that the user needs to be prompted to enter the file in which directory to delete, and then the user is prompted to enter the keyword of the file that needs to be deleted.

According to the requirements, we know that this question is actually to traverse all the files under the directory entered by the user. Since the underlying data structure of the file is a number, then we need to use the idea of ​​recursion. When the recursion reaches the folder, continue the recursion. When the recursion When the file is reached, it is judged whether the file contains the specified keyword entered by the user.

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("请输入要扫描的文件路径");
        Scanner scanner = new Scanner(System.in);
        String path = scanner.next();
        File rootPath = new File(path);
        //判断用户输入的扫描文件是否合法
        if (!rootPath.isDirectory()) {
    
    
            System.out.println("您输入的扫描文件的路径有误");
            return;
        }
        System.out.println("请输入要删除的文件的关键词");
        String word = scanner.next();

        scanDir(rootPath,word);
    }

    private static void scanDir(File rootPath, String word) {
    
    
        File[] files = rootPath.listFiles();
        if(files == null) return;
        for (File f : files) {
    
    
            //添加一个日志,知道遍历到哪个文件了
            System.out.println("当前扫描文件" + f.getAbsolutePath());
            if (f.isFile()) {
    
    
                chechDelete(f,word);
            }else {
    
    
                scanDir(f,word);
            }
        }
    }

    private static void chechDelete(File f, String word) {
    
    
        if (!f.getName().contains(word)) return;
        System.out.println("当前文件为" + f.getAbsolutePath() + ", 请确认是否要删除(Y/N)");
        Scanner scanner = new Scanner(System.in);
        String choice = scanner.next();
        if (choice.equals("Y")) {
    
    
            f.delete();
            System.out.println("删除完毕");
        }else {
    
    
            System.out.println("取消删除");
        }
    }
}

Insert image description here

Copy specified file

public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        //复制文件到指定路径
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要复制的文件:");
        String srcPath = scanner.next();
        File srcFile = new File(srcPath);
        if (!srcFile.exists()) {
    
    
            System.out.println("您输入的文件不存在,请确认文件路径的正确性");
            return;
        }
        if (!srcFile.isFile()) {
    
    
            System.out.println("您输入的文件不是普通文件,请确认文件路径的正确性");
            return;
        }

        System.out.println("请输入要复制到的目标路径");
        String desPath = scanner.next();
        File desFile = new File(desPath);
        if (desFile.isDirectory()) {
    
    
            System.out.println("您输入的路径是一个目录,不是文件,请确认文件路径是否正确");
            return;
        }

        if (desFile.isFile()) {
    
    
            if (desFile.exists()) {
    
    
                System.out.println("您要复制到的文件已存在,是否需要覆盖:Y/N");
                String choice = scanner.next();
                if (!choice.equals("Y")) return;
            }
        }

        try (InputStream inputStream = new FileInputStream(srcFile)) {
    
    
            try (OutputStream outputStream = new FileOutputStream(desFile)) {
    
    
                byte[] b = new byte[1024];
                while (true) {
    
    
                    int n = inputStream.read(b);
                    if (n == -1) break;
                    outputStream.write(b,0,n);
                }
            }
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Search the specified directory for files whose file names or content contain keywords

public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要扫描的目录");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);
        if(!rootDir.isDirectory()) {
    
    
            System.out.println("您输入的路径不是正确的目录路径,请检查你输入的路径");
            return;
        }

        System.out.println("请输入你要查询的文件名或者文件内容中包含的关键词");
        String word = scanner.next();
        List<File> list = new ArrayList<>();
        scanDir(rootDir,word,list);
        for (File s : list) {
    
    
            System.out.println(s.getAbsoluteFile());
        }
    }

    private static void scanDir(File rootDir, String word, List<File> list) {
    
    
        File[] files = rootDir.listFiles();
        if (files == null) return;
        for (File f : files) {
    
    
            if (f.isDirectory()) {
    
    
                scanDir(f,word,list);
            }else {
    
    
                if (isContainsContent(f,word)) {
    
    
                    list.add(f);
                }
            }
        }
    }

    private static boolean isContainsContent(File f, String word) {
    
    
        if (f.getName().contains(word)) return true;
        StringBuilder stringBuilder = new StringBuilder();
        try (InputStream inputStream = new FileInputStream(f.getAbsoluteFile())) {
    
    
            try (Scanner scanner = new Scanner(f.getAbsoluteFile())) {
    
    
                while (scanner.hasNextLine()) {
    
    
                    stringBuilder.append(scanner.nextLine());
                    if (stringBuilder.indexOf(word) != -1) return true;
                }

                return false;
            }
        } catch (FileNotFoundException e) {
    
    
            throw new RuntimeException(e);
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

Guess you like

Origin blog.csdn.net/m0_73888323/article/details/133561257