You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Rust nom7实现带容量限制的流式多行文本解析器

实现方案

前置依赖

先在Cargo.toml中添加nom 7依赖:

[dependencies]
nom = "7.0"

完整实现代码

use nom::{
    character::streaming::line_ending,
    error::{Error, ErrorKind},
    Err, IResult,
};
use std::fs::File;
use std::io::{Read, Write};
use std::str;

/// 从输入切片中切割出一行内容(不含行结束符),流式模式
fn parse_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let (remaining, line_content) = nom::bytes::streaming::take_until("\n")(input)?;
    let (remaining, _) = line_ending(remaining)?;
    Ok((remaining, line_content))
}

fn main() -> std::io::Result<()> {
    // 打开输入文件和两个输出文件
    let mut input_file = File::open("/words.txt")?;
    let mut valid_file = File::create("valid_ascii.txt")?;
    let mut invalid_file = File::create("invalid_content.txt")?;

    // 1000字节固定缓冲区,对应1000ASCII字符的容量上限
    const BUF_SIZE: usize = 1000;
    let mut buf = vec![0u8; BUF_SIZE];
    // 记录当前缓冲区已填充的有效数据长度
    let mut filled = 0;

    loop {
        // 往缓冲区的剩余空间读入新数据
        let read_count = input_file.read(&mut buf[filled..])?;
        if read_count == 0 && filled == 0 {
            // 读到文件末尾,无剩余数据,退出循环
            break;
        }
        filled += read_count;

        // 循环解析缓冲区中的完整行
        loop {
            match parse_line(&buf[..filled]) {
                Ok((remaining, line)) => {
                    // 校验行是否为不含控制字符的有效ASCII
                    let is_valid = line.iter().all(|&b| b.is_ascii() && !b.is_ascii_control());
                    if is_valid && !line.is_empty() {
                        writeln!(valid_file, "A{}> {}", line.len(), str::from_utf8(line).unwrap())?;
                    } else {
                        writeln!(invalid_file, "C> {}", String::from_utf8_lossy(line))?;
                    }
                    // 计算剩余未解析数据的长度,将剩余数据移到缓冲区头部
                    let remaining_len = remaining.len();
                    buf.copy_within(filled - remaining_len..filled, 0);
                    filled = remaining_len;
                }
                Err(Err::Incomplete(_)) => {
                    // 当前缓冲区没有完整行,需要继续读数据
                    // 如果缓冲区已经满了还没有行结束符,说明行超长,判定为无效
                    if filled == BUF_SIZE {
                        writeln!(invalid_file, "C> {}", String::from_utf8_lossy(&buf[..filled]))?;
                        filled = 0;
                    }
                    break;
                }
                Err(Err::Error(Error { input: _, code })) | Err(Err::Failure(Error { input: _, code })) => {
                    // 解析错误,将当前缓冲区数据判定为无效
                    if filled > 0 {
                        writeln!(invalid_file, "C> {}", String::from_utf8_lossy(&buf[..filled]))?;
                        filled = 0;
                    }
                    if code == ErrorKind::Eof {
                        break;
                    }
                }
            }
        }
    }

    // 处理缓冲区最后剩余的无行结束符的内容
    if filled > 0 {
        let is_valid = buf[..filled].iter().all(|&b| b.is_ascii() && !b.is_ascii_control());
        if is_valid && filled > 0 {
            writeln!(valid_file, "A{}> {}", filled, str::from_utf8(&buf[..filled]).unwrap())?;
        } else {
            writeln!(invalid_file, "C> {}", String::from_utf8_lossy(&buf[..filled]))?;
        }
    }

    Ok(())
}

关键逻辑说明

  • 缓冲区设计:使用固定1000字节的缓冲区,不会随文件大小增长内存占用,符合大文件流式处理的低内存要求。
  • 流式行解析:基于nom 7的streaming::line_ending实现行结束符识别,只有遇到完整行才会触发内容校验和写入,不需要加载全量文件到内存。
  • 边界处理:包含了行超长、文件末尾无行结束符、解析异常等场景的处理,不会遗漏任何输入内容。
  • 校验逻辑和原有实现完全兼容,仅把打印输出改为写入对应文件,也可以根据需求调整输出格式。

内容的提问来源于stack exchange,提问作者Quiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 01:45:02