C语言 即使它是唯一的输入,如何从scanf获取换行符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14565956/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get newline character from scanf even if it's the only input
提问by user2018675
I'm doing homework that asks me to read an integer nrepresenting the size of a loop and then read a line of characters ntimes and print it right after the user's input. So I used scanfand then I print it with printf. The problem is that if the user's input is only a newline character, it should print another \n, but scanfseems to ignore the input when it's a single \n.
我正在做作业,要求我读取一个n表示循环大小的整数,然后读取一行字符的n时间并在用户输入后立即打印出来。所以我使用scanf然后我用printf. 问题是,如果用户的输入只是一个换行符,它应该打印另一个\n,scanf但当它是单个\n.
Is there any way to make this assignment with scanfor should I try something else?
有什么办法可以完成这项任务,scanf或者我应该尝试其他方法吗?
int i;
scanf("%d", &i);
for(int ct=0; ct<i; ct++)
{
char buff[28];
scanf("%s", buff); // if I just press enter here
prtinf("%s\n", buff); // then I must get \n\n here
}
回答by Daniel Fischer
Using fgetsto read in a line is simpler and more robust:
使用fgets读取一行更简单,更健壮:
if (!fgets(buff, 28, stdin))
{
// reading failed, do appropriate error handling
// we're just exiting here
exit(EXIT_FAILURE);
}
// We have successfully read in a line, or at least the first 27
// characters of the line. Check whether a full line was read,
// if it was, whether the line was empty
size_t l = strlen(buff); // <string.h> must be included
if (buff[l-1] == '\n')
{
// a full line was read, remove trailing newline unless
// the line was empty
if (l > 1)
{
buff[l-1] = 0;
}
}
else
{
// the input was too long, what now?
// leave the remaining input for the next iteration or
// empty the input buffer?
}
printf("%s\n",buff);
It doesn't work with scanf("%s",buff)because most scanfconversions ignore leading white space:
它不起作用,scanf("%s",buff)因为大多数scanf转换忽略前导空格:
Input white-space characters (as specified by the
isspacefunction) are skipped, unless the specification includes a[,c, ornspecifier.
输入的空白字符(由指定的
isspace功能)被跳过,除非本说明书包括一个[,c或n说明符。
So if the user inputs an empty line, scanfignores that input unless its format is one of the exceptional.
因此,如果用户输入空行,则scanf忽略该输入,除非其格式是例外格式之一。
You can use scanfwith a character set format instead,
您可以使用scanf字符集格式,
scanf("%27[^\n]%*c", buff);
to read all characters until a newline (but limited to 28 - 1here to avoid buffer overruns), and then consume a newline without storing it (the *in the %*cconversion specifier suppresses assignment), that would handle non-empty lines consisting entirely of whitespace, which the %sconversion would not. But if the first character of the input is a newline, the %27[^\n]conversion fails (thanks to chuxfor drawing attention to that), the newline is left in the input buffer, and subsequent scans with that format would also fail if the newline isn't removed from the input buffer.
读取所有字符直到换行符(但仅限28 - 1于此以避免缓冲区溢出),然后使用换行符而不存储它(*在%*c转换说明符中禁止赋值),这将处理完全由空格组成的非空行,其中%s转换不会。但是如果输入的第一个字符是换行符,则%27[^\n]转换失败(感谢chux引起注意),换行符留在输入缓冲区中,如果换行符不是,则使用该格式的后续扫描也将失败从输入缓冲区中删除。
A somewhat robust (but ugly; and not dealing with too long input) loop using scanfwould, as far as I can see, need to check for a newline before scanning, e.g.
一个有点健壮(但丑陋;并且不处理太长的输入)循环使用scanf将,据我所知,需要在扫描前检查换行符,例如
for(int ct = 0; ct < i; ++ct)
{
int ch = getchar();
if (ch == EOF)
{
// something bad happened; we quit
exit(EXIT_FAILURE);
}
if (ch == '\n')
{
// we had an empty line
printf("\n\n");
}
else
{
// The first character was not a newline, scanning
// with the character set format would have succeeded.
// But we don't know what comes next, so we put the
// character back first.
// Although one character of pushback is guaranteed,
if (ungetc(ch,stdin) == EOF)
{
// pushback failed
exit(EXIT_FAILURE);
}
scanf("%27[^\n]%*c",buff);
printf("%s\n",buff);
}
}
Use fgets, really. It's better.
使用fgets,真的。更好。
回答by qwertz
I have two solutions for this:
我有两个解决方案:
- Use
fgetsinstead ofscanf. - Append a
\nat the end of the string, because you know the use will end the input with\n.
- 使用
fgets代替scanf。 \n在字符串的末尾附加 a ,因为您知道 use 将以\n.
First solution:
第一个解决方案:
...
char buf[28];
fgets(buf, 28, stdin);
...
Second solution:
第二种解决方案:
#include <string.h>
...
char buf[28];
scanf("%s", buf);
strcat(buf, "\n"); // add the newline to the string
...
回答by jetty
Yes, scanf() cando it if you use the right format-specifiers:
--To accept a string of no more than 27 chars and
--To accept all chars for that string (including space) except newline '\n', and
--To see if scanf() made any conversions, write:
是的,如果您使用正确的格式说明符,scanf()可以做到:
--接受不超过 27 个字符的字符串和
--接受该字符串的所有字符(包括空格),除了换行符 '\n',和
-- 要查看 scanf() 是否进行了任何转换,请编写:
scanRet = scanf("%27[^\n]s", buf); // fills buf[28] with a string.
if(scanRet < 1) printf("Sorry, scanf didn't change buf[] array.\n");
CAUTION! Repeated scanf() calls or conversions often get spoiled by unexpected inputs from users and/or leftover 'junk' in stdin. You can clear stdin between each scanf() call like this:
警告!重复的 scanf() 调用或转换通常会被来自用户的意外输入和/或标准输入中剩余的“垃圾”破坏。您可以在每次 scanf() 调用之间清除 stdin,如下所示:
char junk;
do {
junk = getc(stdin);
printf("junk: %c (int %d)\n", junk, (int)junk); // show me the junk we cleared
}
while(junk != '\n' || junk != EOF);
Here's my best 'clean' solution using only scanf():
这是我仅使用 scanf() 的最佳“干净”解决方案:
char buf[28] = ""; // sets buf[0] = '##代码##';
char junk;
int i, iMax, scanRet;
printf("how many lines? (integer>0):\n");
scanRet = scanf(" %d", &iMax);
if(scanRet < 1) {
printf("Sorry, I couldn't read your integer. Bye.\n");
return 1; // error exit.
}
printf("please enter %d lines of text (<=27 chars/line):\n",iMax);
for(i=0; i<iMax; i++) {
// but first, clear any leftover junk in stdin;
do {
junk = getc(stdin);
printf("junk: %c (int %d)\n", junk, (int)junk); // VERBOSE.
}
while (junk != '\n' && junk != EOF);
// THEN try to read user's latest text string:
scanRet = scanf("%27[^\n]",buf); // [^\n]== accept ALL chars except newline
if(scanRet < 1) { // format conversion failed. Nothing new in buf.
printf("(empty line)\n");
}
else {
printf("%s\n", buf); // non-empty user string
}
}
return 0; // normal exit.
}
回答by rasmus
One option is to read one line at a time using getsand then parse each line using sscanf.
一种选择是使用一次读取一行,gets然后使用sscanf.
EDIT based on comments: Using fgetsis more appropriate since you can avoid buffer overrun.
基于评论的编辑:使用fgets更合适,因为您可以避免缓冲区溢出。

