课后练习:C语言实现Linux命令——od
课后练习:C语言实现Linux命令——od
————————CONTENTS————————
题目详情与分析
题目:复习c文件处理内容,编写myod.c,用myod XXX实现Linux下od -tc -tx1 XXX的功能。
在Linux下使用od -tc -tx1 XXX
的效果为:
下面对od命令进行简单的分析:
1、功能
od命令用于将指定文件内容以八进制、十进制、十六进制、浮点格式或ASCII编码字符方式显示,通常用于显示或查看文件中不能直接显示在终端的字符。od命令系统默认的显示方式是八进制,名称源于Octal Dump。
常见的文件为文本文件和二进制文件。od命令主要用来查看保存在二进制文件中的值,按照指定格式解释文件中的数据并输出,不管是IEEE754格式的浮点数还是ASCII码,od命令都能按照需求输出它们的值。
2、格式
od [<选项><参数>] [<文件名>]
3、常用的命令选项
-t,--format=TYPE:指定输出格式,格式包括a、c、d、f、o、u和x,各含义如下:
- a:具名字符;
- c:ASCII字符或者反斜杠;
- d[SIZE]:十进制,正负数都包含,SIZE字节组成一个十进制整数;
- f[SIZE]:浮点,SIZE字节组成一个浮点数;
- o[SIZE]:八进制,SIZE字节组成一个八进制数;
- u[SIZE]:无符号十进制,只包含正数,SIZE字节组成一个无符号十进制整数;
- x[SIZE]:十六进制,SIZE字节为单位以十六进制输出,即输出时一列包含SIZE字节。
例如:od -tx testfile
表示以十六进制输出,默认以四字节为一组(一列)显示;od -tx1 testfile
表示以十六进制输出,每列输出一字节。
设计思路
题目的要求是,实现od -tc -tx1 XXX
命令,-tc表示输出ASCII字符,-tx1表示以十六进制输出,每组输出一字节。
观察到od命令每行输出16个字节,所以需要在读取文件的过程中加一个循环,每读取16个字节后输出并换行。-tc用格式化输出%c
,-tx用格式化输出%x
。
这样一来,基本的框架就有了。伪代码大概是这个样子的:
int main()
{
从命令行参数读入文件;
if(参数为“-tc -tx1”){
while(未到达文件末尾){
将文件以字节为单位读入一个定长数组(16字节);
分别以%c和%x格式依次输出数组中的内容;
}
}
}
根据这个流程设计程序,再调整一下格式,就能得到基本的输出了。关键代码如下:
while(fgets(ch,17,file)!=NULL){
for(i=0;i<16;i++)
{
if(ch[i]=='\0')
break;
printf("%x ",ch[i]);
}
printf("\n");
for(i=0;i<16;i++)
{
if(ch[i]=='\0')
break;
putchar(' ');
putchar(' ');
printf("%c", ch[i]);
putchar(' ');
}
printf("\n");
}
遇到的问题及解决
『问题一』:以上程序具有很大的局限性,只能得到od -tc -tx1 XXX
这个命令的输出。如何同时实现“-tc”、“-tx1”、“-td1”、“-to1”等选项的功能呢?
『解决』:
这需要对我们输入的命令行参数进行判断,根据判断结果执行不同的操作。这样看来,应该让不同的功能由不同的函数实现,每次判断并调用即可。也有助于将来程序功能的拓展。
需要注意的是,命令行参数argv[0]为文件的地址信息,使用
int main(int argc,char *argv[])
{
for(int i = 0; i < strlen(argv[0]); i++){
printf("%c", argv[0][i]);
}
return 0;
}
可以看到输出结果:
所以,输入的参数是从argv[1]开始的。
由此,可以拓展“-tc”、“-tx1”、“-td1”、“-to1”等功能,只是最后的格式化输出有所区别。
- “-tc”:printf("%c", ch[i]);
- “-tx1”:printf("%x ",ch[i]);
- “-td1”:printf("%d ",ch[i]);
- “-to1”:printf("%o ",ch[i]);
『问题二』:为了实现功能,以上分别将–tx1、–td1、–to1与–tc等写成了不同的函数。比如,如果检测到命令行输入了–tc,就先在控制台打印全部ASCII字符。但注意到Linux命令是一行ASCII字符,一行进制相间输出的,所以如果与此同时检测到传入了–tx1参数,就要移动光标至(0,1),再调用输出进制的函数,将其全部输出。如何做到呢?
『解决』:
查阅了相关资料,了解到可以利用 windows.h
定义的 SetConsoleCursorPosition()
来实现对光标的控制。具体方法为:
- ①定义一个COORD类型的结构体;
- ②设置结构体中x和y的值,即光标的位置;
- ③调用SetConsoleCursorPosition()函数,完成设置。
//设置光标的位置
void gotoxy(int x,int y)
{
COORD c;
c.X=x-1;
c.Y=y-1;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
}
C语言实现控制台中光标随意移动和C语言编程——控制台程序光标控制两篇博客中有详尽的介绍,可以实现一个类似于终端的命令行程序。
『问题三』:当输入的第一个参数为-tc
时,if(argv[1] == "-tc"))
始终判断为假。
『解决』:第一次写的时候,if(argv[1] == "-tc"))
始终判断为假,打印了argv[1]
的值也的确是"-tc"
。
这是因为,字符串比较需要用专门的库函数strcmp(str1,str2)
,如果两个字符串完全相同,则返回值为0。以此来进行判断。
既然遇到这个问题,在这里就顺便复习一下strcmp()函数的特性和用法。string.h库对函数的说明如下:
函数原型:
int strcmp(const char *s1, const char *s2);
描述:比较s1和s2指向的两个字符串。如果完全匹配,则两字符串相同,否则比较首次出现不匹配的字符对。通过字符编码值比较字符。如果两个字符串相同,函数返回0;如果第一个字符串小于第二个字符串,函数返回小于0的值;如果第一个字符串大于第二个字符串,函数返回大于0的值。
『问题四』:以上输出与linux下od的输出还有一些细微的差别,比如缺少每行最前面的“0000020”等计数标识;上一行的ASCII字符与下一行的进制没有对齐等等,如何调整格式,严格仿照linux下的形式输出呢?
『解决』:
- 如何输出“0000020”?
- 通过观察发现,每行开头这串数字为八进制,数值为在本行之前的字符数。所以,只需在prinf()函数中格式化输出printf("%07o",参数)即可。
- 如何使同一个字符的ASCII字符与对应的进制上下对齐?
- printf()的修饰符中,数字表示最小字段宽度。如果该字段不能容纳待打印的数字或字符串,系统会用更宽的字段。所以,例如printf("4d%",参数)即可打印宽度为4的十进制数。
- 发现程序无法显示“\n”的ASCII字符,但linux的od命令可以,怎么修改?
- 对读取的字符进行判断,如遇到“\n”,手动输出。要注意使用转义字符,即printf("\n")。
待实现的设想与思考
『设想一』:
由于虚拟机出了点问题,还在尝试修复,所以这个程序起初是在Windows下写的。当试图将此程序从Windows下移植到Linux下时,出现了错误提示:
查询资料了解到,windows.h包含windows下的所有API函数、常量、结构体的声明等等,Ubuntu下没有windows内核dll的支持,无法移植。那么Ubuntu有没有什么功能类似的函数呢?
『思考』:
在Linux下curses函数库和关于curse.h终端图形库的学习两篇博客中学习到,curses.h函数库的int move(int new_y, int new_x);
可以实现光标位置的移动,功能类似于windows.h。
除此之外,curses函数库还可以实现清除屏幕、移动和更新窗口、彩色显示等等功能,目前正在参考娄老师分享的《UNIX.Linux下curses库开发指南》进行学习。
学习反思与感悟
在用C语言模拟od命令时,起初想法很简单,仅仅完成od -tc -tx1 XXX的功能即可。实现之后,又将目光放在了功能拓展上,因为毕竟od的选项不同,得到的结果也不一样。除了-tx1,其余如-td1、-to1等选项的功能如何一并实现呢?所以我想到了将不同的功能封装成不同的函数,判断并调用即可。
但实现过程中又遇到了很多问题:注意到Linux命令的执行结果是ASCII字符与进制相间输出的,如果检测到输入的参数为-tc和-tx1,系统先调用-tc对应的函数将ASCII字符全部打印,那么再打印十六进制时,就需要将光标移动到开头,再调用-tx1对应的函数。
为了解决这个问题,我找到了windows.h和conio.h库函数,实现了我的设想;但Linux下不支持该库函数,所以又在Ubuntu安装了curses函数库。在安装curses函数库的过程中,也出现了各种错误提示,例如:Temporary failure resolving 'us.archive.ubuntu.com'
,即暂时不能解析域名“us.archive.ubuntu.com”;E: 无法获得锁 /var/lib/apt/lists/lock - open (11 资源临时不可用)
之类的问题,不过最终都顺利解决了。目前还在修改调试程序,争取在Linux下运行成功。
本来挺简单的问题,却在实现的过程中频遇坎坷......貌似我的确把问题复杂化了(无奈.jpg)。不管怎样,虽然探索的过程很艰辛,但收获不小。感谢娄老师的建议!接下来会继续完善程序的~
附1:myod.c「1.0版本」(Windows下)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <conio.h>
void tc(FILE *file);
void tx(FILE *file);
void to(FILE *file);
void td(FILE *file);
int main(int argc,char *argv[])
{
if(strcmp(argv[1], "-tc")==0){
FILE *file=fopen(argv[3],"r");
tc(file);
}
if(strcmp(argv[2], "-tx1")==0){
FILE *file=fopen(argv[3],"r");
tx(file);
}
else if(strcmp(argv[2], "-to1")==0){
FILE *file=fopen(argv[3],"r");
to(file);
}
else if(strcmp(argv[2], "-td1")==0){
FILE *file=fopen(argv[3],"r");
td(file);
}
return 0;
}
void tc(FILE *file)
{
char ch[18];
int i=0,j=0;
while(fgets(ch,17,file)!=NULL){
printf("%07o",16*j);
j++;
for(i=0;i<16;i++)
{
if(ch[i]=='\n')
{ i++;
putchar(' ');
printf("\\n");
}
if(ch[i]=='\0')
break;
putchar(' ');
putchar(' ');
printf("%c", ch[i]);
putchar(' ');
}
printf("\n\n");
}
printf("%07o",16*(j-1)+i);
fclose(file);
}
void tx(FILE *file)
{
//Initialize the coordinates
COORD coord = {0, 1};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
char ch[18];
int i;
while(fgets(ch,17,file)!=NULL){
printf(" ");
for(i=0;i<16;i++)
{
if(ch[i]=='\n')
{ i++;
printf("%3x ",'\n');
}
if(ch[i]=='\0')
break;
printf("%3x ",ch[i]);
}
printf("\n\n");
}
fclose(file);
}
void to(FILE *file)
{
//Initialize the coordinates
COORD coord = {0, 1};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
char ch[18];
int i;
while(fgets(ch,17,file)!=NULL){
printf(" ");
for(i=0;i<16;i++)
{
if(ch[i]=='\n')
{ i++;
printf("%03o ",'\n');}
if(ch[i]=='\0')
break;
printf("%03o ",ch[i]);
}
printf("\n\n");
}
fclose(file);
}
void td(FILE *file)
{
//Initialize the coordinates
COORD coord = {0, 1};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
char ch[18];
int i;
while(fgets(ch,17,file)!=NULL){
printf(" ");
for(i=0;i<16;i++)
{
if(ch[i]=='\n')
{ i++;
printf("%3d ",'\n');
}
if(ch[i]=='\0')
break;
printf("%3d ",ch[i]);
}
printf("\n\n");
}
fclose(file);
}
运行结果:
附2:参考资料
- Linux命令(2)——od命令
- Linux下curses函数库
- C语言实现控制台中光标随意移动
- C语言编程——控制台程序光标控制
- E: 无法获得锁 /var/lib/apt/lists/lock - open (11: 资源暂时不可用)
- 关于curse.h终端图形库的学习
- Ubuntu apt-get install 问题: Could not resolve 'cn.archive.ubuntu.com'
- 文件操作 及文件指针移动 rewind ftell
——————TO BE CONTINUED...——————
课后练习:C语言实现Linux命令——od的更多相关文章
- C语言实现Linux命令——od
C语言实现Linux命令--od 实现要求: 复习c文件处理内容 编写myod.c 用myod XXX实现Linux下od -tx -tc XXX的功能 main与其他分开,制作静态库和动态库 编写M ...
- 对Linux命令od -tc -tx1的C语言程序实现myod-优化版
导语 自编od C语言实现版名为myod 上个星期有一个初代版,链接- myod原版 这星期的课上要求实现myod-系统调用版本,要求如下 1 参考教材第十章内容 2 用Linux IO相关系统调用编 ...
- shell脚本语言与linux命令的联系与区别
使用linux肯定是要会使用命令的,就算提供有用户界面,绝大部分功能还是要通过命令行去操作的.而shell脚本语言也是运行在linux上的脚本语言,对于服务器运维人员也是几乎必须要掌握的.而shell ...
- Linux命令——od
参考:Linux OD Command Tutorial for Beginners (6 Examples) 简介 查看普通文本文件,可以使用cat.head.tail.tac.less.more等 ...
- C语言编程实现Linux命令——who
C语言编程实现Linux命令--who 实践分析过程 who命令是查询当前登录的每个用户,它的输出包括用户名.终端类型.登录日期及远程主机,在Linux系统中输入who命令输出如下: 我们先man一下 ...
- [转]Linux之od命令
转自:http://os.51cto.com/art/200912/173136.htm 随着计算机飞速的发展,很多人开始学习Linux,怎样才能学好Linux,一定要学好Linux的命令.学习Lin ...
- Linux命令之dot - 绘制DOT语言脚本描述的图形
本文链接:http://codingstandards.iteye.com/blog/840055 用途说明 Graphviz (Graph Visualization Software的缩写)是一个 ...
- 必备Linux命令和C语言基础
每一个学习嵌入式单片机的伙伴我相信对于这两个都不陌生,这毕竟是嵌入式单片机的生存之道 所有基础还是要打牢的 有句老话说的好基础不牢地动山摇 下面看下系统的资料吧 希望能对大家有所帮 ...
- 20155212 C语言实现linux下pwd命令的两种方法
20155212 C语言实现linux下pwd命令的两种方法 学习pwd命令 通过man pwd命令查看 pwd [OPTION],一般不加参数 -P显示当前目录的物理路径 -L显示当前目录的连接路径 ...
随机推荐
- JAVA中String类的方法(函数)总结--JAVA基础
1.concat()方法,当参数为两字符串时,可实现字符串的连接: package cn.nxl123.www; public class Test { public static void main ...
- [CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)
Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花 ...
- jdbc连接数据库并打印的简单例子
6步连接数据库: import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; impor ...
- mount挂接命令使用
挂接 操作系统 1.-t vfstype 指定文件系统的类型,通常不必指定.mount 会自动选择正确的类型.常用类型有: 光盘或光盘镜像:iso9660 DOS fat16文件系统:msdos Wi ...
- fitnesse - 框架介绍
fitnesse - 框架介绍 2017-09-29 目录: 1 fitnesse是什么?2 框架介绍3 与junit.testng比较,fitnesse教其他框架有什么优势 1 fitnesse是什 ...
- 建立简单的Hash table(哈希表)by C language
#define SIZE 1000 //定义Hash table的初始大小 struct HashArray { int key; int count; struct HashArray* next; ...
- [转]ZooKeeper的学习与应用
[转]ZooKeeper的学习与应用 http://blog.csdn.net/rengq126/article/details/7393227 1. ZooKeeper的学习与应用 1.1. 概述 ...
- 如何彻底关闭windows defender
我是一个喜欢裸奔的人,我不喜欢使用那些安全软件,什么360啊,什么毒霸啊让我深恶痛绝,就连windows自带的杀软我都不能忍啊,因为我平时喜欢找一下软件,很多的补丁和注册机,这些安全软件都会误报,所以 ...
- Python快速入门之与C语言异同
代码较长,建议使用电脑阅读本文. 10分钟入门Python 本文中使用的是Python3如果你曾经学过C语言,阅读此文,相信你能迅速发现这两种语言的异同,达到快速入门的目的.下面将开始介绍它们的异同. ...
- Linux基础命令详解
1 遍历目录 cd:change dicrectory的缩写 .或者./代表当前目录,..或../代表上一级目录,cd -代表进入上一次的目录. 2 文件和目录列表 ls:list的缩写,会显示目录下 ...