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

标签 c printing lines lseek

我正在研究 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

最佳答案

第一期:

如果您在这里读到换行符...

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

...然后将位置直接设置为换行符前面的字符...

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

然后 linecount>= 10 (while 循环终止),那么您读取的第一个字符是最后一个换行符之前的行的最后一个字符。换行符本身也不属于最后 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(...);

等等。此外,这不需要系统特定的功能。

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

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

关于c - 使用 read write 和 lseek 打印文件或 stdin 的最后 10 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34861771/

相关文章:

python - 如何测试Python中一条线是否与任意数量的其他未知线相交?

r-ggplot2 : connecting points in polar coordinates with a straight line

c - 位摆弄黑客

c - 以编程方式请求 root 访问权限

c# - 如何使用 EPPlus 创建 A4 纸的 Excel 文件

javascript - 使线条在 Canvas 内闪烁

c - *( (int *)ptr+1) 的含义

c - scanf 不等待输入

javascript - 使用 page-break-inside : avoid wth css or javascript or from browser? 时在分页符时添加页眉

r - 在 R 中的同一行打印字符串和变量内容