[英]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.