问题

命令tail用来打印一个文件的最后n行。其格式为:
tail [-n] filename
其中:
-n :n表示需要打印的行数,省略时n的值为10。
filename :给定文件名。
如,命令tail –20 example.txt 表示打印文件example.txt的最后20行。

实现该程序,该程序应具有一定的错误处理能力,如能处理非法命令参数和非法文件名。

算法分析

1、如何得到需要打印的行数和文件名?

  使用命令行参数

    int main(int argc, char *argv[])

  行数 n = atoi(argv[1]+1)

  文件名为 argv[2]

打印一个文件的最后n行_文件名


2、如何得到最后n行?

方法一:使用n个节点的循环链表。

  首先创建一个空的循环链表;

  然后再依次读入文件的每一行挂在链表上,最后链表上即为最后n行。

方法二:使用一个n个元素的指针数组。
  依次读入每一行,然后循环挂到指针数组上。
    char *lineptr[N]; /存入所读入的行/
    char *line; /当前读入行/
    int i; /读入的行数/

    lineptr[i % n] = (char *)malloc(strlen(line)+1);
    strcpy(lineptr[i%n], line);

方法三:两次扫描文件。
  第一遍扫描文件,用于统计文件的总行数N;
  第二遍扫描文件时,首先跳过前面N-n行,只读取最后n行。
  如何开始第二遍扫描?
    fseek(fp, 0, SEEK_SET);

算法实现(循环链表)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define
#define
struct Tail {
char *line;
struct Tail *next;
};
int main(int argc, char *argv[ ])
{
char curline[MAXLEN],*filename;
int n = DEFLINES, i;
struct Tail *first, *ptr;
FILE *fp;
if( argc == 3 && argv[1][0] == '-') {
n = atoi(argv[1]+1); //C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。stdlib.h中声明
filename = argv[2];
}
else if( argc == 2)
filename = argv[1];
else {
fprintf(stderr, "Usage: tail [-n] filename\n");
return (1);
}

if((fp = fopen(filename, "r")) == NULL){
fprintf(stderr, " Cann't open file: %s !\n", filename);
return (-1);
}
first = ptr = (struct Tail *)malloc(sizeof ( struct Tail));
first->line = NULL;
for(i=1; i<n; i++){
ptr->next = (struct Tail *)malloc(sizeof ( struct Tail));
ptr = ptr->next;
ptr->line = NULL;
}
ptr->next = first;
ptr = first;

while(fgets(curline, MAXLEN, fp) != NULL){
if(ptr->line != NULL)
free(ptr->line);
ptr->line = (char *) malloc ( strlen(curline)+1);
strcpy(ptr->line, curline);
ptr = ptr->next;
}
for(i=0; i<n; i++) {
if(ptr->line != NULL)
printf("%s",ptr->line);
ptr = ptr->next;
}
fclose(fp);
return 0;
}