Rust之构建命令行程序(六):信息写入

用Rust创建可靠的命令行程序 2020年是技术领域的元年,编程语言也迎来了翻天覆地的变化。Java、Python、JavaScript等多种语言在这片广袤的市场上掀起了一阵股风,各个方向都有不少开发者投入其中,同时还有一些企业将更加重视其技术能力。而在这些语言中,Rust 则名扬天下。Rust 是一种新兴的编程语言,拥有强大的性能、安全、并发特性和方便开发的特点。本文就带你用 Rust 创建一个可靠的命令行程序,看看它到底有多强悍。Rust 是一门注重速度、零开销、内存安全性和高效开发的系统级编程语言。 阅读详情

开发环境

  • Windows 11
  • Rust 1.77.2 

  • VS Code 1.88.1

项目工程

这次创建了新的工程minigrep.

将错误信息写入标准错误而不是标准输出

此时,我们正在使用宏println!将所有输出写入终端。在大多数终端中,有两种输出:一般信息的标准输出(stdout)和错误消息的标准错误(stderr)。这种区别使用户能够选择将程序的成功输出指向文件,但仍将错误信息打印到屏幕上。

 println!宏只能打印到标准输出,所以我们必须使用其他东西打印到标准错误。

检查错误写入的位置

首先,让我们观察minigrep打印的内容当前是如何写入标准输出的,包括我们希望改为写入标准错误的任何错误消息。我们将通过将标准输出流重定向到一个文件来实现这一点,同时故意造成一个错误。我们不会重定向标准错误流,因此发送到标准错误的任何内容都将继续显示在屏幕上。 

命令行程序应该将错误消息发送到标准错误流,这样即使我们将标准输出流重定向到文件,我们仍然可以在屏幕上看到错误消息。我们的程序目前表现不佳:我们将看到它将错误消息输出保存到一个文件中!

为了演示这种行为,我们将使用》和文件路径output.txt运行程序,我们希望将标准输出流重定向到该文件路径。我们不会传递任何参数,这会导致一个错误: 

$ cargo run > output.txt

> 语法告诉系统将标准输出的内容写入output.txt而不是屏幕。我们没有看到我们期望的打印到屏幕上的错误消息,所以这意味着它一定在文件中结束了。以下是output.txt包含的内容:

Problem parsing arguments: not enough arguments

 是的,我们的错误信息被打印到标准输出。将这样的错误信息打印到标准错误中会更有用,这样只有成功运行的数据才会出现在文件中。我们会改变的。

打印误差到标准误差

我们将使用示例12-24中的代码来改变错误信息的打印方式。由于我们在本章前面所做的重构,所有打印错误信息的代码都在一个函数main中。标准库提供了eprintln!打印到标准错误流的宏,所以让我们更改我们调用println!的两个位置!要打印错误,请使用eprintln!相反。

文件名:src/main.rs 

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = cfg::Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

    if let Err(e) = cfg::run(config) {
        eprintln!("Application error: {e}");
        process::exit(1);
    }
}

 示例12-24:使用eprintln!将错误消息写入标准错误而不是标准输出

 现在让我们以同样的方式再次运行程序,没有任何参数,并且使用>重定向标准输出:

$ cargo run > output.txt
Problem parsing arguments: not enough arguments

现在我们在屏幕上看到错误,output.txt不包含任何内容,这是我们期望命令行程序的行为。

让我们使用不会导致错误但仍将标准输出重定向到文件的参数再次运行程序,如下所示: 

$ cargo run -- to poem.txt > output.txt

我们不会在终端上看到任何输出,output.txt将包含我们的结果:

文件名:output.txt 

Are you nobody, too?
How dreary to be somebody!

运行结果:

这表明我们现在使用标准输出进行成功输出,并根据需要使用标准错误进行错误输出。

全部代码

文件名: main.rs

use std::env;
use std::process;
use minigrep::cfg;
 

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = cfg::Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

    if let Err(e) = cfg::run(config) {
        eprintln!("Application error: {e}");
        process::exit(1);
    }
}

文件名:lib.rs

pub mod cfg {
    use std::error::Error;
    use std::env;
    use std::fs;

    pub struct Config {
        pub query: String,
        pub file_path: String,
        pub ignore_case: bool,
    }
    
    impl Config {
        pub fn build(args: &[String]) -> Result<Config, &'static str> {
            if args.len() < 3 {
                return Err("not enough arguments");
            }
    
            let query = args[1].clone();
            let file_path = args[2].clone();
    
            let ignore_case = env::var("IGNORE_CASE").is_ok();
    
            Ok(Config {
                query,
                file_path,
                ignore_case,
            })
        }
    }


#[cfg(test)]
mod tests {
    use super::*;
 
    #[test]
    fn case_sensitive() {
        let query = "duct";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
 
        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
    }
 
    #[test]
    fn case_insensitive() {
        let query = "rUsT";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
 
        assert_eq!(
            vec!["Rust:", "Trust me."],
            search_case_insensitive(query, contents)
        );
    }
}

    pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
        let mut results = Vec::new();

        for line in contents.lines() {
            if line.contains(query) {
                results.push(line);
            }
        }

        results
    }


    pub fn search_case_insensitive<'a>(
        query: &str,
        contents: &'a str,
    ) -> Vec<&'a str> {
        let query = query.to_lowercase();
        let mut results = Vec::new();
    
        for line in contents.lines() {
            if line.to_lowercase().contains(&query) {
                results.push(line);
            }
        }
    
        results
    }

    pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
        let contents = fs::read_to_string(config.file_path)?;

        let results = if config.ignore_case {
            search_case_insensitive(&config.query, &contents)
        } else {
            search(&config.query, &contents)
        };

        for line in results {
            println!("{line}");
        }

        Ok(())
    }
}

总结 

本章概述了到目前为止您所学的一些主要概念,并介绍了如何在Rust中执行常见的I/O操作。通过使用命令行参数、文件、环境变量和eprintln!宏处理打印错误,现在您可以准备编写命令行应用程序了。结合前几章的概念,您的代码将组织良好,在适当的数据结构中有效地存储数据,很好地处理错误,并得到良好的测试。 

接下来,我们将探索一些受函数式语言影响的Rust特性:闭包和迭代器。 

本章重点

  • 区分标准错误输出println!
  • 标准误差宏eprintln!及使用方法
  • 如何将错误信息写入文件

 

rust-embed:Rust宏,它在发行时的编译时将文件加载到rust二进制文件中,并在开发期间从fs加载文件 立即下载

相关推荐

115.工业相机海康SDK开发指南(阅读)

包含初始化SDK和反初始化SDK接口。(由于看不到函数内部的具体实现,因此以下的解释仅代表个人的理解)

weixin_63779802的博客 5993

[rust-007]rust的println!函数的各种用法

println!是宏。 println!的第一个参数,是格式化字符串,其他参数要放在这个字符串里打印出来。 第一种情况,格式化字符串,是纯粹的字符串: println!("Hello World"); // 输出结果如下: // Hello World 第二种情况,格式化字符串里有替换占位符"{}",从第二个参数开始,各参数逐个替换格式化字符串的占位符: println!("{} World {} {}", "Hello", true, 42); //输出结果: //Hello World t

大数据和机器学习研究 9140

反无人机技术实战解析:从探测到拦截的全方位防御策略

本文深入解析了反无人机(C-UAS)技术的全链条防御策略。从雷达、无线电频谱、光电等多维度探测技术,到数据融合与威胁评估,再到无线电干扰、导航诱骗及物理拦截等核心拦截手段,系统阐述了如何构建从探测到拦截的实战化防御体系,为关键设施、大型活动等场景提供定制化空域安全解决方案。

weixin_29230649的博客 401

rust println!格式化输出

格式 格式说明 样例代码 输出效果 {} 将任何实现了std::fmt::Display trait的类型格式化为字符串输出 println!("Hello, {}!", "world"); Hello, world! {:?} 以调试模式打印任何实现了std::fmt::Debug trait的类型 println!("Debug: {:?}", [1, 2, 3]); Debug: [1, 2, 3] {:#?} 在调试模式下将

小码农的博客 1449

告别理论推导!用Matlab工具箱5步搞定相机标定(附张正友法原理白话解读)

本文详细介绍了如何使用Matlab工具箱在5步内完成相机标定,无需复杂理论推导。通过棋盘格拍摄和Matlab的Camera Calibrator工具箱,用户可以快速获取相机的内参和外参,显著提升3D视觉的准确性。文章还提供了张正友标定法的白话解读,适合初学者和工程师快速上手。

weixin_42548816的博客 415

embedding计算过程

文章目录embedding计算过程(错了跟我讲,我会改)参考示例个人理解过程图示总结(基于pytorch) embedding计算过程(错了跟我讲,我会改) 参考示例 https://www.jianshu.com/p/63e7acc5e890 个人理解过程图示 总结(基于pytorch) 总的来说,就是将独热向量的长度(对应嵌入层的num_embeddings)转变为嵌入层的emdedding_size(而独热向量的维数就是你的数据中一共产生了多少个不一样的词,至少是这么多)。 数据的变化,将二维

纸上得来终觉浅,绝知此事要躬行! 1632

Rust eprintln

可以将错误消息输出到标准错误流中,并使其在终端上呈现出不同的颜色,以便用户能够更方便地区分错误消息和正常输出。同时,由于标准输出和标准错误是不同的输出流,因此将错误消息输出到标准错误流中不会混淆程序的正常输出。标准错误流和标准输出流是程序与外界交互的两个基本接口。标准错误流一般用于输出程序运行过程中的错误消息,而标准输出流则用于输出正常的结果信息。代表 error,表示将消息输出到标准错误流中,而不是标准输出流。是一个 Rust 的宏,用于将错误消息输出到标准错误流中。宏,用于将消息打印到标准输出流中。

醒途 413

【LCMM实战指南】从增长混合模型到潜在类别轨迹分析的纵向数据建模

本文深入解析了LCMM(潜在类别混合模型)在纵向数据建模中的应用,对比了LCGA与GMM的核心差异与选择策略。通过R语言lcmm包的实战演练,详细展示了从数据模拟、模型拟合、类别数选择到结果解读的全流程,为识别数据中不同的发展轨迹亚型提供了系统指南。

weixin_29070861的博客 380

Rust语言进阶之println!用法实例(八十七)

本篇目的:Rust语言进阶之println!用法实例基本概念println!是Rust语言中的一个宏,用于将输出打印到标准输出(通常是终端)。它属于Rust的标准库中的宏之一,广泛用于调试、日志记录和交互式程序中。println!的作用是格式化并打印一段字符串,同时在字符串的末尾自动添加换行符。作为宏,println!在编译时进行展开,因此它比常规函数调用具有更高的灵活性和效率。格式化输出println!不仅仅是输出一个简单的字符串,它还支持格式化输出。

Android系统攻城狮 1309

先进封装技术的一些优缺点探讨

半导体封装技术是半导体制造过程中的关键环节,它不仅保护了芯片免受物理损伤,还提供了电气连接和散热功能。随着技术的发展,出现了多种先进的封装技术,每种技术都有其特定的应用场景和优缺点。

阿拉伯梳子的专栏 2170

Android性能分析深度指南:Perfetto工具全面解析

本文深入探讨Android性能优化工具Perfetto的应用与实践。作为Google推出的新一代追踪系统,Perfetto整合了CPU调度、内存分配、电池状态等多维度分析功能,通过高性能数据处理和可视化界面帮助开发者定位性能瓶颈。文章系统介绍了Perfetto的架构原理、配置方法和核心功能,包括CPU性能分析、内存泄漏检测等关键技术。通过电商应用卡顿优化的实战案例,展示了如何利用数学建模(如帧时间公式$T_{\text{render}}\leq16.67\text{ms}$)指导性能调优。

郑伟强dev的专栏 144

rust学习-rust中的格式化打印

可以通过实现 std::fmt::Display 和 std::fmt::Debug trait 来为自定义类型定义格式化行为x: i32,y: i32,write!println!}", p);Display trait 用于定义类型的字符串表示,适用于用户友好的输出Debug trait 用于调试目的,提供详细的内部结构信息。

weixin_42333247的博客 1543

【小白深度教程 1.8】手把手教你使用 Depth Anything V2 估计单目深度,并映射到 3D 点云(含 Python 代码)

我们尝试使用单目深度估计技术(Depth Anything V2),来进行准确的双目深度估计,并将场景转换成 3D 点云,Python 代码在最后。

2401_87064292的博客 5090

Rust之构建命令行程序(一):接受命令行参数

Rust之构建命令行程序,接受命令行参数

wing的专栏 2000

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Rust学习-构建命令行程序

Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择本文以实现一个minigrep为例,展开对之前学习的回归。

wangkai6666的博客 1458

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【跟小嘉学 Rust 编程】十二、构建一个命令行程序

本章是一个目前所学的很多技能的应用,以及标准库的探索,我们讲构建一个命令行程序工具来练习现在已经学习过的一些Rust的技能。我们将构建自己的版本的命令行工具:grep(Globally search a Regular Expression and print)。主要教材参考 《The Rust Programming Language》

小嘉丶学长的博客 2987

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Rust之构建命令行程序(三):重构改进模块化和错误处理

Rust之构建命令行程序(三):重构改进模块化和错误处理

wing的专栏 1652

【Rust】Rust学习 第十二章一个 I/O 项目:构建一个命令行程序

本章既是一个目前所学的很多技能的概括,也是一个更多标准库功能的探索。我们将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。grep。grep 是 “egularrint.” 的首字母缩写。grep最简单的使用场景是在特定文件中搜索指定字符串。为此,grep获取一个文件名和一个字符串作为参数,接着读取文件并找到其中包含字符串参数的行,然后打印出这些行。在这个过程中,我们会展示如何让我们的命令行工具利用很多命令行工具中用到的终端功能。

StudyWinter的博客 2065

11、Rust程序设计语言——构建一个命令行程序

Rust学习笔记

qq_51180928的博客 499

Rust学习记录--C12 实例:写一个命令行程序

要求:在一个文件中搜索字符串返回对应的行的列表。

weixin_44900096的博客 905

Rust从0到1实战系列篇 如何一个 I/O 项目:构建一个命令行程序

本章通过构建一个简化版grep命令行程序来练习Rust的关键功能。项目名为minigrep,能够接受文件路径和搜索字符串两个参数,读取文件内容并打印匹配行。首先使用std::env::args()获取命令行参数,将查询字符串和文件路径存入变量。然后通过std::fs::read_to_string读取文件内容并打印。虽然当前功能简单,但后续会处理错误并分离功能模块。这个项目展示了Rust在构建命令行工具方面的优势,并为理解更复杂的类似项目(如ripgrep)打下基础。完整代码可从示例12-1至12-4中查看

m0_68635815的博客 795

12-输入/输出项目构建命令行程序

测试驱动开发(TDD, test-driven development)是一种软件开发方法,即在实际代码执行之前编写测试。它是一种用于确保所开发的软件满足指定要求的技术,并有助于在开发周期的早期捕捉错误。该过程遵循一个短暂的重复周期,Red-Green-Refactor,即 "红-绿-重构":①. 红色编写一个定义函数或函数改进的测试,该测试最初失败的原因应该是函数不存在或不按预期执行。②. 绿色:编写通过测试所需的最少代码量。这一阶段的主要目标是在遵守指定要求的前提下尽快通过测试。

优快云爱好者的独到见解 1322

Rust 从零到精通:构建一个专业级命令行工具 greprs

欢迎来到 Rust 的世界!Rust 是一门现代化的系统编程语言,专注于性能、内存安全和并发性。它没有垃圾回收器,却能通过强大的编译器和所有权系统在编译时就保证内存安全。这使得 Rust 成为构建高性能、高可靠性软件的绝佳选择,从底层的操作系统、游戏引擎,到高性能 Web 后端和云原生应用,无处不见其身影。本教程将不仅仅是“Hello, world!我们的目标是带您走过一段完整的旅程:从在 Linux 环境下安装 Rust,到亲手构建一个功能完整、结构清晰、经过专业测试的命令行文本搜索工具——

2301_80863610的博客 1万+

Rust入门(八):构建一个命令行程序

Rust入门(八):构建一个命令行测试程序的例子

weixin_46463785的博客 1361

青少年编程与数学 02-019 Rust 编程基础 18课题、命令行程序

使用 Rust 编写一个处理 I/O 的命令行程序,以及涉及的关键知识点的讲解。

明月看潮生的博客 1667

Rust从入门到暴走(19)--- 如何写一个组织优雅的Rust命令行程序

main.rs 是命令行程序的入口,负责获取args以及核心业务逻辑的处理函数的调用。根据调用函数的返回结果决定程序是否正常运行下去或者终止。lib.rs 是核心业务逻辑的处理函数的定义,里面包括args的解析和验证函数,以及包括查询指定文件中是否包含指定关键词的函数。

Luca的博客, 网络安全Hacker,人工智能AIer,区块链Web3er,全栈软件开发er 747
上一篇: Rust之构建命令行程序(五):环境变量
下一篇: Rust之函数式语言特性:迭代器和闭包(一):概述
枫★曦
博客等级 码龄19年 251粉丝 153原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值