This was supposed to be very simple, but I'm having trouble to read successive inputs from the keyboard.
这应该是非常简单的,但是我无法从键盘读取连续的输入。
Here's the code:
这是代码:
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
scanf ("%s", string);
printf ("%s", string);
printf ("\nwrite a character: ");
scanf ("%c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
What is happening
发生什么事
When I enter a string (e.g.: computer), the program reads the newline ('\n'
) and puts it in character
. Here is how the display looks like:
当我输入一个字符串(例如:计算机)时,程序会读取换行符('\ n')并将其放入字符中。以下是显示的外观:
write something: computer
computer
Character:
Correspondent number: 10
Moreover, the program does not work for strings with more than one word. How could I overcome these problems?
此外,该程序不适用于包含多个单词的字符串。我怎么能克服这些问题?
6 个解决方案
#1
8
First scanf
read the entered string and left behind \n
in the input buffer. Next call to scanf
read that \n
and store it to character
.
Try this
第一个scanf读取输入的字符串并在输入缓冲区中留下\ n。下一次调用scanf读取\ n并将其存储到字符。尝试这个
scanf (" %c", &characte);
// ^A space before %c in scanf can skip any number of white space characters.
Program will not work for strings more than one character because scanf
stops reading once find a white space character. You can use fgets
instead
程序不适用于多个字符串的字符串,因为一旦找到空格字符,scanf就会停止读取。你可以改用fgets
fgets(string, 200, stdin);
#2
3
OP's first problem is typically solved by prepending a space to the format. This will consume white-space including the previous line's '\n'
.
OP的第一个问题通常是通过在格式前加一个空格来解决。这将占用空白区域,包括前一行的'\ n'。
// scanf("%c", &character);
scanf(" %c", &character);
Moreover, the program does not work for strings with more than one word. How could I overcome these problems?
此外,该程序不适用于包含多个单词的字符串。我怎么能克服这些问题?
For the the 2nd issue, let us go for a more precise understanding of "string" and what "%s"
does.
对于第二个问题,让我们更准确地理解“字符串”以及“%s”的作用。
A string is a contiguous sequence of characters terminated by and including the first null character. 7.1.1 1
字符串是由第一个空字符终止并包括第一个空字符的连续字符序列。 7.1.1 1
OP is not entering a string even though "I enter a string (e.g.: computer)," is reported. OP is entering a line of text. 8 characters "computer" followed by Enter. There is no "null character" here. Instead 9 char
"computer\n"
.
即使报告“我输入一个字符串(例如:计算机)”,OP也没有输入字符串。 OP正在输入一行文字。 8个字符“computer”,然后按Enter键。这里没有“空字符”。而是9个字符“计算机\ n”。
"%s"
in scanf("%s", string);
does 3 things:
scanf中的“%s”(“%s”,字符串);做3件事:
1) Scan, but not save any leading white-space.
1)扫描,但不保存任何前导空白区域。
2) Scan and save into string
any number of non-white-space.
2)扫描并保存为字符串任意数量的非空白区域。
3) Stop scanning when white-space or EOF reached. That char
is but back into stdin
. A '\0'
is appended to string
making that char
array a C string.
3)到达白色空间或EOF时停止扫描。那个char只能回到stdin。字符串附加'\ 0',使char数组成为C字符串。
To read a line including spaces, do not use scanf("%s",...
. Consider fgets()
.
要读取包含空格的行,请不要使用scanf(“%s”,....考虑fgets()。
fgets(string, sizeof string, stdin);
// remove potential trailing \r\n as needed
string[strcspn(string, "\n")] = 0;
Mixing scanf()
and fgets()
is a problem as calls like scanf("%s", string); fgets(...)
leave the '\n'
in stdin
for fgets()
to read as a line consisting of only "\n"
. Recommend instead to read all user input using fgets()
(or getline()
on *nix system). Then parse the line read.
混合scanf()和fgets()是一个问题,像scanf(“%s”,string)这样的调用; fgets(...)在stdin中保留'\ n',以便fgets()读取为仅包含“\ n”的行。建议使用fgets()(或* nix系统上的getline())读取所有用户输入。然后解析读取的行。
fgets(string, sizeof string, stdin);
scanf(string, "%c", &character);
If code must user scanf()
to read user input including spaces:
如果代码必须使用scanf()来读取包含空格的用户输入:
scanf("%*[\n]"); // read any number of \n and not save.
// Read up to 199 `char`, none of which are \n
if (scanf("%199[^\n]", string) != 1) Handle_EOF();
Lastly, code should employ error checking and input width limitations. Test the return values of all input functions.
最后,代码应该使用错误检查和输入宽度限制。测试所有输入函数的返回值。
#3
2
What you're seeing is the correct behavior of the functions you call:
您所看到的是您调用的函数的正确行为:
-
scanf will read one word from the input, and leave the input pointer immediately after the word it reads. If you type
computer<RETURN>
, the next character to be read is the newline.scanf将从输入中读取一个字,并在输入的字后立即保留输入指针。如果键入computer
,则要读取的下一个字符是换行符。 -
To read a whole line, including the final newline, use
fgets
. Read the documentation carefully:fgets
returns a string that includes the final newline it read. (gets
, which shouldn't be used anyway for a number of reasons, reads and discards the final newline.)要读取整行,包括最终换行符,请使用fgets。仔细阅读文档:fgets返回一个字符串,其中包含它读取的最终换行符。 (获取,由于多种原因不应该使用,读取并丢弃最终换行符。)
I should add that while scanf
has its uses, using it interactively leads to very confusing behavior, as I think you discovered. Even in cases where you want to read word by word, use another method if the intended use is interactive.
我应该补充一点,虽然scanf有其用途,但是我认为你发现,交互使用它会导致非常混乱的行为。即使在您想要逐字阅读的情况下,如果预期用途是交互式的,也可以使用其他方法。
#4
0
You can make use of %*c
:
你可以使用%* c:
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
scanf ("%s%*c", string);
printf ("%s", string);
printf ("\nwrite a character: ");
scanf ("%c%*c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
%*c
will accept and ignore the newline or any white-spaces
%* c将接受并忽略换行符或任何空格
#5
0
You cal also put getchar()
after the scanf
line. It will do the job :)
你也可以在scanf行之后放入getchar()。它会做的工作:)
#6
-1
The streams need to be flushed. When performing successive inputs, the standard input stream, stdin, buffers every key press on the keyboard. So, when you typed "computer" and pressed the enter key, the input stream absorbed the linefeed too, even though only the string "computer" was assigned to string
. Hence when you scanned for a character later, the already loaded new line character was the one scanned and assigned to character
.
流需要刷新。执行连续输入时,标准输入流stdin缓冲键盘上的每次按键。因此,当您键入“计算机”并按下回车键时,输入流也会吸收换行,即使只将字符串“computer”分配给字符串。因此,当您稍后扫描某个字符时,已加载的新行字符是扫描并分配给字符的字符。
Also the stdout streams need to be flushed. Consider this:
还需要刷新stdout流。考虑一下:
...
printf("foo");
while(1)
{}
...
If one tries to execute something like this then nothing is displayed on the console. The system buffered the stdout
stream, the standard output stream, unaware of the fact it would be encounter an infinite loop next and once that happens, it never gets a chance to unload the stream to the console.
如果尝试执行类似这样的操作,则控制台上不会显示任何内容。系统缓冲了stdout流,标准输出流,没有意识到接下来会遇到无限循环的事实,一旦发生这种情况,它就永远不会有机会将流卸载到控制台。
Apparently, in a similar manner whenever scanf
blocks the program and waits on stdin
, the standard input stream, it affects the other streams that are buffering. Anyway, whatsoever may be the case it's best to flush the streams properly if things start jumbling up.
显然,每当scanf阻塞程序并等待stdin(标准输入流)时,它以类似的方式影响其他缓冲流。无论如何,无论如何,如果事情开始笨拙,最好能够正确地冲洗流。
The following modifications to your code seem to produce the desired output
对代码的以下修改似乎产生了所需的输出
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
fflush(stdout);
scanf ("%s", string);
fflush(stdin);
printf ("%s", string);
printf ("\nwrite a character: ");
fflush(stdout);
scanf ("%c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
Output:
write something: computer
computer
write a character: a
输出:写点东西:电脑上写一个字符:a
Character a Correspondent number: 97
字符对应号码:97
#1
8
First scanf
read the entered string and left behind \n
in the input buffer. Next call to scanf
read that \n
and store it to character
.
Try this
第一个scanf读取输入的字符串并在输入缓冲区中留下\ n。下一次调用scanf读取\ n并将其存储到字符。尝试这个
scanf (" %c", &characte);
// ^A space before %c in scanf can skip any number of white space characters.
Program will not work for strings more than one character because scanf
stops reading once find a white space character. You can use fgets
instead
程序不适用于多个字符串的字符串,因为一旦找到空格字符,scanf就会停止读取。你可以改用fgets
fgets(string, 200, stdin);
#2
3
OP's first problem is typically solved by prepending a space to the format. This will consume white-space including the previous line's '\n'
.
OP的第一个问题通常是通过在格式前加一个空格来解决。这将占用空白区域,包括前一行的'\ n'。
// scanf("%c", &character);
scanf(" %c", &character);
Moreover, the program does not work for strings with more than one word. How could I overcome these problems?
此外,该程序不适用于包含多个单词的字符串。我怎么能克服这些问题?
For the the 2nd issue, let us go for a more precise understanding of "string" and what "%s"
does.
对于第二个问题,让我们更准确地理解“字符串”以及“%s”的作用。
A string is a contiguous sequence of characters terminated by and including the first null character. 7.1.1 1
字符串是由第一个空字符终止并包括第一个空字符的连续字符序列。 7.1.1 1
OP is not entering a string even though "I enter a string (e.g.: computer)," is reported. OP is entering a line of text. 8 characters "computer" followed by Enter. There is no "null character" here. Instead 9 char
"computer\n"
.
即使报告“我输入一个字符串(例如:计算机)”,OP也没有输入字符串。 OP正在输入一行文字。 8个字符“computer”,然后按Enter键。这里没有“空字符”。而是9个字符“计算机\ n”。
"%s"
in scanf("%s", string);
does 3 things:
scanf中的“%s”(“%s”,字符串);做3件事:
1) Scan, but not save any leading white-space.
1)扫描,但不保存任何前导空白区域。
2) Scan and save into string
any number of non-white-space.
2)扫描并保存为字符串任意数量的非空白区域。
3) Stop scanning when white-space or EOF reached. That char
is but back into stdin
. A '\0'
is appended to string
making that char
array a C string.
3)到达白色空间或EOF时停止扫描。那个char只能回到stdin。字符串附加'\ 0',使char数组成为C字符串。
To read a line including spaces, do not use scanf("%s",...
. Consider fgets()
.
要读取包含空格的行,请不要使用scanf(“%s”,....考虑fgets()。
fgets(string, sizeof string, stdin);
// remove potential trailing \r\n as needed
string[strcspn(string, "\n")] = 0;
Mixing scanf()
and fgets()
is a problem as calls like scanf("%s", string); fgets(...)
leave the '\n'
in stdin
for fgets()
to read as a line consisting of only "\n"
. Recommend instead to read all user input using fgets()
(or getline()
on *nix system). Then parse the line read.
混合scanf()和fgets()是一个问题,像scanf(“%s”,string)这样的调用; fgets(...)在stdin中保留'\ n',以便fgets()读取为仅包含“\ n”的行。建议使用fgets()(或* nix系统上的getline())读取所有用户输入。然后解析读取的行。
fgets(string, sizeof string, stdin);
scanf(string, "%c", &character);
If code must user scanf()
to read user input including spaces:
如果代码必须使用scanf()来读取包含空格的用户输入:
scanf("%*[\n]"); // read any number of \n and not save.
// Read up to 199 `char`, none of which are \n
if (scanf("%199[^\n]", string) != 1) Handle_EOF();
Lastly, code should employ error checking and input width limitations. Test the return values of all input functions.
最后,代码应该使用错误检查和输入宽度限制。测试所有输入函数的返回值。
#3
2
What you're seeing is the correct behavior of the functions you call:
您所看到的是您调用的函数的正确行为:
-
scanf will read one word from the input, and leave the input pointer immediately after the word it reads. If you type
computer<RETURN>
, the next character to be read is the newline.scanf将从输入中读取一个字,并在输入的字后立即保留输入指针。如果键入computer
,则要读取的下一个字符是换行符。 -
To read a whole line, including the final newline, use
fgets
. Read the documentation carefully:fgets
returns a string that includes the final newline it read. (gets
, which shouldn't be used anyway for a number of reasons, reads and discards the final newline.)要读取整行,包括最终换行符,请使用fgets。仔细阅读文档:fgets返回一个字符串,其中包含它读取的最终换行符。 (获取,由于多种原因不应该使用,读取并丢弃最终换行符。)
I should add that while scanf
has its uses, using it interactively leads to very confusing behavior, as I think you discovered. Even in cases where you want to read word by word, use another method if the intended use is interactive.
我应该补充一点,虽然scanf有其用途,但是我认为你发现,交互使用它会导致非常混乱的行为。即使在您想要逐字阅读的情况下,如果预期用途是交互式的,也可以使用其他方法。
#4
0
You can make use of %*c
:
你可以使用%* c:
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
scanf ("%s%*c", string);
printf ("%s", string);
printf ("\nwrite a character: ");
scanf ("%c%*c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
%*c
will accept and ignore the newline or any white-spaces
%* c将接受并忽略换行符或任何空格
#5
0
You cal also put getchar()
after the scanf
line. It will do the job :)
你也可以在scanf行之后放入getchar()。它会做的工作:)
#6
-1
The streams need to be flushed. When performing successive inputs, the standard input stream, stdin, buffers every key press on the keyboard. So, when you typed "computer" and pressed the enter key, the input stream absorbed the linefeed too, even though only the string "computer" was assigned to string
. Hence when you scanned for a character later, the already loaded new line character was the one scanned and assigned to character
.
流需要刷新。执行连续输入时,标准输入流stdin缓冲键盘上的每次按键。因此,当您键入“计算机”并按下回车键时,输入流也会吸收换行,即使只将字符串“computer”分配给字符串。因此,当您稍后扫描某个字符时,已加载的新行字符是扫描并分配给字符的字符。
Also the stdout streams need to be flushed. Consider this:
还需要刷新stdout流。考虑一下:
...
printf("foo");
while(1)
{}
...
If one tries to execute something like this then nothing is displayed on the console. The system buffered the stdout
stream, the standard output stream, unaware of the fact it would be encounter an infinite loop next and once that happens, it never gets a chance to unload the stream to the console.
如果尝试执行类似这样的操作,则控制台上不会显示任何内容。系统缓冲了stdout流,标准输出流,没有意识到接下来会遇到无限循环的事实,一旦发生这种情况,它就永远不会有机会将流卸载到控制台。
Apparently, in a similar manner whenever scanf
blocks the program and waits on stdin
, the standard input stream, it affects the other streams that are buffering. Anyway, whatsoever may be the case it's best to flush the streams properly if things start jumbling up.
显然,每当scanf阻塞程序并等待stdin(标准输入流)时,它以类似的方式影响其他缓冲流。无论如何,无论如何,如果事情开始笨拙,最好能够正确地冲洗流。
The following modifications to your code seem to produce the desired output
对代码的以下修改似乎产生了所需的输出
#include <string.h>
#include <stdio.h>
int main()
{
char string[200];
char character;
printf ("write something: ");
fflush(stdout);
scanf ("%s", string);
fflush(stdin);
printf ("%s", string);
printf ("\nwrite a character: ");
fflush(stdout);
scanf ("%c", &character);
printf ("\nCharacter %c Correspondent number: %d\n", character, character);
return 0;
}
Output:
write something: computer
computer
write a character: a
输出:写点东西:电脑上写一个字符:a
Character a Correspondent number: 97
字符对应号码:97