[英]Taking string input in C with spaces
我正在在线 gdb 上编译我的 C 程序。在那里我遇到了一个特殊的问题。 我试图在 2D 数组中存储 10 个句子,为此我想输入 10 个字符串,空格以新行结尾。 我尝试了所有可能的语法,如scanf("%[^\n]%*c")
或scanf("%[^\n]s")
等等; 没有一个有效。 在我尝试手动创建一个 function 以输入空格之后; 即使那样也行不通。 我需要帮助了解为什么事情不起作用。 我在下面附上了我的代码以及我尝试输入空格的手动方式。
int main()
{
int c;
printf("Enter the number of sentences\n");
scanf("%d",&c);
char s[c][100];
for(int i=0; i<c; i++)
{
printf("Enter your sentence ");
int k = 0;
scanf("%c", &s[i][k]);
while (s[i][k]!='\n')
{
k++;
scanf("%c", &s[i][k]);
}
s[i][k]='\0';
}
}
如果您刷新scanf()
留下的\n
,那么您可以使用 fgets() 来读取每个句子:
#include <stdio.h>
#define SENTENCE_LEN 100
int main() {
printf("Enter the number of sentences\n");
int c;
if(scanf("%d",&c) != 1) {
// handle error
return 1;
}
for(;;) {
int ch = getchar();
if(ch == EOF) return 1;
if(ch == '\n') break;
}
char s[c][SENTENCE_LEN];
for(size_t i = 0; i < c; i++) {
printf("Enter your sentence ");
if(!fgets(s[i], SENTENCE_LEN, stdin)) {
// handle error
return 1;
}
// strip trailing newline
s[i][strcspn(s[i], "\n")] = '\0';
}
for(size_t i = 0; i < c; i++) {
printf("%s\n", s[i]);
}
}
和示例 session:
Enter the number of sentences
2
Enter your sentence hello world
Enter your sentence hello friend
hello world
hello friend
线
scanf("%[^\n]s", ... )
将不起作用,因为%[...]
和%s
是两个不同的转换格式说明符。 您似乎正在尝试混合使用两者,这可能行不通。 格式字符串中的s
将被解释为文字s
,因此scanf
将期望在输入 stream 中看到文字s
并使用它。 这不是你想要的。
相反,线
scanf("%[^\n]%*c", ... )
应该工作(除非输入是一个空行),尽管你声称它不起作用。 由于您没有提供该代码的最小可重现示例,因此我无法告诉您为什么它不适用于您。
关于您发布的代码,问题在以下问题中进行了描述:
问题是这条线
scanf("%d",&c);
不会消耗整行。 它至少会在输入 stream 上留下该行的换行符。因此,第一次调用
scanf("%c", &s[i][k]);
可能会读取剩余的换行符,而不是句子的第一个字符。
一种简单的解决方案是预先调用 function getchar
,以便从输入 stream 中消耗换行符。但是,这仅在换行符是输入 stream 中唯一剩余的字符时才有效。如果有可能输入 stream 上有更多剩余字符,那么有几种方法可以消耗所有这些字符:
使用scanf
:
scanf( "%*[^\n]" );
getchar();
在do
... while
循环中使用getchar
:
int c;
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
在紧凑for
循环中使用getchar
:
for ( int c; ( c = getchar() ) != EOF && c != '\n'; )
;
解决上述所有问题并将代码添加到 output 结果后,您的程序应如下所示:
#include <stdio.h>
int main( void )
{
int c;
//get the number of sentences from the user
printf( "Enter the number of sentences: " );
scanf( "%d", &c );
//discard all leftover characters
scanf( "%*[^\n]" );
getchar();
//declare variable-length array based on user input
char s[c][100];
//get the input sentences
for ( int i=0; i<c; i++ )
{
printf( "Enter your sentence: " );
int k = 0;
scanf( "%c", &s[i][k] );
while ( s[i][k] != '\n' )
{
k++;
scanf( "%c", &s[i][k] );
}
s[i][k] = '\0';
}
//print the sentences
printf( "\nThe results are:\n\n" );
for ( int i=0; i<c; i++ )
{
printf( "%s\n", s[i] );
}
}
该程序具有以下行为:
Enter the number of sentences: 5
Enter your sentence: test1
Enter your sentence: test2
Enter your sentence: test3
Enter your sentence: test4
Enter your sentence: test5
The results are:
test1
test2
test3
test4
test5
请注意,此程序只能保证处理最多99
字节的行,否则您将遇到缓冲区溢出,这意味着您的程序可能会崩溃。 此外,如果用户在程序开始时输入的数字无效(例如负数或根本不是数字),则程序也可能会崩溃或以其他方式出现异常。
在处理基于行的用户输入时,通常不建议使用scanf
部分读取一行。 通常最好总是一次读取整行输入,例如使用fgets
。 因此,我重写了您的整个程序,完全不使用scanf
,而是使用fgets
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
//forward declarations
int get_int_from_user( const char prompt[] );
void get_line_from_user( const char prompt[], char buffer[], int buffer_size );
int main( void )
{
int c;
//get the number of sentences from the user
for (;;)
{
c = get_int_from_user( "Enter the number of sentences: " );
//break out of loop if input is valid
if ( c > 1 )
break;
printf( "Input must be a positive integer, please try again!\n" );
}
//declare variable-length array based on user input
char s[c][100];
//get the input sentences
for ( int i=0; i<c; i++ )
{
get_line_from_user( "Enter your sentence: ", s[i], sizeof s[i] );
}
//print the sentences
printf( "\nThe results are:\n\n" );
for ( int i=0; i<c; i++ )
{
printf( "%s\n", s[i] );
}
}
//This function will attempt to read one integer from the user. If
//the input is invalid, it will automatically reprompt the user,
//until the input is valid.
int get_int_from_user( const char prompt[] )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfj23jlj" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
//This function will read exactly one line of input from the
//user. If the line is too long to fit in the buffer, then the
//function will automatically reprompt the user for input. On
//failure, the function will never return, but will print an
//error message and call "exit" instead.
void get_line_from_user( const char prompt[], char buffer[], int buffer_size )
{
for (;;)
{
char *p;
//prompt user for input
fputs( prompt, stdout );
//attempt to read one line of input
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
//attempt to find newline character
p = strchr( buffer, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small to store the entire line)
if ( p == NULL )
{
int c;
//a missing newline character is ok if the next
//character is a newline character or if we have
//reached end-of-file (for example if the input is
//being piped from a file or if the user enters
//end-of-file in the terminal itself)
if ( !feof(stdin) && (c=getchar()) != '\n' )
{
printf( "Input was too long to fit in buffer!\n" );
//discard remainder of line
do
{
if ( c == EOF )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
c = getchar();
} while ( c != '\n' );
continue;
}
}
else
{
//remove newline character by overwriting it with
//null character
*p = '\0';
}
//input was ok, so break out of loop
break;
}
}
我已将 function get_int_from_user
从我的这个答案带到另一个问题。 有关 function 如何工作的更多信息,请参阅该答案。 例如,我将 function 设计为执行完整的输入验证并在输入无效时自动重新提示用户。
第二个程序与第一个程序具有相同的行为,除了输入验证现在是健壮的并且如果用户输入无效输入程序不会崩溃:
Enter the number of sentences: -5
Input must be a positive integer, please try again!
Enter the number of sentences: 0
Input must be a positive integer, please try again!
Enter the number of sentences: test
Error converting string to number!
Enter the number of sentences: 6abc
Unexpected input encountered!
Enter the number of sentences: 6
Enter your sentence: test1
Enter your sentence: test2
Enter your sentence: test3
Enter your sentence: test4
Enter your sentence: test5
Enter your sentence: test6
The results are:
test1
test2
test3
test4
test5
test6
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.