使用条件运算符进行评估后打印结果

huangapple go评论58阅读模式
英文:

Printing result after evaluation using conditional operator

问题

在执行下面的小段代码时,每次我输入一个字符,输出都会重复,我不明白为什么会这样。有人能解释一下吗?
附注:我刚开始学习C语言编程。

如果我打印字符如 'a',我应该得到输出1,然后另一个提示要求我再次输入字符。但我实际上得到的是1,一个提示,再加上一个1,然后又一个提示要求我输入字符。
英文:

While executing the small piece of code below, every time I enter a character, the output is repeated and I don't understand why. Can someone explain to me why is it behaving like this?
ps: Just started my programming journey in c.

If I print a character such as 'a' I'm supposed to have 1 as output then another prompt asking me to enter a character again. But I instead get the 1, a prompt, and a 1 again then another prompt asking me to enter a character.

#include <stdio.h>

int main()
{
  int usr_ch;
  for ( usr_ch = 0; (usr_ch != 'q');){
       printf("Enter a single character: (enter 'q' to stop)\n");
       usr_ch = getc(stdin);
       printf("%d\n", (usr_ch != 'q') ? 1 : 0));
  }
  return 0;
}

input: u
output:
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)

答案1

得分: 1

stdin默认是行定向的,当您输入“u\n”时,getc()会首先返回“u”,在下一个调用中返回“\n”。

在这种情况下,更自然的做法是使用do-while循环(或者for(;;)循环,在循环体的末尾使用break)。然后在循环中读取字母,直到找到您喜欢的一个(即不是“\n”)。处理EOF是个好主意。

#include <stdio.h>

int main() {
    int usr_ch;
    do {
        printf("输入一个单个字符:(输入 'q' 停止)\n");
        while ((usr_ch = getc(stdin)) && usr_ch == '\n');
        printf("%d\n", usr_ch != 'q');
    } while (usr_ch != 'q' && usr_ch != EOF);
}

以下是示例运行:

输入一个单个字符:(输入 'q' 停止)
a
1
输入一个单个字符:(输入 'q' 停止)
q
0

您还可以使用fgets()读取一行,或者使用scanf(" %c", &usr_ch)来忽略前导空格。

英文:

stdin, by default, is line oriented when you enter "u\n" so getc() will first return u and in the next call \n.

In this case it is more natural to use a do-while-loop (or a for(;;) with a break at the end of the loop body). Then read a letter in a loop till you find one you like (i.e. not a \n). It's a good idea to handle EOF.

#include &lt;stdio.h&gt;

int main() {
	int usr_ch;
	do {
		printf(&quot;Enter a single character: (enter &#39;q&#39; to stop)\n&quot;);
		while((usr_ch = getc(stdin)) &amp;&amp; usr_ch == &#39;\n&#39;);
		printf(&quot;%d\n&quot;, usr_ch != &#39;q&#39;));
	} while(usr_ch != &#39;q&#39; &amp;&amp; usr_ch != EOF);
}

and here is example runs:

Enter a single character: (enter &#39;q&#39; to stop)
a
1
Enter a single character: (enter &#39;q&#39; to stop)
q
0

AYou can also just fgets() a line or use scanf(&quot; %c&quot;, &amp;usr_ch) to ignore leading white space.

答案2

得分: 1

你已经有一个很好的答案,解释了当用户按<kbd>ENTER</kbd>时生成附加的&#39;\n&#39;字符。继续从问题下面的评论和@AllanWard的评论中,关于使用fgets()的能力,它可以提供接受所有单个字符输入并在仅按<kbd>ENTER</kbd>时结束输入的能力。还有许多其他好处。

使用fgets()读取一行时,你将输入读入缓冲区(字符数组或分配的内存块)。不要在缓冲区大小上吝啬... fgets()读取并包括填充缓冲区的尾随&#39;\n&#39;。这意味着在足够大的缓冲区的情况下,整行输入都会被消耗,包括尾随的&#39;\n&#39;。尾随的&#39;\n&#39;不会留在未读的输入缓冲区(stdin)中。这将避免你遇到的问题。

要访问数组中的第一个字符,你只需要解引用指针。 (数组在访问时会转换为指向其第一个元素的指针,C18标准-6.3.2.1(p3))。所以,如果你声明char line[1024];来保存输入,只需引用*line就可以访问第一个字符。

使用fgets()避免了新C程序员在使用scanf()时陷入的所有陷阱,同时消除了未读的&#39;\n&#39;。这些是鼓励新C程序员(以及不那么新的C程序员)使用fgets()(或与之相同的行为方式的POSIX getline())接受所有用户输入的主要原因。

除了接受输入外,不需要太多的工作,你可以确保用户只输入了一个可打印字符,通过进行一些简单的测试。这允许你根据需要处理各种错误情况。可以编写一个使用fgets()并处理几种可以预见的错误情况的简短示例,如下所示:

#include &lt;stdio.h&gt;
#include &lt;ctype.h&gt;

#define MAXC 1024     /* 如果需要常量,请使用#define定义一个或多个 */

int main (void)
{
  char line[MAXC];    /* 用于保存行的缓冲区 */
  
  /* 提示,然后将输入读入line */
  while (fputs ("输入单个字符:(单独输入回车键以停止): ", stdout) &&
         fgets (line, MAXC, stdin)) {
    /* 如果仅输入回车键,退出 */
    if (*line == '\n') {
      puts ("退出");
      break;
    }
    /* 如果不是单个字符,处理错误 */
    else if (line[1] != '\n') {
      fputs ("  错误:输入了多于1个字符。\n", stderr);
    }
    /* 如果是可打印字符,输出 */
    else if (isprint ((unsigned char)*line)) {
      printf ("  你输入了'%c'\n", *line);
    }
    else {  /* 否则,处理错误 */
      fputs ("  错误:生成了不可打印字符。\n", stderr);
    }
  }
}

(**注意:**这些只是你可以使用的分类测试的几个示例。你可以自由添加或删除尽可能多的测试。你甚至可以为不可打印字符提供一个查找表,并在按下时输出表示,例如&#39;\t&#39;,这完全取决于你。)

示例用法/输出

以下演示了每个涵盖的错误情况的练习(&#39;\t&#39;字符用于不可打印字符),例如。

$ ./bin/fgets-char
输入单个字符:(单独输入回车键以停止): a
  你输入了'&#39;a&#39;'
输入单个字符:(单独输入回车键以停止): b
  你输入了'&#39;b&#39;'
输入单个字符:(单独输入回车键以停止): q
  你输入了'&#39;q&#39;'
输入单个字符:(单独输入回车键以停止): Q
  你输入了'&#39;Q&#39;'
输入单个字符:(单独输入回车键以停止):
  错误:生成了不可打印字符。
输入单个字符:(单独输入回车键以停止): foo
  错误:输入了多于1个字符。
输入单个字符:(单独输入回车键以停止):
退出

使用getc()fgetc()getchar()接受单个字符输入是没有问题的,但你必须处理任何未读的额外字符(包括尾随的&#39;\n&#39;)。然后,如果用户连续两次按<kbd>ENTER</kbd>,或者猫踩到键盘上,生成500次按键怎么办?这就是fgets()可以帮助的地方。

另一种方法,不幸的是在不同操作系统之间不可移植,是将终端设置为原始的无缓冲(非规范)模式,其中输入会立即被处理。对于Linux,你可以使用tcsetattr()。 (你还可以使用setvbuf,参见man 3 setbuf以在无缓冲、行缓冲或完

英文:

You already have a great answer explaining the additional &#39;\n&#39; character generated when the user presses <kbd>ENTER</kbd>. Continuing from the comments below the question and comment by @AllanWard about the use of fgets(), it can provide the ability to take all single characters as input and end the input when <kbd>ENTER</kbd> alone is pressed. There are a number of other benefits as well.

When reading a line with fgets() you read the input into a buffer (character array or allocated block of memory). Don't skimp on buffer size... fgets() reads and includes the trailing &#39;\n&#39; in the buffer it fills. This means an entire line of input is consumed, including the trailing &#39;\n&#39; given a sufficiently sized buffer. The &#39;\n&#39; is not left in the input buffer (stdin) unread. This will avoid the problem you are experiencing.

To access the first character in the array, all you need do is derefernce the pointer. (an array is converted to a pointer to its first element on access, C18 Standard - 6.3.2.1(p3)). So if you declare char line[1024]; to hold the input, simply referencing *line provides access to the first character.

Using fgets() avoids all of the pitfalls new C programmers fall into using scanf() and eliminates the &#39;\n&#39; being left unread. These are the primary reasons new C programmers (as well as not so new C programmers) are encouraged to take all user input using fgets() (or POSIX getline() which behaves in the same manner, but can also provide auto-allocation to handle a string of any length)

In addition to taking the input, without much more effort you can ensure the user has only entered one-printable character with a few simple tests. This allows you to handle individual error cases as needed. A short example of the use of fgets() and handling several of the foreseeable error cases can be written as:

#include &lt;stdio.h&gt;
#include &lt;ctype.h&gt;

#define MAXC 1024     /* if you need a constant, #define one (or more) */

int main (void)
{
  char line[MAXC];    /* buffer to hold line */
  
  /* prompt and then read input into line */
  while (fputs (&quot;Enter a single character: (enter alone to stop): &quot;, stdout) &amp;&amp;
         fgets (line, MAXC, stdin)) {
    /* if ENTER alone, break */
    if (*line == &#39;\n&#39;) {
      puts (&quot;exiting&quot;);
      break;
    }
    /* if not a single-character, handle error */
    else if (line[1] != &#39;\n&#39;) {
      fputs (&quot;  error: more than 1 char entered.\n&quot;, stderr);
    }
    /* if printable character, output */
    else if (isprint ((unsigned char)*line)) {
      printf (&quot;  you entered &#39;%c&#39;\n&quot;, *line);
    }
    else {  /* otherwise, handle error */
      fputs (&quot; error: non-printable character generated.\n&quot;, stderr);
    }
  }
}

(note: these are only a few examples of the classification test you can use. You are free to add or remove as many as you like. You can even provide a lookup-table for non-printable character and output a representation, e.g. &#39;\t&#39;, when one is pressed, it's entirely up to you.)

Example Use/Output

The following exercises each of the covered error cases (the &#39;\t&#39; character is used for the non-printable character), e.g.

$ ./bin/fgets-char
Enter a single character: (enter alone to stop): a
  you entered &#39;a&#39;
Enter a single character: (enter alone to stop): b
  you entered &#39;b&#39;
Enter a single character: (enter alone to stop): q
  you entered &#39;q&#39;
Enter a single character: (enter alone to stop): Q
  you entered &#39;Q&#39;
Enter a single character: (enter alone to stop):
 error: non-printable character generated.
Enter a single character: (enter alone to stop): foo
  error: more than 1 char entered.
Enter a single character: (enter alone to stop):
exiting

There is absolutely nothing wrong with using getc() or fgetc() or getchar() for taking a single-character as input, but you must handle any additional characters that remain unread (including the trailing &#39;\n&#39;). Then what if the user presses <kbd>ENTER</kbd> twice in a row, or a cat steps on the keyboard generating 500 keypresses? That's where fgets() can help.

Another approach, unfortunately non-portable between different OS's, is to place the terminal in raw unbuffered (non-cannonical) mode where the input is processed immediately. For Linux you can use tcsetattr(). (you can also use setvbuf, see man 3 setbuf to switch between unbuffered, line-buffered or fully-buffered input) For Windows getch() can be used.

Worth exploring each as you continue your learning in C. Let me know if you have further questions.

huangapple
  • 本文由 发表于 2023年2月16日 08:03:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75466568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定