C 如何使用Unix系统调用打印文本文件的前10行?

C 如何使用Unix系统调用打印文本文件的前10行?,c,linux,unix,C,Linux,Unix,我想编写自己版本的headUnix命令,但我的程序无法运行 我试图打印文本文件的前10行,但程序打印所有行。我通过命令行参数指定文件名和要打印的行数。我只需要使用Unix系统调用,如read()、open()和close() 代码如下: #include "stdlib.h" #include "stdio.h" #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #define BUFFER

我想编写自己版本的
head
Unix命令,但我的程序无法运行

我试图打印文本文件的前10行,但程序打印所有行。我通过命令行参数指定文件名和要打印的行数。我只需要使用Unix系统调用,如
read()
open()
close()

代码如下:

#include "stdlib.h"
#include "stdio.h"
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFFERSZ 256
#define LINES 10

void fileError( char*, char* );

int main( int ac, char* args[] )
{
    char buffer[BUFFERSZ];
    int linesToRead = LINES;
    int in_fd, rd_chars;

    // check for invalid argument count
    if ( ac < 2 || ac > 3 )
    {
        printf( "usage:  head FILE [n]\n" );
        exit(1);
    }

    // check for n
    if ( ac == 3 )
        linesToRead = atoi( args[2] );

    // attempt to open the file
    if ( ( in_fd = open( args[1], O_RDONLY ) ) == -1 )
         fileError( "Cannot open ", args[1] );

    int lineCount = 0;

    //count no. of lines inside file
    while (read( in_fd, buffer, 1 ) == 1) 
    {        
        if ( *buffer == '\n' )
        {
           lineCount++;
        }
    }
    lineCount = lineCount+1;

    printf("Linecount: %i\n", lineCount);

    int Starting = 0, xline = 0;

    // xline = totallines - requiredlines
    xline = lineCount - linesToRead;
    printf("xline: %i \n\n",xline);

    if ( xline < 0 )
        xline = 0;

    // count for no. of line to print
    int printStop = lineCount - xline;
    printf("printstop: %i \n\n",printStop);

    if ( ( in_fd = open( args[1], O_RDONLY ) ) == -1 )
        fileError( "Cannot open ", args[1] );

    //read and print till required number
    while (Starting != printStop) {
        read( in_fd, buffer, BUFFERSZ );
        Starting++;    //increment starting
    }

    //read( in_fd, buffer, BUFFERSZ );
    printf("%s \n", buffer);

    if ( close( in_fd ) == -1 )
        fileError( "Error closing files", "" );
    return 0;
}

void fileError( char* s1, char* s2 )
{
    fprintf( stderr, "Error: %s ", s1 );
    perror( s2 );
    exit( 1 );
}
#包括“stdlib.h”
#包括“stdio.h”
#包括
#包括
#包括
#定义缓冲区sz 256
#定义第10行
无效文件错误(字符*,字符*);
int main(int ac,char*args[]
{
字符缓冲区[BUFFERSZ];
int linesToRead=行;
int in_fd,rd_chars;
//检查参数计数是否无效
如果(ac<2 | | ac>3)
{
printf(“用法:头文件[n]\n”);
出口(1);
}
//检查n
如果(ac==3)
linesToRead=atoi(args[2]);
//尝试打开该文件
如果((in_fd=open(args[1],O_RDONLY))=-1)
文件错误(“无法打开”,参数[1]);
int lineCount=0;
//计数文件中的行数
while(读取(在缓冲区中,1)==1)
{        
如果(*缓冲区=='\n')
{
lineCount++;
}
}
lineCount=lineCount+1;
printf(“行数:%i\n”,行数);
int起始=0,xline=0;
//xline=总计行-所需行
xline=行数-行存储AD;
printf(“xline:%i\n\n”,xline);
if(xline<0)
xline=0;
//要打印的行数的计数
int printStop=lineCount-xline;
printf(“打印停止:%i\n\n”,打印停止);
如果((in_fd=open(args[1],O_RDONLY))=-1)
文件错误(“无法打开”,参数[1]);
//阅读并打印至所需编号
同时(启动!=打印停止){
读取(in_fd,buffer,BUFFERSZ);
启动+++;//增量启动
}
//读取(in_fd,buffer,BUFFERSZ);
printf(“%s\n”,缓冲区);
如果(关闭(在fd中)=-1)
fileError(“关闭文件时出错”,即“”);
返回0;
}
无效文件错误(字符*s1,字符*s2)
{
fprintf(标准,“错误:%s”,s1);
perror(s2);
出口(1);
}

我做错了什么?

非常奇怪的是,你打开文件,扫描它以计算总行数,然后继续回显第一行。在你开始回音之前,绝对不需要预先知道总共有多少行,这对你没有任何用处。但是,如果要执行此操作,则应在重新打开文件之前
close()
。对于您的简单程序,这是一个良好形式的问题,而不是正确功能的问题——您观察到的错误行为与此无关

您的计划的关键部分存在几个问题:

  • 在本节中,您没有检查
    read()
    调用的返回值。您必须检查它,因为它不仅告诉您是否存在错误/文件结尾,还告诉您实际读取了多少字节。不能保证在任何调用时都填充缓冲区,只有这样才能知道缓冲区的哪些元素随后包含有效数据。(预计数行在这方面对您没有任何帮助。)

  • 您正在执行raw
    read()
    s,并且显然假设每一个都只读取一行。这种假设是无效的
    read()
    不会对行终止符进行任何特殊处理,因此可能会有跨多行的读取,以及只读取部分行的读取(可能在同一读取中同时读取)。因此,不能通过计算
    read()
    调用来计算行数。相反,您必须扫描读取缓冲区中的有效字符,并计算其中的换行

  • 实际上,您不会在读取循环中打印任何内容。相反,您需要等待完成所有读取,然后在最后一次读取后在缓冲区中打印所有内容。当您在第一次读取中没有获得所需的所有行时,这将不符合您的目的,因为每次后续成功的读取都会破坏前一次读取的数据

  • 将缓冲区传递给
    printf()
    ,就好像它是一个以null结尾的字符串一样,但不确保它实际上是以null结尾的
    read()
    不适合您


  • 我很难相信你的说法,你的程序总是打印指定文件的所有行,但我可以相信它打印你正在测试的特定文件的所有行。如果文件足够短,整个文件都可以放入缓冲区,那么它可能会这样做。然后,您的程序可能会在第一次调用
    read()
    时将整个内容读入缓冲区(尽管不能保证这样做),然后在随后的每次调用中都不读取任何内容,返回-1并保持缓冲区不变。最后打印缓冲区时,它仍然包含文件的全部内容。

    次要:为什么在
    int main(int ac,char*args[])
    中使用
    ac,args
    而不是非常常见的
    argc,argv
    ?请查看源代码:在fileError中,调用
    peror
    是错误的。fprintf可能修改了errno,您将得到意外的结果。倒带文件也是一个选项(而不是重新打开)lseek(fd,0L,SEEK\u SET)就可以了,IIRC.@John Bollinger现在你一定已经意识到我是C语言编程新手了。我很感激你的输入,但我仍然不确定如何修复和更改我的代码,以便通过打印前10行代码来正确运行我的程序。如果您能帮我修改代码,我将不胜感激。@bmalhi,我已经帮过您了。自己计算细节对你有好处。@JohnBollinger你能解释一下“扫描读取缓冲区中的有效字符并计算其中的换行符”是什么意思吗
        //read and print till required number
        while (Starting != printStop) {
            read( in_fd, buffer, BUFFERSZ );
            Starting++;    //increment starting
        }
    
        //read( in_fd, buffer, BUFFERSZ );
        printf("%s \n", buffer);