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!及使用方法
  • 如何将错误信息写入文件

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/574814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker安装【zookeeper】【kafka】【provectuslabs/kafka-ui】记录

目录 1.安装zookeeper:3.9.2-jre-172.安装kafka:3.7.03.安装provectuslabs/kafka-ui &#xff08;选做&#xff09;新环境没有jdk&#xff0c;安装jdk-17.0.10备用 mkdir -p /export/{data,apps,logs,conf,downloads}cd /export/downloadscurl -OLk https://download.oracle.…

【VScode】VScode+如何从git上面拉取代码?

目录标题 1、打开VSCode。File>New Window。2、打开集成终端&#xff08;Terminal > New Terminal 或使用快捷键Ctrl \)。3、在终端中&#xff0c;使用Git命令克隆仓库。4、打开项目。 1、打开VSCode。File>New Window。 2、打开集成终端&#xff08;Terminal > …

基于HAL库的stm32中定时器的使用--定时器中断每隔一秒进行led灯的闪烁以及定时器生成PWM

一&#xff1a;什么是定时器 &#xff08;1&#xff09;stm32定时器&#xff0c;是存在于stm32单片机中的一个外设。stm32共有八个定时器&#xff0c;两个高级定时器&#xff08;TIM1、TIM8&#xff09;&#xff0c;四个通用定时器&#xff08;TIM2、TIM3、TIM4、TIM5&#xff…

Java中的ArrayList集合

特点&#xff1a; ArrayList中的一些方法&#xff1a; 1、add(Object element):向集合的末尾添加元素 add(int index,Object element):在列表的指定位置&#xff08;从0开始&#xff09;插入指定元素 2、size():返回列表的中的元素个数 3、get(int index):返回下标为index位置的…

基于昇腾AI 使用AscendCL实现垃圾分类和视频物体分类应用

现如今&#xff0c;人工智能迅猛发展&#xff0c;AI赋能产业发展的速度正在加快&#xff0c;“AI”的需求蜂拥而来&#xff0c;但AI应用快速落地的过程中仍存在很大的挑战&#xff1a;向下需要适配的硬件&#xff0c;向上需要完善的技术支持&#xff0c;两者缺一不可。 基于此&…

SQL中的锁

一、概述 介绍 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资(CPU、RAM、I/0)的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&#xff0c;锁冲…

02-JVM学习记录-运行时数据区

二、运行时数据区 每个JVM只有一个Runtime实例&#xff0c;只有一个运行时数据区。 虚拟机栈、堆、方法区最重要 方法区和堆与虚拟机的生命周期相同&#xff08;随虚拟机启动而创建&#xff0c;虚拟机退出而销毁&#xff09;&#xff0c;程序计数器、虚拟机栈、本地方法栈生命…

JavaScript云LIS系统概述 前端框架JQuery+EasyUI+Bootstrap医院云HIS系统源码 开箱即用

云LIS系统概述JavaScript前端框架JQueryEasyUIBootstrap医院云HIS系统源码 开箱即用 云LIS&#xff08;云实验室信息管理系统&#xff09;是一种结合了计算机网络化信息系统的技术&#xff0c;它无缝嵌入到云HIS&#xff08;医院信息系统&#xff09;中&#xff0c;用于连…

wps/word中字体安装教程

问题&#xff1a;下载的字体怎么导入wps/word wps或word中没有相应字体&#xff0c;怎么导入。其实方法很简单。 Step 1&#xff1a;下载字体 首先&#xff0c;在网上搜索自己喜欢的字体&#xff0c;然后下载到本地。字体的格式通常是.ttf 下面是我网上找的字体&#xff08…

Vue 3 路由机制详解与实践

一、路由的理解 路由是指导用户界面导航的一种机制。它通过映射 URL 到应用程序的不同视图组件来实现页面间的切换和导航。 二、路由基本切换效果 路由基本切换效果指的是当用户在应用程序中进行页面导航时&#xff0c;通过路由可以实现页面的切换&#xff0c;从而展示不同的…

[Flutter3] 记录Dio的简单封装(一)

文章目录 效果使用ResponseEntity类DioManager封装_onResponse / _onDioException 的设计Response的处理catch处理 效果 请求成功/失败/异常的日志输出效果 成功: 失败:500 失败:404 网络异常: 使用 举个使用的例子, 在调用 DioManager的时候, 直接通过返回值的状态, 来…

ESP32开发WebSocket报错TRANSPORT_WS: Sec-WebSocket-Accept not found

我的芯片是ESP32-S3&#xff0c;用ESP-IDF框架进行开发的时候&#xff0c;用官方的WebSocket的example创建了项目。然后把WebSocket连接uri替换为自己的服务器后&#xff0c;运行到esp_websocket_client_start开始连接后&#xff0c;直接报错&#xff1a; E (10615) TRANSPORT…

网络爬虫之爬虫原理

** 爬虫概述 Python网络爬虫是利用Python编程语言编写的程序&#xff0c;通过互联网爬取特定网站的信息&#xff0c;并将其保存到本地计算机或数据库中。 """ 批量爬取各城市房价走势涨幅top10和跌幅top10 """ ​ from lxml import etree impor…

AJAX——黑马头条-数据管理平台项目

1.项目介绍 功能&#xff1a; 登录和权限判断查看文章内容列表&#xff08;筛选&#xff0c;分页&#xff09;编辑文章&#xff08;数据回显&#xff09;删除文章发布文章&#xff08;图片上传&#xff0c;富文本编辑器&#xff09; 2.项目准备 技术&#xff1a; 基于Bootst…

【韩国】UE5的MetaHuman确实可以导入Blender进行编辑。

UE5的MetaHuman确实可以导入Blender进行编辑。根据网络上的信息&#xff0c;你可以将MetaHuman模型导出为FBX文件&#xff0c;然后在Blender中进行修改。修改完成后&#xff0c;你可以将其重新导入到Unreal Engine 5中4。请注意&#xff0c;当你在Blender中编辑模型时&#xff…

第12章 最佳的UI体验——Material Design实战

第12章 最佳的UI体验——Material Design实战 其实长久以来&#xff0c;大多数人都认为Android系统的UI并不算美观&#xff0c;至少没有iOS系统的美观。以至于很多IT公司在进行应用界面设计的时候&#xff0c;为了保证双平台的统一性&#xff0c;强制要求Android端的界面风格必…

使用Shell终端访问Linux

一、实验目的 1、熟悉Linux文件系统访问命令&#xff1b; 2、熟悉常用 Linux Shell的命令&#xff1b; 3、熟悉在Linux文件系统中vi编辑器的使用&#xff1b; 4、进一步熟悉虚拟机网络连接模式与参数配置&#xff01; 二、实验内容 1、使用root帐号登陆到Linux的X-windows…

artifactory配置docker本地存储库

​一、概述 本地 Docker 存储库是我们部署和托管内部 Docker 镜像的位置。实际上&#xff0c;它是一个 Docker 注册表&#xff0c;能够托管的 Docker 镜像的集合。通过本地存储库&#xff0c;你可以保存、加载、共享和管理自己的 Docker 镜像&#xff0c;而无需依赖于外部的镜像…

API提取IP

API代理作为IP代理的一项重要业务&#xff0c;在绕开地域网络限制&#xff0c;提高作业效率等方面提供强大的技术支持。它能够帮助用户快速实现软件与软件间的交流&#xff0c;无障碍连通不同应用程序逻辑开发的系统应用。API代理用途范围广泛&#xff0c;如使用API提取代理IP、…

AcWing 1264. 动态求连续区间和 ,详细讲解线段树与树状数组(Python,篇一)

本篇博客主要介绍一下什么是线段树与树状数组&#xff0c;它们的原理与结构是怎样&#xff0c;并通过实际题型来讲解&#xff0c;篇一主要讲解线段树&#xff0c;下一篇博客讲解树状数组。 线段树与树状数组的区别和特点&#xff1a; 它们的时间复杂度都是O(nlogn) 存储方式…