使用read write和lseek打印最后10行文件或stdin

我正在研究tail函数的实现,我只应该对I / O使用read()write()lseek() ,到目前为止我有这个:

 int printFileLines(int fileDesc) { char c; int lineCount = 0, charCount = 0; int pos = 0, rState; while(pos != -1 && lineCount < 10) { if((rState = read(fileDesc, &c, 1)) = 10) lseek(fileDesc, 2, SEEK_CUR); else lseek(fileDesc, 0, SEEK_SET); char *lines = malloc(charCount - 1 * sizeof(char)); read(fileDesc, lines, charCount); lines[charCount - 1] = 10; write(STDOUT_FILENO, lines, charCount); return 0; } 

到目前为止它适用于超过10行的文件,但是当我传递少于10行的文件时它会刹车,它只打印该文件的最后一行,我无法使用stdin 。 如果有人能让我知道如何解决这个问题,那就太棒了:D

第一个问题:

如果你在这里阅读换行符……

 if(read(fileDesc, &c, 1) < 0) { perror("read:"); } 

...然后将位置直接设置到换行符之前的字符...

 pos--; pos=lseek(fileDesc, pos, SEEK_SET); 

然后linecount >= 10 (while循环终止),然后你读取的第一个char是最后一个换行之前的行的最后一个char。 换行本身也不是最后10行的一部分,所以只需从当前流位置跳过两个字符:

 if (linecount >= 10) lseek(fileDesc, 2, SEEK_CUR); 

对于第二个问题:

让我们假设流偏移量已到达流的开头:

 pos--; pos=lseek(fileDesc, pos, SEEK_SET); // pos is now 0 

while条件仍为TRUE:

 while(pos != -1 && lineCount < 10) 

现在读一个字符。 在此之后,文件偏移量为1 (第二个字符):

 if(read(fileDesc, &c, 1) < 0) { perror("read:"); } 

在这里,pos下降到-1,lseek将失败

 pos--; pos=lseek(fileDesc, pos, SEEK_SET); 

由于lseek失败,文件中的位置现在是第二个字符,因此缺少第一个字符。 如果在while循环后pos == -1 ,则通过将文件偏移重置为文件的开头来解决此问题:

 if (linecount >= 10) lseek(fileDesc, 2, SEEK_CUR); else lseek(fileDesc, 0, SEEK_SET); 

性能:

这需要很多系统调用。 一个简单的增强是使用缓冲的f *函数:

 FILE *f = fdopen(fileDesc, "r"); fseek(...); fgetc(...); 

此外,这不需要特定于系统的function。

更好的方法是按块读取文件向后块并对这些块进行操作,但这需要更多的编码工作。

对于Unix,您还可以mmap()整个文件并在内存中向后搜索换行符。