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 1024

int 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.mfbz.cn/a/593209.html

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

相关文章

【银角大王——Django课程——用户表的基本操作2】

用户表的基本操作2 编辑用户按钮删除按钮入职日期——不显示时分&#xff0c;只显示年月日——使用DataField函数不使用DateTimeField修改models记得重新执行命令&#xff0c;更新数据库结构修改前修改后 编辑用户按钮 点击编辑&#xff0c;跳转到编辑页面&#xff08;将编辑的…

CrossOver支持的软件多吗 CrossOver支持软件列表 crossover兼容性查询

如果你是一个喜欢在Mac上工作的用户&#xff0c;但又不想放弃一些Windows上的优秀软件&#xff0c;那么可以考虑使用一些兼容工具来运行Windows程序。其中&#xff0c;CrossOver就是一款功能强大且受欢迎的兼容工具。那么&#xff0c;CrossOver到底能支持哪些Windows软件呢&…

JVM笔记2--垃圾收集算法

1、如何确认哪些对象“已死” 在上一篇文章中介绍到Java内存运行时的各个区域。其中程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生&#xff0c;随线程而灭&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊的执行着入栈和出栈操作。每个栈帧中分配多少内存基本上…

VMvare如何更改虚拟机内共享文件夹的挂载点

更改虚拟机内共享文件夹的路径 进入目录 /etc/init.d ,并找到vmware-tools文件 里面有配置项 vmhgfs_mnt"/mnt/hgfs" 将引号内的内容更改为你需要挂载的路径,重启即可 注意挂载的路径不能是 “/”&#xff0c;必须根目录下的某个文件夹&#xff0c;或者其子文件夹 …

定时器编程前配置和控制LED隔一秒亮灭

1.配置定时器 0 工作模式16位计时 2.给初值&#xff0c;定一个10ms出来 3.开始计时

环形链表的判断方法与原理证明

&#xff08;题目来源&#xff1a;力扣&#xff09; 一.判读一个链表是否是环形链表 题目&#xff1a; 解答&#xff1a; 方法&#xff1a;快慢指针法 内容&#xff1a;分别定义快慢指针&#xff08;fast和slow&#xff09;&#xff0c;快指针一次走两步&#xff0c;慢指…

物体检测:如何检测小物体?

原文地址&#xff1a;https://medium.com/voxel51/how-to-detect-small-objects-cfa569b4d5bd 2024 年 4 月 22 日 物体检测是计算机视觉的基本任务之一。在高层次上&#xff0c;它涉及预测图像中物体的位置和类别。最先进的&#xff08;SOTA&#xff09;深度学习模型&#x…

3031087 -“无数据”:物料不显示在 MRP 应用中

症状 使用其中一个 MRP 应用&#xff08;监控物料覆盖范围、管理物料覆盖范围、监控外部需求等&#xff09;时无法找到物料。 用户在搜索过滤器时会收到错误消息“无数据”。 “本 KBA 中的图像/数据来自 SAP 内部系统、示例数据或演示系统。任何与真实数据相似的都是完全巧…

Apache反代理Tomcat项目,分离应用服务器和WEB服务器

项目的原理是使用单独的机器做应用服务器&#xff0c;再用单独的机器做WEB服务器&#xff0c;从网络需要访问我们的应用的话&#xff0c;就会先经过我们的WEB服务器&#xff0c;再到达应用程序&#xff0c;这样子的好处是我们可以保护应用程序的机器位置&#xff0c;同时还可以…

R语言中,查看经安装的包,查看已经加载的包,查看特定包是否已经安装,安装包,更新包,卸载包

创建于&#xff1a;2024.5.4 R语言中&#xff0c;查看经安装的包&#xff0c;查看已经加载的包&#xff0c;查看特定包是否已经安装&#xff0c;安装包&#xff0c;更新包&#xff0c;卸载包 文章目录 1. 查看经安装的包2. 查看已经加载的包3. 查看特定包是否已经安装4. 安装包…

java发送请求-http和https

http和https区别 1、http是网络传输超文本协议&#xff0c;client---- http------ server 2、httpshttpssl证书&#xff0c;让网络传输更安全 &#xff0c;client---- httpssl------ server 3、ssl证书是需要客户端认可的&#xff0c;注意官方证书和jdk生成的证书的用户来使…

实现批量自动文本标注(输出标签)代码复现

一&#xff1a;项目地址&#xff1a; IDEA-Research/Grounded-Segment-Anything: Grounded-SAM: Marrying Grounding-DINO with Segment Anything & Stable Diffusion & Recognize Anything - Automatically Detect , Segment and Generate Anything (github.com) 二…

3.SpringSecurity基本原理

SpringSecurity本质是一个过滤器链。十多个过滤器构成一个过滤器链。 这些过滤器在项目启动就会进行加载。每个过滤器执行放行操作才会执行下一个过滤器。 常见过滤器 FilterSecurityInterceptor 是一个方法级的权限过滤器&#xff0c;基本位于过滤器链的最底部。 Excepti…

内核workqueue框架

workqueue驱动的底半部实现方式之一就是工作队列&#xff0c;作为内核的标准模块&#xff0c;它的使用接口也非常简单&#xff0c;schedule_work或者指定派生到哪个cpu的schedule_work_on。 还有部分场景会使用自定义的workqueue&#xff0c;这种情况会直接调用queue_work和qu…

sql 中having和where区别

where 是用于筛选表中满足条件的行&#xff0c;不可以和聚类函数一起使用 having 是用于筛选满足条件的组 &#xff0c;可与聚合函数一起使用 所以having语句中不能使用select中定义的名字

QT:QT与操作系统

文章目录 信号槽与事件 信号槽与事件 在之前的信号槽中&#xff0c;已经有了一个基本的认识&#xff0c;那么对于QT中事件的理解其实就非常的类似&#xff0c;当用户进行某种操作的时候&#xff0c;就会触发事件&#xff0c;去执行一些对应的方法 QT对于事件又进行了封装&…

Lucene从入门到精通

**************************************************************************************************************************************************************************** 1、概述 【1】入门&#xff1a;作用、有点与缺点 【2】应用&#xff1a;索引、搜索、fie…

【软件开发规范篇】JAVA后端开发编程规范

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

Python数据分析案例43——Fama-French回归模型资产定价(三因子/五因子)

案例背景 最近看到要做三因子模型的同学还挺多的&#xff0c;就是所谓的Fama-French回归模型&#xff0c;也就是CAMP资本资产定价模型的升级版&#xff0c;然后后面还升级为了五因子模型。 看起来眼花缭乱&#xff0c;其实抛开金融资产定价的背景&#xff0c;从机器学习角度来…

04_jvm性能调优_并行收集器介绍

并行收集器&#xff08;此处也称为吞吐量收集器&#xff09;是类似于串行收集器的分代收集器。串行和并行收集器之间的主要区别在于并行收集器具有多个线程&#xff0c;用于加速垃圾回收过程。 通过命令行选项-XX:UseParallelGC 可启用并行收集器。默认情况下&#xff0c;使用…