Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >缓冲和非缓冲cat(1)实现

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

Code Review用户
提问于 2020-08-10 20: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
运行
AI代码解释
复制
// 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
运行
AI代码解释
复制
// 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-10 23:49:29

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

参数解析

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
运行
AI代码解释
复制
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

复制
相关文章
C的全缓冲、行缓冲和无缓冲
基于流的操作最终会调用read或者write函数进行I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。
恋喵大鲤鱼
2018/08/03
3.2K0
C/CPP 的全缓冲、行缓冲和无缓冲
C/C++中,基于 I/O 流的操作最终会调用系统接口 read() 和 write() 完成 I/O 操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O接口的调用次数。
CtrlX
2022/11/14
1.2K0
CC++的全缓冲、行缓冲和无缓冲
C/C++中,基于I/O流的操作最终会调用系统接口read()和write()完成I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O接口的调用次数。
恋喵大鲤鱼
2019/02/22
1.9K0
Go-并发编程-无缓冲和有缓冲 channel 的区别(二)
有缓冲 channel 是指带有一定存储空间的 channel,发送和接收操作不一定需要同步进行。当缓冲区未满时,发送操作会立即返回,并将数据存储在缓冲区中,而接收操作则会等待直到缓冲区中有数据可用。当缓冲区已满时,发送操作将被阻塞,直到缓冲区中有空闲位置可用。
堕落飞鸟
2023/04/21
2780
Go-并发编程-无缓冲和有缓冲 channel 的区别(一)
Go 语言提供了一种称为 channel 的通信机制,可以用于协调并发执行的多个 goroutine。在 Go 中,channel 是一种特殊类型的变量,用于在 goroutine 之间进行通信。
堕落飞鸟
2023/04/21
3490
实现getChannel()+非直接缓冲区
只有ByteBuffer可以获得直接缓冲区,通过allocateDirect()获取的缓冲区为直接缓冲区,这些缓冲区是建立在物理内存之中的。
用户7108768
2021/09/26
4090
iOS的三重缓冲和微型口吃(micro stuttering)
iOS 中采用双重缓冲和三重缓冲一起使用,从 display 中就可以看出来。即:双缓冲不够用了就采用三缓冲。
全栈程序员站长
2022/08/26
5230
iOS的三重缓冲和微型口吃(micro stuttering)
文件I/O (一).非缓冲IO实现mycopy
UNIX/Linux 的缔造者们将数据的 来源和目标 都抽象为 文件,所以在 UNIX/Linux 系统中 一切皆文件
franket
2021/09/16
5050
NIO之缓冲区【直接和非直接缓冲区】
  字节缓冲区跟其他缓冲区类型最明显的不同在于,它们可以成为通道所执行的 I/O 的源头和/或目标。其实发现通道只接收ByteBuffer作为参数这个将Channel的时候会发现。
用户4919348
2019/04/18
2K0
NIO之缓冲区【直接和非直接缓冲区】
非直接缓冲区和直接缓冲区 有什么区别
通过allocate()方法获取的缓冲区都是非直接缓冲区。这些缓冲区是建立在JVM堆内存之中的。
用户7108768
2021/09/26
5140
无限缓冲的channel(1)
事情的起因是前几周看到鸟窝写了一篇关于实现无限缓冲 channel 的文章,当时忙着和小姐姐聊天没看,今天想起来了。
RememberGo
2021/07/04
7770
CAT3、CAT4、CAT5、CAT5E、CAT6、CAT6A、CAT7和CAT8网线的介绍
Cat5 Ethernet cable introduced the 10/100 Mbps speed to the Ethernet, which means that the cables can support either 10 Mbps or 100 Mbps speeds. A 100 Mbps speed is also known as Fast Ethernet, and Cat5 cables were the first Fast Ethernet-capable cables to be introduced. Cat5 Ethernet cable can also be used for telephone signals and video, in addition to Ethernet data.
黑马Amos
2023/03/21
10.7K0
CAT3、CAT4、CAT5、CAT5E、CAT6、CAT6A、CAT7和CAT8网线的介绍
cat监控实现腾讯云短信告警
CAT(Central Application Tracking),是基于 Java 开发的分布式实时监控系统。CAT在基础存储、高性能通信、大规模在线访问、服务治理、实时监控、容器化及集群智能调度等领域提供业界领先的、统一的解决方案。CAT 目前在美团的产品定位是应用层的统一监控组件。
腾讯即时通信IM
2020/05/29
8.7K0
NB-IoT和Cat-M1与Cat-1:如何选择正确的LTE-IoT标准
NB-IoT-and-Cat-M1-vs.-Cat-1-How-To-Choose-The-Right-LTE-IoT-Standard-1536x944-1.jpg
用户4122690
2020/06/23
3.3K0
NB-IoT和Cat-M1与Cat-1:如何选择正确的LTE-IoT标准
17.opengl高级-帧缓冲(1)
1.3. check帧缓冲状态(没有深入理解,具体需要检测哪些数据),后面的操作渲染到当前帧缓冲中,即离屏渲染(Off-screen Rendering),窗口上看不到任何变化
公号sumsmile
2020/07/10
1.9K0
17.opengl高级-帧缓冲(1)
cat监控实现腾讯云短信告警
CAT(Central Application Tracking),是基于 Java 开发的分布式实时监控系统。CAT在基础存储、高性能通信、大规模在线访问、服务治理、实时监控、容器化及集群智能调度等领域提供业界领先的、统一的解决方案。CAT 目前在美团的产品定位是应用层的统一监控组件。
GavinWang
2020/05/28
9.9K0
[Go] golang缓冲通道实现资源池
go的pool资源池: 1.当有多个并发请求的时候,比如需要查询数据库 2.先创建一个2个容量的数据库连接资源池 3.当一个请求过来的时候,去资源池里请求连接资源,肯定是空的就创建一个连接,执行查询,结束后放入了资源池里 4.当第二个请求过来的时候,也是去资源池请求连接资源,就直接在池中拿过来一个连接进行查询 5.当并发大的时候,资源池里面没有足够连接资源,就会不停创建新资源,放入池里面的时候,也会放不进去,就主动关闭掉这个资源 6.这里的资源池实质上是一个缓冲通道,里面放着连接资源
唯一Chat
2019/09/10
8400
[Go] golang缓冲通道实现资源池
电源缓启动(软起动)
谈起电源的缓启动(软起动),我们都知道现在大多数电子系统都要支持热插拔功能。所谓热插拔,也就是在系统正常工作时,带电对系统的某个单元进行插拔操作,且不对系统产生任何影响。
芯动大师
2024/03/28
1410
电源缓启动(软起动)
环形缓冲区的实现
队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO)的线性表,只允许在一端插入(入队),在另一端进行删除(出队)。
刘盼
2018/07/26
2.9K0
环形缓冲区的实现
cat命令
cat命令属于文件管理,用于连接文件并打印到标准输出设备上,cat经常用来显示文件的内容,注意,当文件较大时,文本在屏幕上迅速闪过,会出现滚屏现象,此时往往看不清所显示的内容,为了控制滚屏,可以按Ctrl+S键停止滚屏,按Ctrl+Q键可以恢复滚屏,此外可以用more等命令进行读文件并分页显示。
WindRunnerMax
2020/08/27
1.5K0

相似问题

cat重新实现的尺寸改进

20

cat命令的简单Windows实现

10

Golang非缓冲信道-正确使用

20

循环缓冲C++实现

20

循环缓冲区实现

10
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文