read_lines

Một cách tiếp cận ngây thơ.

Đây có thể là một nỗ lực hợp lý đầu tiên cho việc đọc các dòng từ một tệp tin đối với một người mới bắt đầu.

#![allow(unused)]
fn main() {
use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    let mut result = Vec::new();

    for line in read_to_string(filename).unwrap().lines() {
        result.push(line.to_string())
    }

    result
}
}

Vì phương thức lines() trả về một bộ lặp qua các dòng trong tập tin, chúng ta cũng có thể thực hiện một phép ánh xạ trực tiếp và thu thập các kết quả, tạo ra một biểu thức ngắn gọn và trôi chảy hơn

#![allow(unused)]
fn main() {
use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    read_to_string(filename) 
        .unwrap()  // panic khi có lỗi đọc tệp có thể xảy ra.
        .lines()  // chia chuỗi thành một bộ lặp của các dòng
        .map(String::from)  // chuyển mỗi phần của chuỗi thành một chuỗi mới
        .collect()  // tập hợp chúng lại thành một vector
}
}

Lưu ý rằng trong cả hai ví dụ trên, chúng ta phải chuyển đổi tham chiếu &str được trả về từ lines() thành kiểu được sở hữu String, sử dụng .to_string()String::from tương ứng.

Một cách tiếp cận hiệu quả hơn

Ở đây chúng ta truyền sở hữu của File đã mở vào một struct BufReader. BufReader sử dụng bộ đệm nội bộ để giảm thiểu các phân bổ trung gian. Chúng ta cũng cập nhật read_lines để trả về một bộ lặp thay vì phân bổ các đối tượng String mới trong bộ nhớ cho mỗi dòng.

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // Tệp tin hosts.txt phải tồn tại trong đường dẫn hiện tại.
    if let Ok(lines) = read_lines("./hosts.txt") {
        // Sử dụng iterator, trả về một Chuỗi (Tùy chọn)
        for line in lines {
            if let Ok(ip) = line {
                println!("{}", ip);
            }
        }
    }
}

//  Kết quả được bọc trong một Result để cho phép phù hợp với các lỗi
//  Trả về một Iterator đến Reader của các dòng trong tập tin.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

Chạy chương trình này đơn giản chỉ in các dòng một cách riêng lẻ.

$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1

(Lưu ý rằng vì File::open mong đợi một generic AsRef như đối số, chúng ta xác định phương thức generic read_lines() với cùng một ràng buộc generic, sử dụng từ khóa where.) Quá trình này hiệu quả hơn việc tạo String trong bộ nhớ với tất cả nội dung của tập tin. Điều này đặc biệt có thể gây ra vấn đề hiệu suất khi làm việc với các tập tin lớn hơn.