如何在 Rust 中解码和读取 zstd 文件?

How to decode and read a zstd file in Rust?

提问人:TrkDgnr 提问时间:10/17/2023 更新时间:10/17/2023 访问量:117

问:

我正在寻找一些关于如何解码和读取 zstd 文件的建议,我感到有点迷茫,因为这是我开始学习 Rust 以来的第一个大项目。

我在这个项目中使用 Rust,因为它是为了实习,而且数据导出/压缩工具很久以前就用 Rust 编写了,所以我想我可以从中获得一些灵感。我正在从头开始学习 Rust,所以我对文件 I/O 进程的结构和功能不是很熟悉。我有一个代码片段目前不起作用,所以我有一些问题:

use std::fs::File;
use std::io::{self, BufReader};
use zstd::stream::read::Decoder;

fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, { 
    if let Ok(file) = File::open(filename) { 
        if let Ok(buf_reader) = BufReader::new(file) { 
            if let Ok(decoder) = Decoder::new(buf_reader) { 
                return Ok(io::BufReader::new(decoder).lines()); } } } }

if let Ok(lines) = read_lines(filename) {

    for line in lines {
        if let Ok(ip) = line {
            println!("{}", ip)

        }
    }
}

既然是压缩文件,我是否应该先将其整体解码,然后开始逐行读取?我知道解压缩的文件是jsonl格式,所以每一行都是一个单独的json文件。如果文件大小太大而无法一口气读完,我该怎么办?

另外,如果您有您正在使用的 zstd 以外的其他软件包,请与我分享。我将不胜感激所有的帮助。

文件 io zstd

评论

1赞 kmdreko 10/17/2023
“我应该先把它作为一个整体解码,然后开始一行一行地阅读吗?”- 这真的是你的问题吗?你的下一个问题绝对意味着不是,特别是当你展示的内容有一个(至少是)一个完全流式的解决方案(没有预先阅读所有内容)时。虽然你的类型和语法有很大不同,但这就是问题的真正意义吗?
0赞 TrkDgnr 10/17/2023
感谢您的评论,问题是我无法真正了解它应该如何工作。我不认为它应该将文件作为一个整体解压缩,因为我被告知文件可能会变得非常大。但我也认为压缩不会逐行工作。另一件事是,我找不到很多用 Rust 编写的示例,其中大型 zstd 文件被解压缩,所以我想在这里问一下,并获得一些关于它应该如何工作的想法。
1赞 cdhowie 10/17/2023
@TrkDgnr 您不必担心解码器是否真的可以解压缩离散的整行,因为这无关紧要。 将包装另一个读取器并提供此功能。例如,它不会在内存中缓冲整个文件,仅足以提前读取一点并查看一行的结束位置。因此,它的读数刚好足以获得换行符,甚至可能比这更多一点。BufReader
0赞 TrkDgnr 10/17/2023
谢谢你的解释。我知道这段代码在语法上不正确,但你认为它在逻辑上是正确的吗?首先打开文件,然后放入缓冲区读取器,然后放入 zstd 包的解码器中,解析成行。如果是这样,我将尝试保留逻辑,但尝试使其与 Rust 语法一起使用。

答:

2赞 kmdreko 10/17/2023 #1

你以正确的方式去做,使用 in a 将允许您从压缩文件中读取行,而无需预先加载整个文件。用于读取行的外部将从解码器读取块,直到到达换行符,从解码器读取将从文件中解码块。DecoderBufReaderBufReader

您只是没有正确的结构和返回类型。这是我会做的:

use std::fs::File;
use std::io::{BufRead, BufReader, Error as IoError, Lines};
use std::path::Path;

use zstd::stream::read::Decoder;

fn read_lines<P>(filename: P) -> Result<Lines<BufReader<Decoder<'static, BufReader<File>>>>, IoError>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    let decoder = Decoder::new(file)?;
    Ok(BufReader::new(decoder).lines())
}

再解释一下:

  • 由于如果遇到问题,两者都会返回,因此我们可以使用它来尽早返回错误并避免嵌套 S。File::openDecoder::newstd::io::Error?if-let
  • Decoder::new接受一个读取器类型并创建一个(即它为它本身创建一个),所以我们不必做那部分。Decoder<'_, BufReader<_>>BufReaderFile
  • 返回类型将所有图层嵌套在一起,但如果您愿意,可以将其替换为本实例以保持简洁。Result<Lines<impl BufRead>, IoError>

评论

0赞 TrkDgnr 10/17/2023
感谢您的回放和代码示例!我尝试在主函数中使用简单的 for 循环运行您的函数并打印每一行以查看它是否正在解码,我收到错误:Err(Custom { kind: Other, error: “Dictionary mismatch” })。为什么你认为它会导致字典不匹配?
1赞 kmdreko 10/17/2023
@TrkDgnr我不熟悉这个错误,但也许这个这个会有所帮助。
0赞 TrkDgnr 10/17/2023
哦,我明白了,谢谢你的链接。我认为我拥有的文件也是用外部字典压缩的,但我目前没有。我将尝试检查另一个文件。