首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >缓冲和非缓冲cat(1)实现

缓冲和非缓冲cat(1)实现
EN

Code Review用户
提问于 2020-08-11 04:18:02
回答 1查看 84关注 0票数 6

为了好玩,我重新实现了cat(1)。我遵循的是开放组基规范(2018年年版第7期),而不是GNU变体及其命令行参数。

缓冲和-un缓冲行为

虽然规范定义了-u的行为,但它没有定义在缺少-unbuffered参数时如何连接参数。为了适应BufReadBufWriter,我使用了一种缓冲方法,它只使用Rust已经存在的方法。

我对返回代码问题并不完全满意。目前,我在io::Result中使用main返回最后一个错误,但是,这也意味着最后一个错误将被报告两次。我可以使用std::process::exit,但这需要main的包装器。

参数解析和附加依赖项

我承认,main的争论处理部分不会赢得选美比赛。但是,我不想添加clap或其他参数处理库,而是专注于使用std。程序应该遵循效用参数语法,但是它不遵循准则5(例如,-uuuu-u -u -u -u不同)和9 (-u不需要作为第一个参数)。然而,参数分组是这个玩具程序的一个非目标。

此外,与GNU cat相比,未知选项被解释为文件名,而GNU cat将带错误消息退出。不过,我不确定这是否违反了规范。

Implementation

应用程序分为两部分,即main.rs (主要是参数解析)和lib.rs (实际实现)。对该组织的任何评论都是可以的。

代码语言:javascript
代码运行次数:0
运行
复制
// main.rs
use std::ffi::OsString;
use std::path::Path;

use cat::{cat_buffered_single, cat_unbuffered_single};

fn main() -> std::io::Result<()> {
    let mut args: Vec<OsString> = std::env::args_os().skip(1).collect();

    // Only parse arguments up to "--"
    let args_up_to = if let Some(index) = args.iter().position(|arg| arg == "--") {
        args.remove(index);
        index
    } else {
        args.len()
    };

    // Keep all arguments after "--" as-is
    let verbatim_args = args.split_off(args_up_to);

    // Check for "-u" in valid positions and remove the first one
    let cat_func = if args.iter().any(|arg| arg == "-u") {
        args = args.into_iter().filter(|x| x != "-u").collect();
        cat_unbuffered_single
    } else {
        cat_buffered_single
    };

    // Recombine arguments
    args.extend(verbatim_args);

    // Fallback to stdin behaviour
    if args.is_empty() {
        args.push("-".into());
    }

    let mut result = Ok(());

    for arg in args {
        let path = Path::new(&arg);
        match cat_func(path) {
            Ok(()) => continue,
            Err(e) => {
                eprintln!("cat: {}: {}", path.to_string_lossy(), e);
                result = Err(e);
            }
        }
    }
    result
}
代码语言:javascript
代码运行次数:0
运行
复制
// lib.rs
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;

// Dumps all bytes from `src` into `dest`, using both buffer functionalities.
fn dump_buffered_single(src: &mut dyn BufRead, dest: &mut BufWriter<impl Write>) -> io::Result<()> {
    loop {
        let buf = src.fill_buf()?;
        if buf.is_empty() {
            break;
        }
        dest.write_all(buf)?;
        let bytes = buf.len();
        src.consume(bytes);
    }
    dest.flush()
}

/// Dumps the file given by `path` on `stdout` using buffered IO.
///
/// If `path` is `"-"`, then `stdin` is used as input instead of a file.
///
/// Example
/// ```
/// # use cat::cat_buffered_single;
/// cat_buffered_single("hello.txt".as_ref());
/// ```
pub fn cat_buffered_single(path: &Path) -> io::Result<()> {
    let stdout = io::stdout();
    let handle = stdout.lock();
    let mut writer = io::BufWriter::new(handle);

    if path == Path::new("-") {
        dump_buffered_single(&mut io::stdin().lock(), &mut writer)?;
    } else {
        let input = BufReader::new(File::open(path)?);
        let mut reader = BufReader::new(input);
        dump_buffered_single(&mut reader, &mut writer)?;
    }
    Ok(writer.flush()?)
}

// Dumps all bytes from `src` into `dest`, byte by byte.
fn dump_unbuffered_single(src: &mut dyn Read, dest: &mut dyn Write) -> io::Result<()> {
    for byte in src.bytes() {
        dest.write_all(std::slice::from_ref(&byte?))?;
    }
    Ok(())
}

/// Dumps the file given by `path` on `stdout` without buffering
///
/// If `path` is `"-"`, then `stdin` is used as input instead of a file.
///
/// Example
/// ```
/// # use cat::cat_unbuffered_single;
/// cat_unbuffered_single("hello.txt".as_ref());
/// ```
pub fn cat_unbuffered_single(path: &Path) -> io::Result<()> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();

    if path == Path::new("-") {
        dump_unbuffered_single(&mut io::stdin().lock(), &mut handle)?;
    } else {
        let mut file = File::open(path)?;
        dump_unbuffered_single(&mut file, &mut handle)?;
    }
    Ok(())
}

#[cfg(test)]
mod test {
    use super::*;

    fn test_dump_buffered_single(test_bytes: &[u8]) {
        let src = test_bytes;
        let mut reader = BufReader::new(src);
        let mut dest = vec![];
        {
            let mut writer = BufWriter::new(&mut dest);
            dump_buffered_single(&mut reader, &mut writer).unwrap();
        }
        assert_eq!(dest, src);
    }

    #[test]
    fn dumps_all_buffered_data() {
        test_dump_buffered_single(b"Hello, World");
    }
    #[test]
    fn dumps_no_buffered_data() {
        test_dump_buffered_single(b"");
    }

    fn test_dump_unbuffered_single(bytes: &[u8]) {
        let source = bytes;
        let mut reader = source.clone();
        let mut destination = vec![];
        dump_unbuffered_single(&mut reader, &mut destination).unwrap();
        assert_eq!(destination, source);
    }
    #[test]
    fn dumps_all_unbuffered_data() {
        test_dump_unbuffered_single(b"Hello, World");
    }
    #[test]
    fn dumps_no_unbuffered_data() {
        test_dump_unbuffered_single(b"");
    }
}

我在上面的代码中使用了cargo fmtcargo clippy。顺便说一句,我认为自己是个生锈初学者,所以可以随意评论代码的任何部分。

EN

回答 1

Code Review用户

回答已采纳

发布于 2020-08-11 07:49:29

首先,我想指出,以一致的格式、有帮助的注释和清晰的逻辑阅读代码是非常高兴的。以下几点可能是主观的和挑剔的,但它们并不代表我的一般印象。

参数解析

程序应该遵循效用参数语法,但是它不遵循... 9准则(-u不需要成为第一个参数)。

但是,要求-u出现在操作数之前,不会使实现变得更容易吗?我是这样想的:

代码语言:javascript
代码运行次数:0
运行
复制
let (buffered, operands) = match args.get(0) {
    None => {
        args.push("-".into());
        (true, &args[..])
    }
    Some(arg) if *arg == "-u" => (false, &args[1..]),
    Some(_) => (true, &args[..]),
};

BufReadWrite

&mut dyn BufRead不同,按值取BufRead更为常见。原因是对BufRead的可变引用自动实现了BufRead

不使用&mut BufWriter<impl Write>类型的参数,只需使用Write就够了,因为BufWriter的功能可以通过Write访问。

结果:

代码语言:javascript
代码运行次数:0
运行
复制
fn dump_buffered_single<R, W>(mut src: R, mut dest: W) -> io::Result<()>
where
    R: BufRead,
    W: Write,
{
    // ...
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/247752

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档