如何逐行读取文件,消除重复项,然后写回同一个文件?

How can I read a file line-by-line, eliminate duplicates, then write back to the same file?

我想读取一个文件,消除所有重复项并将其余部分写回到文件中 - 就像重复清理器一样。 Vec,因为普通数组具有固定大小,但我的 .txt 是灵活的(我这样做对吗?)。

读取,Vec 中的行 + 删除重复项: 缺少写回文件。

use std::io;

fn main() {
    let path = Path::new("test.txt");
    let mut file = io::BufferedReader::new(io::File::open(&path, R));

    let mut lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
    // dedup() deletes all duplicates if sort() before
    lines.sort();
    lines.dedup();

    for e in lines.iter() {
        print!("{}", e.as_slice());
    }
}

读取+写入文件(未经测试但我猜应该可以)。 缺少 Vec 行,因为它看起来没有 BufferedReader 就无法工作(或者我做错了其他事情,也是一个很好的机会)。

use std::io;

fn main() {
    let path = Path::new("test.txt");
    let mut file = match io::File::open_mode(&path, io::Open, io::ReadWrite) {
        Ok(f) => f,
        Err(e) => panic!("file error: {}", e),
    };  
    let mut lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
    lines.sort();
    // dedup() deletes all duplicates if sort() before
    lines.dedup();

    for e in lines.iter() {
        file.write("{}", e);
    }
} 

那么 .... 我如何将这 2 个放在一起? :)

最终,您将 运行 遇到一个问题:您正在尝试写入与正在读取的文件相同的文件。在 this 的情况下,它是安全的,因为你要读取整个文件,所以之后你就不需要它了。但是,如果您确实 尝试写入文件,您会发现打开文件进行读取不允许写入!这是执行此操作的代码:

use std::{
    fs::File,
    io::{BufRead, BufReader, Write},
};

fn main() {
    let mut file = File::open("test.txt").expect("file error");
    let reader = BufReader::new(&mut file);

    let mut lines: Vec<_> = reader
        .lines()
        .map(|l| l.expect("Couldn't read a line"))
        .collect();

    lines.sort();
    lines.dedup();

    for line in lines {
        file.write_all(line.as_bytes())
            .expect("Couldn't write to file");
    }
}

这是输出:

% cat test.txt
    a
    a
    b
    a
                                                                                                                                                                                                                                     % cargo run
thread 'main' panicked at 'Couldn't write to file: Os { code: 9, kind: Other, message: "Bad file descriptor" }', src/main.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

您可以打开文件进行读写:

use std::{
    fs::OpenOptions,
    io::{BufRead, BufReader, Write},
};

fn main() {
    let mut file = OpenOptions::new()
        .read(true)
        .write(true)
        .open("test.txt")
        .expect("file error");

    // Remaining code unchanged
}

但是你会看到 (a) 输出是 appended 和 (b) 所有的换行符都丢失了,因为 BufRead 没有不包括他们。

我们可以将文件指针重置回开头,但是您可能会在末尾留下尾随的东西(去重可能写入的字节数少于读取的字节数) .重新打开文件进行写入会更容易,这将 t运行cate 文件。另外,让我们使用一个集合数据结构来为我们做去重!

use std::{
    collections::BTreeSet,
    fs::File,
    io::{BufRead, BufReader, Write},
};

fn main() {
    let file = File::open("test.txt").expect("file error");
    let reader = BufReader::new(file);

    let lines: BTreeSet<_> = reader
        .lines()
        .map(|l| l.expect("Couldn't read a line"))
        .collect();

    let mut file = File::create("test.txt").expect("file error");

    for line in lines {
        file.write_all(line.as_bytes())
            .expect("Couldn't write to file");

        file.write_all(b"\n").expect("Couldn't write to file");
    }
}

并且输出:

% cat test.txt
a
a
b
a
a
b
a
b

% cargo run
% cat test.txt
a
b

效率较低但较短的解决方案是将整个文件作为一个字符串读取并使用 str::lines:

use std::{
    collections::BTreeSet,
    fs::{self, File},
    io::Write,
};

fn main() {
    let contents = fs::read_to_string("test.txt").expect("can't read");
    let lines: BTreeSet<_> = contents.lines().collect();

    let mut file = File::open("test.txt").expect("can't create");
    for line in lines {
        writeln!(file, "{}", line).expect("can't write");
    }
}

另请参阅: