Rust 示例:流式解压 xz 压缩的 JSONL 文件并打印单个属性

此示例程序解析 xz 压缩的 JSONL 文件,并在存在时打印每一行的 timestamp 属性。无效或空行将被忽略。

main.rs

stream_decompress.rs
//! 本程序流式读取并解压用 xz 压缩的 JSONL 文件,
//! 将每一行解析为 JSON,并在存在时打印 "timestamp" 字段。
//! 无效或空行将被忽略。

use std::fs::File; // 用于打开文件
use std::io::{BufRead, BufReader}; // 用于缓冲读取
use anyhow::{Context, Result}; // 用于错误处理
use serde::Deserialize; // 用于反序列化 JSON
use xz2::read::XzDecoder; // 用于 xz 解压

/// 表示 JSONL 文件中的单行。
/// 仅提取 "timestamp" 字段。
#[derive(Deserialize, Debug)]
struct Line {
    timestamp: Option<f64>,
}

/// 处理给定的 xz 压缩 JSONL 文件。
/// 实时解压,将每一行解析为 JSON,
/// 并在可用时打印时间戳。忽略无效行。
fn process_file(path: &str) -> Result<()> {
    // 打开文件
    let file = File::open(path).with_context(|| format!("打开文件失败:{}", path))?;
    // 创建缓冲读取器以提高效率
    let reader = BufReader::new(file);
    // 用 xz 解压器包装
    let decompressor = XzDecoder::new(reader);
    // 再创建一个缓冲读取器用于逐行读取
    let buf_reader = BufReader::new(decompressor);

    // 遍历每一行
    for line_result in buf_reader.lines() {
        let line = match line_result {
            Ok(l) => l,
            Err(_) => continue, // 跳过有 IO 错误的行
        };
        // 跳过空行
        if line.trim().is_empty() {
            continue;
        }
        // 尝试解析为 JSON
        match serde_json::from_str::<Line>(&line) {
            Ok(obj) => {
                // 如果存在则打印时间戳
                if let Some(ts) = obj.timestamp {
                    println!("{}", ts);
                }
            }
            Err(e) => {
                eprintln!("解析 JSON 行失败:{}", e);
                continue;
            }
        }
    }
    Ok(())
}

/// 主入口点。
/// 将文件路径作为第一个参数,如果未提供则使用默认示例文件。
fn main() -> Result<()> {
    // 要求文件路径作为第一个位置参数。
    // 如果缺失,打印简短用法信息并以非零退出码退出。
    let mut args = std::env::args();
    let prog = args.next().unwrap_or_else(|| "rust_parse_status_logs".to_string());
    let path = match args.next() {
        Some(p) => p,
        None => {
            eprintln!("用法:{} <path-to-xz-jsonlines-file>", prog);
            std::process::exit(2);
        }
    };

    // 处理文件
    process_file(&path)
}

Cargo.toml

Cargo.toml
[package]
name = "rust_parse_status_logs"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
xz2 = "0.1"
anyhow = "1"

示例输入

example.json
{"timestamp": 1744267290.991, "status": "ok"}
{"timestamp": 1744267291.987, "status": "ok"}
{"status": "missing timestamp"}
invalid json line

请注意,你需要用 xz 压缩此文件才能创建程序的输入文件。

compress_example.sh
xz example.json

构建与运行

run_decompress_example.sh
cargo run -- example.json.xz

预期输出:

decompress_output.txt
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/rust_parse_status_logs example.json.xz`
1744267290.991
1744267291.987
Failed to parse JSON line: expected value at line 1 column 1

Check out similar posts by category: Rust