/* 1、可以以命令行参数形式顺序浏览多个文件 2、支持: q退出, 回车键输出一行,空格键输出正页 3、带百分比等提示功能 4、目前无其他功能,有待改进 */ #include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <termios.h> #define NUMLINE 47 #define NUMCHAR 1000 struct termios old; /* 恢复终端设置 */ void recover_terminal_attr(void) { tcsetattr(fileno(stdin), TCSANOW, &old); } /* 修改终端,为使其接收单个字符,而不需要回车键 */ void set_terminal_attr(void) { struct termios new; /*保存旧终端,以备程序结束时恢复*/ tcgetattr(fileno(stdin), &old); new = old; new.c_lflag &= ~(ECHO|ICANON); //一次读取一个字符 new.c_cc[VMIN] = 1; //Timeout in deciseconds for noncanonical read(TIME) new.c_cc[VTIME] = 0; tcsetattr(fileno(stdin), TCSAFLUSH, &new); atexit(recover_terminal_attr); } /* 输入出 “more01”及百分比提示字样 文件结尾返回1, 否则返回0 */ int print_more(FILE *fp, long end) { long cur; cur = ftell(fp); if (cur != end) { /*反白输出字符*/ printf("\033[7m --more01--(%d%%)\033[m", (int)(cur*100/end)); return 0; } return 1; } /* 获得文件长度,并返回,单位字节。 */ long getsize(FILE *fp) { long cur, end; cur = ftell(fp); fseek(fp, 0, SEEK_END); end = ftell(fp); fseek(fp, cur, SEEK_SET); return end; } /* 以行为单位输出fp文件内容,size为文件大小,num为输出行数 文件结尾返回1, 否则返回0 */ int print(FILE *fp, int size, int num) { char buf[NUMCHAR]; int i, wnum; /*擦除提示信息, \r return本行起始位置 " "输出空格将之前信息覆盖*/ printf("\r \r"); for (i = 0; i < num;i++) { if (fgets(buf, NUMCHAR, fp) == NULL) return 1; wnum = fputs(buf, stdout); if (wnum == EOF) exit(4); } if (print_more(fp, size) == 0) return 0; else return 1; } int main(int argc, char* argv[]) { FILE *fp; char buf[NUMCHAR], cmd; int num, size, flag, n; long end; set_terminal_attr(); for (n = 1, num = NUMLINE; n < argc; n++, flag = 0) { fp = fopen(argv[n], "r"); if (fp == NULL) { perror(argv[n]); continue; } else if (n != 1) { printf("\033[7m --more01--(Next File: %s)\033[m", argv[n]); goto getcmd; } size = getsize(fp); while (1) { flag = print(fp, size, num); getcmd: if (flag == 1 ) break; while (1) { cmd = getchar(); if (cmd == 'q' || cmd == '\n'||cmd ==32) break; } switch (cmd) { case '\n': num = 1; break; case ' ': num = NUMLINE; break; case 'q': printf("\r \r"); exit(0); } } fclose(fp); } exit(0); }
总结:
1、在程序运行期间,需要输入单个字符及实现命令的响应(不需要回车键发送)。修改终端参数,并通过atexit函数调用恢复函数,在程序退出时,恢复之前设置。
2、若在程序中调用system函数执行,shell命令等。执行程序的进程会单独创建子进程用于该命令的实现,并将结果输出到标准输出。
3、
#include <stdio.h> int fileno(FILE *fp);
文件指针作为参数,返回描述符
4、非缓冲文件I/O:open read write, 参数为指向文件指针;缓冲I/O:fopen fgets fputs(行缓冲),参数为文件描述符。
判断fgets是否接收为空:if (fgets(...) == NULL)
5、
#include <stdio.h> long ftell(FILE *fp); //Returns: current file position indicator if OK, 1L on error int fseek(FILE *fp, long offset, int whence); //Returns: 0 if OK, nonzero on error void rewind(FILE *fp);
ftell 返回当前偏移量,fseek制定当前偏移到制定位置,rewind返回到文件起始处。
6、
#include <stdio.h> void perror(const char *msg);
The perror function produces an error message on the standard error, based on the current value of errno, and returns.
It outputs the string pointed to by msg, followed by a colon and a space, followed by the error message corresponding to the value of errno, followed by a newline.
from APUE
7、magci number
In computer programming, the term magic number has multiple meanings. It could refer to one or more of the following:
A constant numerical or text value used to identify a file format or protocol; for files, see List of file signatures
Distinctive unique values that are unlikely to be mistaken for other meanings (e.g., Globally Unique Identifiers)
Unique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants
from wiki
8、调用exit函数,若不包含stdlib.h则会警告兼容问题,但可通过,加上之后就OK了。
exit 或 return返回值可通过shell内的 echo $?查看。
待解决问题:
1、若程序以一个管道或重定向作为输入,首先应该判断stdin是否为空,目前未找到判断stdin为空的方法。
2、写程序感觉能写出来,但是细节不确定,很容易被一些细节卡住。另外程序的大体思路不清晰,总是用一种自己感觉不是最优的算法在解决,等解决完了,再修改程序。
3、若以管道或重定向修改进程了的stdin,则进程无法通过终端接受键盘发送的指令(stdin已被修改),此时需要重新打开输入终端。具体可参照《Unix/Linux编程实践教程》,more02例。