如何从头开始打印一行?

How to print a line from its beginning?

我一直在编写一个程序,将数据写入文本文件并在 c 中练习数据处理,并从那里找到数据,每个数据都存储为行。有行,数据是逐行存储的,如:

学生姓名学生姓氏学生phone等

当我输入“学生姓名”时,它开始打印而不打印名字本身,打印后面的内容,如果我搜索姓氏,也会发生同样的情况,只会打印出 phone。

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

int main(){
    FILE *filePtr;
    filePtr=fopen("std.txt","r");
    char char_input[50];
    char string[500];


    printf("%s","Please give an input of the phone number\n");
        scanf("%s",char_input);

        while(!feof(filePtr)){
            fscanf(filePtr,"%s",string);
                if(strcmp(string, char_input)== 0){
                    fgets(string,500,filePtr);
                    puts(string);
            }
        }
    fclose(filePtr);
}

文本文件:

Andrew Brooks 865 965 55

输入:

Andrew

输出:

Brooks 865 965 55

期望的输出:

Andrew Brooks 865 965 55

函数feof只会告诉你之前的输入操作是否已经遇到文件结束。它不会告诉您现在是否已到达文件末尾,因此下一个输入操作将失败。该函数函数无法预测下一次对 fscanffgets 的输入操作是否会失败。因此,一般不应作为循环条件使用。有关详细信息,请参阅此问题:Why is “while ( !feof (file) )” always wrong?

在您的情况下,feof 可能 return false 并且随后对 fscanf 的函数调用可能 return EOF 由于遇到结束-文件。在这种情况下,您发布的代码将忽略 fscanf 的 return 值并表现得好像 fscanf 已经成功,并且您发布的代码将尝试处理不存在的输入。这可能会导致错误。

因此,不要使用函数feof来判断是否继续循环,而应该检查输入函数的return值。

您可以像这样重写循环:

while ( fscanf(filePtr,"%s",string) == 1 ) {
    if ( strcmp(string, char_input ) == 0 ) {
        fgets( string, 500, filePtr );
        puts( string );
    }
}

这将解决上面提到的不检查fscanf的return值的问题。但是,根据确切的输入,函数 fgets 也可能会因遇到文件结尾而失败。因此,如果你的程序也检查函数 fgets 的 return 值,而不是简单地假设函数成功

会更好。

另一个问题是行

puts(string);

只会打印string的内容,也就是" Brooks 865 965 55"。但是,您还想打印 "Andrew",它被 fscanf 函数调用读取但同时被 fgets 函数调用覆盖。最简单的解决方案是在它被覆盖之前打印它。但是,如果用户搜索 "Brooks" 而不是 "Andrew",这将不起作用,因为单词 "Andrew" 已经在之前的循环迭代中被丢弃。这是因为在循环中调用 fscanf(filePtr,"%s",string) 不会在每次循环迭代中读取一行输入,而是会在每次循环迭代中读取一个单词(意义不大)。

使用 fscanf(filePtr,"%s",string) 逐字读取输入文件的另一个结果是您将无法找到 phone 数字 "865 965 55" 的匹配项。这是因为您的程序将首先从输入文件中读取 "865" 并确定此“单词”与搜索字符串不同。然后它将读取 "965" 并确定相同的事情。它会对 "55".

做同样的事情

最好的解决方案可能是重新设计您的循环,使其每次循环迭代始终准确读取一行输入,而不是每次循环迭代仅读取一个单词。读取一行输入后,您可以使用函数 sscanf.

将其拆分为“first name”、“last name”和“phone number”来解析该行
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    FILE *filePtr;
    char search_string[50];
    char line[200];

    //open input file
    filePtr = fopen( "std.txt", "r" );
    if ( filePtr == NULL )
    {
        fprintf( stderr, "unable to open input file!\n" );
        exit( EXIT_FAILURE );
    }

    //prompt user for input
    printf( "Please enter search string: " );

    //Note that the following code now uses "fgets" instead
    //of "fscanf", because fscanf will only read a single
    //word, when using the "%s" format specifier. This means
    //that it would be unable to read the phone number
    //"865 965 55" as an input string, because that line
    //consists of three "words".

    //read exactly one line of input from user
    if ( fgets( search_string, sizeof search_string, stdin ) == NULL )
    {
        fprintf( stderr, "input failure!\n" );
        exit( EXIT_FAILURE );
    }

    //remove newline character from input line by
    //replacing it with terminating null character
    search_string[strcspn(search_string,"\n")] = '[=12=]';

    //read exactly one line of input from the input file
    //per loop iteration
    while ( fgets( line, sizeof line, filePtr ) != NULL )
    {
        char first_name[50];
        char last_name[50];
        char phone_number[50];

        //attempt to parse input
        if (
            sscanf(
                line,
                "%49s %49s %49[^\n]",
                first_name,
                last_name,
                phone_number
            )
            != 3
        )
        {
            fprintf(
                stderr,
                "WARNING: skipping line due to parse error!\n"
            );

            continue;
        }

        //parsing was successful, so we can now search the
        //3 individual fields for the search string
        if (
            strcmp( search_string, first_name   ) == 0
            ||
            strcmp( search_string, last_name    ) == 0
            ||
            strcmp( search_string, phone_number ) == 0
        )
        {
            //remove newline character from input line by
            //replacing it with terminating null character
            line[strcspn(line,"\n")] = '[=12=]';

            //print entire input line of file for user
            printf( "%s\n", line );
        }
    }

    //cleanup
    fclose(filePtr);
}

此程序具有以下行为:

Please enter search string: Andrew
Andrew Brooks 865 965 55
Please enter search string: Brooks
Andrew Brooks 865 965 55
Please enter search string: 865 965 55
Andrew Brooks 865 965 55

请注意,上面的代码并不完美,因为它存在以下问题:

  1. 当使用 fgets 时,如果输入行太长而无法放入缓冲区,那么程序将不会检测到这一点,尽管在这种情况下它可能应该打印一条错误消息并退出。
  2. 如果“first name”、“last name”或“phone number”中的任何一个字段大于 49 个字符,代码会防止缓冲区溢出(这可能会导致程序崩溃), 但它仍然不能正确处理这种情况,例如检查这种情况并打印适当的错误消息。

但是,对于您的目的,代码应该足够了。

解决这些问题的更强大的程序如下:

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

//This function will read exactly one line of input using
//fgets and verify that the line was not too long for the
//input buffer. Note that the buffer size must be two bytes
//longer than the actual string length, because there must
//be space for the newline character and the terminating
//null character. The newline character will be overwritten
//with another terminating null character.
//On success, it will return true. If not further input is
//available due to end-of-file, it will return false.
//Otherwise, the function will not return, but will
//terminate the program with an error message.
bool get_one_line_of_user_input( char *buffer, int buffer_size )
{
    char *p;

    if ( fgets( buffer, buffer_size, stdin ) == NULL )
    {
        if ( feof( stdin ) )
        {
            return false;
        }
        else
        {
            fprintf( stderr, "input error!\n" );
            exit( EXIT_FAILURE );
        }
    }

    p = strchr( buffer, '\n' );
    if ( p == NULL )
    {
        //No newline character was found. This could mean
        //that the line was too long to store in the input
        //buffer, in which case, the program should quit
        //with an error message. However, it could also mean
        //that input has been redirected to come from a
        //file, and that this file ends with a line without
        //a line ending. In that case, the missing newline
        //character can be ignored.
        if ( !feof( stdin ) )
        {
            fprintf( stderr, "line too long for buffer!\n" );
            exit( EXIT_FAILURE );
        }
    }
    else
    {
        //remove newline character
        *p = '[=16=]';
    }

    return true;
}

int main()
{
    FILE *filePtr;
    char search_string[50];
    char line[200];

    //open input file
    filePtr = fopen( "std.txt", "r" );
    if ( filePtr == NULL )
    {
        fprintf( stderr, "unable to open input file!\n" );
        exit( EXIT_FAILURE );
    }

    //prompt user for input
    printf( "Please enter search string: " );

    //read exactly one line of input from user
    if ( !get_one_line_of_user_input( search_string, sizeof search_string ) )
    {
        fprintf( stderr, "input failure!\n" );
        exit( EXIT_FAILURE );
    }

    //read exactly one line of input from the input file
    //per loop iteration
    while ( get_one_line_of_user_input( line, sizeof line ) )
    {
        char first_name[50];
        char last_name[50];
        char phone_number[50];

        //attempt to parse input
        if (
            sscanf(
                line,
                "%49s %49s %49[^\n]",
                first_name,
                last_name,
                phone_number
            )
            != 3
        )
        {
            fprintf(
                stderr,
                "WARNING: skipping line due to parse error!\n"
            );

            continue;
        }

        //verify that none of the fields was too long
        if (
            strlen( first_name   ) == 49
            ||
            strlen( last_name    ) == 49
            ||
            strlen( phone_number ) == 49
        )
        {
            //At least one buffer is full, and we have no way
            //to determine whether the limit was exceeded or whether
            //we are merely at the limit, so we must assume that
            //the limit was exceeded.
            fprintf(
                stderr,
                "WARNING: skipping line due to field length "
                "limit exceeded!\n"
            );

            continue;
        }

        //parsing was successful, so we can now search the
        //3 individual fields for the search string
        if (
            strcmp( search_string, first_name   ) == 0
            ||
            strcmp( search_string, last_name    ) == 0
            ||
            strcmp( search_string, phone_number ) == 0
        )
        {
            //print entire input line of file for user
            printf( "%s\n", line );
        }
    }

    //cleanup
    fclose(filePtr);
}

而不是 incorrectly using feof()fscanf(filePtr,"%s", ... 来错误地读取 。使用 fgets() 读取文件的 并转换为 string.

  • 测试 fgets() 的 return 值以查看是否发生输入。

  • 使用 strstr()string.

    中查找匹配的子字符串

示例:

    while (fgets(string, sizeof string, filePtr)) {
      if (strstr(string, char_input)){
        fputs(string, stdout);
      }
    }