我正在开发tail函数的实现,我只应该使用read()
,write()
和lseek()
进行I / O,到目前为止,我有这个:
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)) < 0)
{
perror("read:");
}
else if(rState == 0) break;
else
{
if(pos == -1)
{
pos = lseek(fileDesc, 0, SEEK_END);
}
pos--;
pos=lseek(fileDesc, pos, SEEK_SET);
if (c == '\n')
{
lineCount++;
}
charCount++;
}
}
if (lineCount >= 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
答案 0 :(得分:2)
第一期:
如果你在这里阅读换行符......
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-condition仍为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(...);
等。此外,这不需要特定于系统的功能。
更好的方法是按块读取文件向后块并对这些块进行操作,但这需要更多的编码工作。
对于Unix,您还可以mmap()
整个文件,并在内存中向后搜索换行符。