面试时曾2次被问到如何实现输出文本文件(可能非常非常大)后n行数据的问题,这个功能基本是Linux下tail命令的简化版,随意写了下。
因为涉及到不同系统及编码下回车换行的问题,情境比较复杂,在这里暂时未处理,但是基本处理思想是一样的。
代码目前在linux下运行正确,在windows下输出异常,就是因为字符编码(主要是回车换行)问题 >_> 仅供参考。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//#define DEBUG
#define MAX_BUF_SIZE 4096
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int main(int argc, char *argv[])
{
#ifndef DEBUG
if (argc != 3)
{
printf("usage: %s number_of_line(s) file\n", argv[0]);
exit(1);
}
int nline = atoi(argv[1]);
const char *filename = argv[2];
#else
int nline = 4;
const char *filename = "test.txt";
#endif
int nl = 0;
FILE *fp;
int buflen;
char buf[MAX_BUF_SIZE];
int fsz = 0, pos = 0;
int nblock = 0;
bool tobreak = false;
if ((fp = fopen(filename, "r")) == NULL)
{
printf("open file \"%s\" error\n", filename);
exit(2);
}
fseek(fp, 0, SEEK_END);
fsz = ftell(fp);
nblock = ceil(fsz / (float)MAX_BUF_SIZE);
for (int k = 0; k < nblock; k++)
{
int blocksz = MIN(fsz - k * MAX_BUF_SIZE, MAX_BUF_SIZE);
int offset = -MIN(fsz, (k + 1) * MAX_BUF_SIZE);
fseek(fp, offset, SEEK_END);
buflen = fread(buf, 1, blocksz, fp);
for (int i = buflen - 1; i >= 0; i--)
{
if (buf[i] == '\n')
{
nl++;
//if (nl == nline)
if (nl > nline)
{
pos += buflen - i - 1;
tobreak = true;
break;
}
}
}
if (tobreak) break;
pos += blocksz;
}
if (tobreak)
fseek(fp, -pos, SEEK_END);
else
fseek(fp, 0, SEEK_SET);
while ((buflen = fread(buf, 1, MAX_BUF_SIZE - 1, fp)) > 0)
{
buf[buflen] = '\0';
printf("%s", buf);
}
//printf("\n");
fclose(fp);
return 0;
}