Rust File Io
This chapter introduces I/O operations in the Rust language.
### Receiving Command-Line Arguments
Command-line programs are the most basic form of computer programs. Almost all operating systems support command-line programs and base the execution of graphical programs on command-line mechanisms.
Command-line programs must be able to receive parameters from the command-line environment. These parameters are usually separated by spaces after the command in a command line.
In many languages (like Java and C/C++), environment parameters are passed to the program as arguments to the main function (often a string array). However, in Rust, the main function is a parameterless function. Developers need to obtain environment parameters through the `std::env` module, and the process is very simple:
## Example
fn main(){
let args = std::env::args();
println!("{:?}", args);
}
Now run the program directly:
Args { inner: ["D:rustgreetingtargetdebuggreeting.exe"] }
Perhaps your result is longer than this, which is normal. In this result, the `Args` struct has an `inner` array containing only a single string, representing the location of the currently running program.
But this data structure is hard to understand. No problem, we can simply iterate over it:
## Example
fn main(){
let args = std::env::args();
for arg in args {
println!("{}", arg);
}
}
Running result:
D:rustgreetingtargetdebuggreeting.exe
Generally, arguments are meant to be iterated over, aren't they?
Now let's open the long-neglected `launch.json`, find `"args": []`, where we can set runtime arguments. Let's write it as `"args": ["first", "second"]`, then save and run the program again. The running result:
D:rustgreetingtargetdebuggreeting.exe first second
As a true command-line program, we've never really used it. As a language tutorial, we won't describe here how to run a Rust program from the command line. But if you are a well-trained developer, you should be able to find the location of the executable file. You can try entering the directory and using command-line commands to test the program's reception of command-line environment arguments.
### Command-Line Input
Earlier chapters detailed how to use command-line output. This is due to the needs of language learning; without output, it's impossible to debug programs. However, obtaining input information from the command line is still quite important for a command-line program.
In Rust, the `std::io` module provides functionality related to standard input (which can be considered as command-line input):
## Example
use std::io::stdin;
fn main(){
let mut str_buf = String::new();
stdin().read_line(&mut str_buf)
.expect("Failed to read line.");
println!("Your input line is n{}", str_buf);
}
Making the VSCode environment support command-line input is a very tedious task, involving cross-platform issues and non-debuggable issues. So we'll run the program directly in the VSCode terminal. Run in the command line:
D:rustgreeting> cd ./target/debug D:rustgreetingtargetdebug> ./greeting.exe Your input line is
`std::io::Stdio` contains the `read_line` method, which can read a line of string into a buffer. The return value is a `Result` enum, used to pass errors that occur during reading. Therefore, the `expect` or `unwrap` functions are commonly used to handle errors.
**Note**: Currently, the Rust standard library does not provide a method to directly read numbers or formatted data from the command line. We can read a line of string and use string recognition functions to process the data.
### File Reading
We create a file `text.txt` in the `D:` directory of our computer, with the following content:
This is a text file.
Here is a program that reads the contents of a text file into a string:
## Example
use std::fs;
fn main(){
let text = fs::read_to_string("D:text.txt").unwrap();
println!("{}", text);
}
Running result:
This is a text file.
In Rust, reading an entire file that fits into memory is extremely simple. The `read_to_string` method in the `std::fs` module can easily complete the reading of text files.
However, if the file to be read is a binary file, we can use the `std::fs::read` function to read a collection of `u8` types:
## Example
use std::fs;
fn main(){
let content = fs::read("D:text.txt").unwrap();
println!("{:?}", content);
}
Running result:
[84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 120, 116, 32, 102, 105, 108, 101, 46]
The above two methods are one-time reads, which are very suitable for web application development. However, for some low-level programs, the traditional stream-based reading method is still irreplaceable, because in more cases, the file size may far exceed the memory capacity.
File stream reading method in Rust:
## Example
use std::io::prelude::*;
use std::fs;
fn main(){
let mut buffer =[0u8;5];
let mut file = fs::File::open("D:text.txt").unwrap();
file.read(&mut buffer).unwrap();
println!("{:?}", buffer);
file.read(&mut buffer).unwrap();
println!("{:?}", buffer);
}
Running result:
[84, 104, 105, 115, 32] [105, 115, 32, 97, 32]
The `File` class in the `std::fs` module is a class that describes a file. It can be used to open a file. After opening the file, we can use the `read` method of `File` to read the next few bytes of the file into a buffer (the buffer is a `u8` array) in a stream. The number of bytes read is equal to the length of the buffer.
Note: VSCode currently does not have the function to automatically add standard library references. Therefore, sometimes errors like "function or method does not exist" may be due to standard library reference issues. We can check the standard library documentation (which appears when you hover over it) to manually add the standard library.
The `open` method of `std::fs::File` opens a file in "read-only" mode and does not have a corresponding `close` method, because the Rust compiler can automatically close the file when it is no longer in use.
### File Writing
File writing is divided into one-time writing and stream writing. Stream writing requires opening a file, and there are two opening modes: "create" and "append".
One-time writing:
## Example
use std::fs;
fn main(){
fs::write("D:text.txt","FROM RUST PROGRAM")
.unwrap();
}
This is as simple and convenient as one-time reading. After executing the program, the content of the `D:text.txt` file will be overwritten with `FROM RUST PROGRAM`. Therefore, use one-time writing with caution! Because it will directly delete the file content (no matter how large the file is). If the file does not exist, it will be created.
If you want to write file content using a stream, you can use the `create` method of `std::fs::File`:
## Example
use std::io::prelude::*;
use std::fs::File;
fn main(){
let mut file = File::create("D:text.txt").unwrap();
file.write(b"FROM RUST PROGRAM").unwrap();
}
This program is equivalent to the previous one.
**Note**: The opened file must be stored in a mutable variable to use the methods of `File`!
The `File` class does not have a static `append` method, but we can use `OpenOptions` to open a file with specific methods:
## Example
use std::io::prelude::*;
use std::fs::OpenOptions;
fn main()-> std::io::Result{
let mut file = OpenOptions::new()
.append(true).open("D:text.txt")?;
file.write(b" APPEND WORD")?;
Ok(())
}
After running, the content of the `D:text.txt` file will become:
FROM RUST PROGRAM APPEND WORD
`OpenOptions` is a flexible method for opening files. It can set opening permissions. Besides the `append` permission, there are also `read` and `write` permissions. If we want to open a file with read-write permissions, we can write it like this:
## Example
use std::io::prelude::*;
use std::fs::OpenOptions;
fn main()-> std::io::Result{
let mut file = OpenOptions::new()
.read(true).write(true).open("D:text.txt")?;
file.write(b"COVER")?;
Ok(())
}
After running, the content of the `D:text.txt` file will become:
COVERRUST PROGRAM APPEND WORD
YouTip