为什么fgets() 会把字符串末尾的换行符视为单独的一行?

编程语言 2026-07-09

我在操作系统课程里要用C 写一个简单的shell。为了读取给定的输入命令,我在一个while循环中使用 fgets(),然后逐行解析。每个输入都被格式化为 "some_string\n",随后我把它存入缓冲区并去掉末尾的换行符。

#define MAX_LINE_SIZE 4096
char* buff = (char*) malloc(MAX_LINE_SIZE * sizeof(char));

while(fgets(buff, MAX_LINE_SIZE, stdin) != NULL) {
    *strchr(buff, '\n') = '\0';

    // without this check, the program later results in a segmentation fault
    // when the last '\n' character is read on its own.
    if(strlen(buff) == 0) {
        break;
    }

    // parse and execute the command
}

free(buff);

在下面的测试用例中,我发现最后一个字符串末尾的换行被当作独立的一行读取,导致得到长度为0 的字符串,并且在没有进行 if(strlen(buff) == 0) 检查的情况下会导致段错误:

debug\n
debug 1\n
debug\n
debug off\n
debug\n

最后的 "debug" 被作为独立的一行读取,而这一行缺少 '\n'' 字符,随后在下一次循环迭代中又被单独读取。

为什么会这样呢?我很难理解这种行为,因为根据手册,fgets() 应该把整个字符串读到(包括)'\n',或者(不包括)EOF,之后将其存储并返回字符串。

解决方案

我无法用你问题中给出的输入重现你的问题。下面的程序在运行时没有任何错误。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE_SIZE 4096

int main( void )
{
    char buff[MAX_LINE_SIZE];

    const char testdata[] =
        "debug\n"
        "debug 1\n"
        "debug\n"
        "debug off\n"
        "debug\n";

    FILE *fp = fopen( "testfile.bin", "w+" );
    if ( fp == NULL )
    {
        fprintf( stderr, "Error opening file!\n" );
        exit( EXIT_FAILURE );
    }

    fprintf( fp, "%s", testdata );

    rewind( fp );

    // This is the code from the question
    while(fgets(buff, MAX_LINE_SIZE, fp) != NULL) {
        *strchr(buff, '\n') = '\0';

        if(strlen(buff) == 0) {
            break;
        }
    }

    fclose( fp );
}

我唯一的解释是,你在问题中贴出的输入并非你实际使用的确切输入。例如,如果我在输入末尾再加一个空格,使输入不是以换行符结尾,那么就会产生段错误。例如,以下对 testdata 的定义就会导致段错误。

const char testdata[] =
    "debug\n"
    "debug 1\n"
    "debug\n"
    "debug off\n"
    "debug\n"
    " ";

为了确定到底哪里出了问题,我建议你对输入文件创建一个 十六进制转储(hexdump)。在Linux上,可以使用下面的命令。

hd inputfile

如果你在其他平台,可以编写一个简单的程序来打印输入文件的十六进制转储,例如下面这个程序。

#include <stdio.h>
#include <stdlib.h>

int main( int argc, char **argv )
{
    FILE *fp;
    int c;
    int column;

    // make sure that program was invoked correctly
    if ( argc != 2 )
    {
        char *program_name;

        if ( argc >= 1 && argv[0][0] != '\0' )
            program_name = argv[0];
        else
            program_name = "programname";

        printf( "Usage: %s inputfile\n", program_name );

        exit( EXIT_FAILURE );
    }

    // attempt to open input file
    fp = fopen( argv[1], "rb" );
    if ( fp == NULL )
    {
        fprintf( stderr, "Error opening file!\n" );
        exit( EXIT_FAILURE );
    }

    // print content of input file as hexadecimal
    column = 0;
    while ( ( c = fgetc( fp ) ) != EOF )
    {
        printf( "%02X", (unsigned char)c );
        column++;
        if ( column % 16 == 0 )
        {
            printf( "\n" );
            column = 0;
        }
        else
        {
            printf( " " );
        }
    }

    // print a newline character, if appropriate
    if ( column != 0 )
    {
        printf( "\n" );
    }

    // if an error occurred, print an appropriate error message
    if ( ferror( fp ) )
    {
        fprintf( stderr, "read error\n" );
        exit( EXIT_FAILURE );
    }

    // cleanup
    fclose( fp );
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章