OSTEP Projects:Unix Utilities

本文将介绍操作系统导论(Operating Systems: Three Easy Pieces)作者所开源的操作系统相关课程项目 的 Unix Utilities 部分,包含个人的代码实现和设计思路。

wcat

思路

要实现一个 wcat 命令,打印从文件中读取到的所有字符。

编写一个 for 循环遍历所有的参数(需要读取的文件的路径),打开该文件,依照 README 中的提示使用 fgets() 每次读取一行,并将读取到的字符串打印到标准输出即可。

代码

#include <stdio.h>
#include <stdlib.h>#define BUF_SIZE 1024int main(int argc, char* argv[]) {char buffer[BUF_SIZE];for (int i = 1; i < argc; ++i) {FILE* fp = fopen(argv[i], "r");if (fp == NULL) {printf("wcat: cannot open file\n");exit(1);}while (fgets(buffer, sizeof(buffer), fp)) {printf("%s", buffer);}fclose(fp);}return 0;
}

wgrep

思路

要实现一个 wgrep 命令,进行字符串匹配。

根据 README 中的提示,本题测试样例中一行的字符可能会很长,因此建议使用 getline() 这类动态分配内存的函数(无需预先指定缓冲区大小)。这里要求当只有一个参数 term 时,从标准输入中读取字符串,读取方式与从文件中读取一致,区别在于文件流参数的不同:从文件中读取为调用 fopen() 返回的指针,而从标准输入读取为 stdin

每次读取一行字符串后,需要判断该字符串中是否存在指定的子串 term,这就回到了经典的字符串匹配的问题上。为了代码编写的方便,这里我使用的是最简单的朴素字符串匹配算法,当然也可以使用有限自动机、KMP 算法、Boyer-Moore 算法等更为高效的算法,值得注意的是,Unix 系统的 grep 命令使用的正是 Boyer-Moore 算法。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 朴素字符串匹配算法
int match(char term[], size_t m, char buffer[], size_t n) {if (n < m) return 0;for (int i = 0; i < n - m; ++i) {int flag = 1;for (int j = 0; j < m; ++j) {if (term[j] != buffer[i + j]) {flag = 0;break;}}if (flag) return 1;}return 0;
}int main(int argc, char* argv[]) {if (argc <= 1) {printf("wgrep: searchterm [file ...]\n");exit(1);}char* term = argv[1];size_t m = strlen(term);char* buffer = NULL;size_t sz = 0;ssize_t n = 0;if (argc == 2) { // 从标准输入中读取while ((n = getline(&buffer, &sz, stdin)) != -1) {if (match(term, m, buffer, (size_t)n)) {printf("%s", buffer);}}}else {for (int i = 2; i < argc; ++i) {FILE* fp = fopen(argv[i], "r");if (fp == NULL) {printf("wgrep: cannot open file\n");exit(1);}while ((n = getline(&buffer, &sz, fp)) != -1) {// 判断字符串是否匹配if (match(term, m, buffer, (size_t)n)) {printf("%s", buffer);}}fclose(fp);}	}return 0;
}

wzip

思路

实现一个简单的压缩命令,将连续重复的字符压缩为 cnt + ch

算法的逻辑如下:

  1. 先将 ch 初始化为一个文件中不会出现的字符(例如 '\0'),cnt 初始化为 0.
  2. 遍历读取到的每次字符,若与 ch 相同,则将 cnt 加 1;若不同,则使用 fwrite() 写入标准输出,并把 ch 更新为当前字符,cnt 置为 1. 最后,在所有文件遍历完成后,再判断 cnt 是否大于 0,若大于 0,则写入。

注意,这里的所有字符均要按照规则进行压缩,包括换行符('\n'),我最开始写的时候还对换行符进行特判,以此来忽略对其进行处理,属实是多此一举了。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char* argv[]) {if (argc <= 1) {printf("wzip: file1 [file2 ...]\n");exit(1);}char* buffer = NULL;size_t sz = 0;ssize_t n = 0;char ch = '\0';int cnt = 0;for (int i = 1; i < argc; ++i) {FILE* fp = fopen(argv[i], "r");if (fp == NULL) {printf("wzip: cannot open file\n");exit(1);}while ((n = getline(&buffer, &sz, fp)) != -1) {for (int j = 0; j < n; ++j) {if (buffer[j] == ch) {++cnt;}else {if (cnt > 0) {fwrite(&cnt, sizeof(int), 1, stdout);fwrite(&ch, sizeof(char), 1, stdout);}ch = buffer[j];cnt = 1;}}}fclose(fp);}if (cnt > 0) {fwrite(&cnt, sizeof(int), 1, stdout);fwrite(&ch, sizeof(char), 1, stdout);}return 0;
}

wunzip

思路

相较于编码,解码就简单很多了。使用 fread() 每次读取一个整数 cnt 和一个字符 ch,并使用 for 循环打印 cnt 个 ch 到标准输出即可。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char* argv[]) {if (argc <= 1) {printf("wunzip: file1 [file2 ...]\n");exit(1);}int cnt = 0;char ch = '\0';size_t rc = 0;for (int i = 1; i < argc; ++i) {FILE* fp = fopen(argv[i], "rb");if (fp == NULL) {printf("wunzip: cannot open file\n");exit(1);}while (1) {if ((rc = fread(&cnt, sizeof(int), 1, fp)) < 1) {break;}if ((rc = fread(&ch, sizeof(char), 1, fp)) < 1) {break;}for (int j = 0; j < cnt; ++j) {printf("%c", ch);}}fclose(fp);}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DDD:根据maven的脚手架archetype生成ddd多模块项目目录结构

随着领域驱动的兴起&#xff0c;很多人都想学习如何进行ddd的项目开发&#xff0c;那ddd的项目结构是怎么样的&#xff1f;又是如何结合SpringBoot呢&#xff1f;那么针对这个问题&#xff0c;笔者使用maven的archetype封装一个相对通用的ddd的项目目录&#xff0c;方便一键生成…

karpathy Let‘s build GPT

1 introduction 按照karpathy的教程&#xff0c;一步步的完成transformer的构建&#xff0c;并在这个过程中&#xff0c;加深对transformer设计的理解。 karpathy推荐在进行网络设计的过程中&#xff0c;同时利用jupyter notebook进行快速测试和python进行主要的网络的构建。 …

STM32标准库SPI通信协议与W25Q64

目录 一、SPI通信 1.SPI通信简介 2.硬件电路 3.移位示意图 4.SPI基本时序图 &#xff08;1&#xff09;起始和终止 &#xff08;2&#xff09;交换一个字节 模式0&#xff1a; 模式1&#xff1a;​编辑 模式2&#xff1a;​编辑 模式3&#xff1a;​编辑 5.SPI时序 …

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

数据仓库实验三:分类规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、决策树分类规则挖掘&#xff08;1&#xff09;新建一个 Analysis Services 项目 jueceshu&#xff08;2&#xff09;建立数据源视图&#xff08;3&#xff09;建立挖掘结构 DST.dmm&#xff08;4&#xff…

43 单例模式

目录 1.什么是单例模式 2.什么是设计模式 3.特点 4.饿汉和懒汉 5.峨汉实现单例 6.懒汉实现单例 7.懒汉实现单例&#xff08;线程安全&#xff09; 8.STL容器是否线程安全 9.智能指针是否线程安全 10.其他常见的锁 11.读者写者问题 1. 什么是单例模式 单例模式是一种经典的&a…

线性数据结构-手写队列-哈希(散列)Hash

什么是hash散列&#xff1f; 哈希表的存在是为了解决能通过O(1)时间复杂度直接索引到指定元素。这是什么意思呢&#xff1f;通过我们使用数组存放元素&#xff0c;都是按照顺序存放的&#xff0c;当需要获取某个元素的时候&#xff0c;则需要对数组进行遍历&#xff0c;获取到指…

Kafka的优点和缺点,以及适用场景

Kafka作为一个开源的分布式流处理平台&#xff0c;在大数据和实时处理领域具有广泛的应用。以下是Kafka的优点、缺点以及适用场景&#xff1a; 一、Kafka的优点 高吞吐量和低延迟&#xff1a;Kafka能够处理每秒数百万条消息&#xff0c;具有极低的延迟&#xff0c;使得它非常…

【skill】onedrive的烦人问题

Onedrive的迷惑行为 安装Onedrive&#xff0c;如果勾选了同步&#xff0c;会默认把当前用户的数个文件夹&#xff08;桌面、文档、图片、下载 等等&#xff09;移动到安装时提示的那个文件夹 查看其中的一个文件的路径&#xff1a; 这样一整&#xff0c;原来的文件收到严重影…

吴恩达2022机器学习专项课程C2(高级学习算法)W1(神经网络):2.1神经元与大脑

目录 神经网络1.初始动机*2.发展历史3.深度学习*4.应用历程 生物神经元1.基本功能2.神经元的互动方式3.信号传递与思维形成4.神经网络的形成 生物神经元简化1.生物神经元的结构2.信号传递过程3.生物学术语与人工神经网络 人工神经元*1.模型简化2.人工神经网络的构建3.计算和输入…

redis 指定数据目录

redis 指定数据目录 1、redis2、redis数据目录 3、配置redis数据目录 1、redis Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中介。它支持多种类型的数据结构&#xff0…

Java与Go: 生产者消费者模型

什么是生产者消费者模型 生产者-消费者模型&#xff08;也称为生产者-消费者问题&#xff09;是一种常见的并发编程模型&#xff0c;用于处理多线程或多进程之间的协同工作。该模型涉及两个主要角色&#xff1a;生产者和消费者&#xff0c;一个次要角色&#xff1a;缓冲区。 生…

18 内核开发-内核重点数据结构学习

课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中。 课程特点&#xff1a; 1. 入门级别&…

办公数据分析利器:Excel与Power Query透视功能

数据分析利器&#xff1a;Excel与Power Query透视功能 Excel透视表和Power Query透视功能是强大的数据分析工具&#xff0c;它们使用户能够从大量数据中提取有意义的信息和趋势&#xff0c;可用于汇总、分析和可视化大量数据。 本文通过示例演示Power Query透视功能的一个小技…

Linux专栏08:Linux基本指令之压缩解压缩指令

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux基本指令之压缩解压缩指令 编号&#xff1a;08 文章目录 Linu…

在ubuntu 24.04上安装xrdp服务器(已验证可用)

上篇博客写了如何在ubuntu 24.04上安装vnc server&#xff0c;虽然它可以使用&#xff0c;但是有两个非常不好的缺点&#xff1a; 需要在主机上登录后vnc viewer才能登录。这样&#xff0c;如果还在vnc viewer上重启主机&#xff0c;然后你就不能再使用vnc viewer登录了。主机…

v-scale-screen 原理

v-scale-screen 原理 大屏项目中的适配屏幕大小缩放原理demo还原 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

Spring Boot与OpenCV:融合机器学习的智能图像与视频处理平台

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

【模板】二维前缀和

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二维前缀和板题。 二维前缀和&#xff1a;pre[i][j]a[i][j]pre[i-1][j]pre[i][j-1]-pre[i-1][j-1]; 子矩阵 左上角为(x1,y1) 右下角(x2,y2…

PG控制文件的管理与重建

一.控制文件位置与大小 逻辑位置&#xff1a;pgpobal 表空间中 物理位置&#xff1a;$PGDATA/global/pg_control --pg_global表空间的物理位置就在$PGDATA/global文件夹下 物理大小&#xff1a;8K 二.存放的内容 1.数据库初始化的时候生成的永久化参数&#xff0c;无法更改…