如何从头开始打印一行?
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
只会告诉你之前的输入操作是否已经遇到文件结束。它不会告诉您现在是否已到达文件末尾,因此下一个输入操作将失败。该函数函数无法预测下一次对 fscanf
或 fgets
的输入操作是否会失败。因此,一般不应作为循环条件使用。有关详细信息,请参阅此问题: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
请注意,上面的代码并不完美,因为它存在以下问题:
- 当使用
fgets
时,如果输入行太长而无法放入缓冲区,那么程序将不会检测到这一点,尽管在这种情况下它可能应该打印一条错误消息并退出。
- 如果“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);
}
}
我一直在编写一个程序,将数据写入文本文件并在 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
只会告诉你之前的输入操作是否已经遇到文件结束。它不会告诉您现在是否已到达文件末尾,因此下一个输入操作将失败。该函数函数无法预测下一次对 fscanf
或 fgets
的输入操作是否会失败。因此,一般不应作为循环条件使用。有关详细信息,请参阅此问题: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
.
#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
请注意,上面的代码并不完美,因为它存在以下问题:
- 当使用
fgets
时,如果输入行太长而无法放入缓冲区,那么程序将不会检测到这一点,尽管在这种情况下它可能应该打印一条错误消息并退出。 - 如果“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);
}
}