简体   繁体   English

接受C中的单个字符?

[英]Accepting a single character in C?

What I intend to do is to get the character entered and used it as a pattern. 我打算做的是输入字符并将其用作模式。 I've tried using getchar() but it won't work. 我已经尝试过使用getchar(),但是它不起作用。 I've hear of using scanf but it skips and stops whenever I press "shift" for the special characters on my keyboard. 我听说过使用scanf,但是每当我按下键盘上的特殊字符“ shift”时,它就会跳过并停止。

int i, j, n;
char c;
c = getchar();
printf("Enter value of n: ");
scanf("%d", &n);

printf("Enter a Character: ");
getchar();

for(i=1; i<=n; i++)
{
    for(j=1; j<=i; j++)
    {
        printf("%c", c);
    }
    printf("\n");
}

You need to assign the value returned by getchar to the variable c , and you had a redundent call to getchar that's why it skips reading the desired input: 您需要将getchar返回的值分配给变量c ,并且对getchar进行了冗余调用,这就是它跳过读取所需输入的原因:

int i, j, n;
char c;
printf("Enter value of n: ");
scanf("%d", &n);

printf("Enter a Character: ");
scanf(" %c", &c);

for(i=1; i<=n; i++)
{
    for(j=1; j<=i; j++)
    {
        printf("%c", c);
    }
    printf("\n");
}

You can use %c with scanf : 您可以将%cscanf

scanf("%d %c", &n, %c);

This eliminates the need for the two getchar calls. 这消除了对两个getchar调用的需要。

The space is required ; 需要空间; it tells scanf to skip whitespace. 它告诉scanf跳过空格。

The problem you have is that your assumptions on getchar(3) are incorrect. 您遇到的问题是您对getchar(3)假设不正确。 You think getchar() is going to return the next key pressed in the input stream, but you are incorrectly assuming that it will be done without buffering or system processing (the terminal driver gives the program complete lines, or even worse, if you are reading from a file, complete buffer blocks, that have to be buffered so you miss no characters from the input stream) 您认为getchar()将返回输入流中按下的下一个键,但是您错误地认为它将在没有缓冲或系统处理的情况下完成(终端驱动程序为程序提供了完整的行,或者更糟糕的是,从文件中读取,必须缓冲完整的缓冲块,因此您不会从输入流中丢失任何字符)

You are assuming incorrectly that the end of line you need to press for the input to be feeded to the program does not count in the input stream. 您错误地假设为输入到程序而需要按的行尾不计入输入流。

What actually happens is: 实际发生的是:

  • you feed a complete line (because the kernel driver works that way) so you press your character, and then you see nothing, not after you have pressed the return key. 您输入了完整的一行(因为内核驱动程序以这种方式工作),所以您按字符,然后什么也没看到,而不是按return键之后。
  • once you press it, you have more than one character (depending on how many you pressed before hitting the return key) that will stay in the buffer, until they are so consumed by the program. 一旦按下它,您将有一个以上的字符(取决于按下返回键之前按下的字符数)保留在缓冲区中,直到被程序占用为止。 Normally this happens when you have executed more getchar() or scanf() statements. 通常,当您执行更多的getchar()scanf()语句时,会发生这种情况。

The idea of this buffering mechanism is to allow a programmer to process character by charcacter large amounts of text, without the overhead of making a system call per character reading (this is a costly operation) so think of getchar() not as a sample function to get new users introduced to the world of programming, but as a hint to experienced programmers to use efficiently without having to think on buffering large amounts of text. 这种缓冲机制的思想是允许程序员通过字符处理大量文本来处理字符 ,而不会因每次字符读取而进行系统调用(这是一项昂贵的操作)的开销,因此请考虑将getchar()视为示例函数让新用户了解编程领域,但这是对经验丰富的程序员的有效提示,而无需考虑缓冲大量文本。

With stdio package, every character counts, so you have to think slowly and minuciously when you feed input to getchar(3) . 使用stdio软件包,每个字符都很重要,因此在将输入提供给getchar(3)时,您必须缓慢而谨慎地思考。

The next question is: Right, then how can I solve and stop my program until I press some key? 下一个问题是: 对,那么我如何解决并停止我的程序,直到按下某个键? The first answer, with the set of tools you have exposed here is, be careful on what you input , instead of asking for any key, ask the user to press the return key, and then, do something like: 使用此处公开的工具集的第一个答案是,请小心输入的内容 ,而不是要求任何键,而要用户按回车键,然后执行类似的操作:

printf("Hit <ENTER> to continue"); fflush(stdout); /* so we get the line out, bypassing the buffering mechanism */
int c;
while ((c = getchar()) != EOF && c != '\n') {
    /* just ignore the character we have received */
}
/* c == '\n' || c == EOF, so we can continue */

or, if you prefer, you can write a function just to do this (as there can be so many criteria to implement it, nobody included such a function in the standard C library, my apologies for that. ;) ) 或者,如果你愿意,你可以写一个函数只是要做到这一点(因为可能有实现这么多的标准,无人包含在标准C库这样的功能,我该道歉。 ;)

void wait_for_enter()
{
    /* I use stderr, for two reasons:
     *   o stderr is normally unbuffered, so there's no need to fflush()
     *   o stdout can be redirected, so the prompt will not be visible in
     *     case you want to save the output of your program.
     */
    fprintf(stderr, "Hit <ENTER> to continue"); 
    int c;
    while ((c = getchar()) != EOF && c != '\n') {
        /* just ignore the character we have received 
         * until we get the end of file (ctrl-d at the terminal)
         * or a new line */
    }
    /* c == '\n' || c == EOF, so we can continue */
    /* it's assumed that the user pressed the enter key, so the echoed
     * enter already did a newline, no need to do it here */
} /* wait_for_enter */

In order to wait for any character and in raw mode, you need first to ensure your input comes from a terminal (you cannot do the following on a normal file), then you have to switch the terminal driver to raw mode, so each character is given immediately to the program and no line editing processing is done, and then set the stdin descriptor to no buffering at all. 为了在原始模式下等待任何字符 ,首先需要确保输入来自终端(您不能在普通文件上执行以下操作),然后必须将终端驱动程序切换到原始模式,因此每个字符立即将其提供给程序,并且不执行任何行编辑处理,然后将stdin描述符设置为完全没有缓冲。 Only then, you can receive individual characters with getchar(3) , one by one, as they are keyed in. I think this is far out of the scope of this question, as the code to do that is far more complex than the above. 只有这样,您才能通过键入键入getchar(3)来接收单个字符。我认为这超出了此问题的范围,因为执行此操作的代码比上面的复杂得多。

EDIT 编辑

Following is a complete sample of a program that uses raw input to process characters as they are keyed in. 以下是该程序的完整示例,该程序使用原始输入来处理键入的字符。

/* pru.c -- program to show raw input from the terminal.
 * Author: Luis Colorado <luiscoloradourcola@gmail.com>
 * Date: Fri Sep 20 08:46:06 EEST 2019
 * Copyright: (C) 2019 Luis Colorado.  All rights reserved.
 * License: BSD.
 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>  /* see termios(3) for a description on terminal conf */

#define F(_fmt) __FILE__":%d:%s: " _fmt, __LINE__, __func__

/* this function switches the terminal into raw mode and returns a malloc(3)ed
 * terminal configuration, so it can be later restored. BEWARE that the returned
 * configuration info must be deallocated by free(3) once it's not needed anymore.
 * In case of failure of any system call, the function returns NULL, and errno is
 * set to the failing cause.  */
struct termios *set_raw(int fd)
{
    struct termios *ret = malloc(sizeof *ret), cfg;
    if (!ret) return NULL;

    int res = tcgetattr(fd, &cfg);
    if (res < 0) goto error;

    *ret = cfg; /* save it for return */

    cfmakeraw(&cfg);

    /* set it after all buffered characters in the driver have drained out */
    res = tcsetattr(fd, TCSADRAIN, &cfg);
    if (res < 0) goto error;

    return ret;

error:
    free(ret);
    return NULL;
} /* set_raw */

/* restores the configuration back to the associated file descriptor */
int restore_cfg(int fd, struct termios *cf)
{
    /* set it after all buffered characters in the driver have drained out */
    return tcsetattr(fd, TCSADRAIN, cf);
} /* restore_cfg */ 

int main()
{
    struct termios *cfg = set_raw(fileno(stdin));
    if (!cfg) {
        fprintf(stderr, F("stdin: %s\n"),
                strerror(errno));
    }

    setbuf(stdin, NULL); /* stdin unbuffered */
    setbuf(stdout, NULL); /* stdout unbuffered */

    /* BEWARE that raw mode doesn't process any characters, so no Ctrl-C(interrupt), Ctrl-D(EOF), etc.
     * will be available, only if you read from a file, you'll get EOF, but you'll not be able to produce
     * that on the terminal, you'll need to swith to another console and kill the process. */
    int c;
    while ((c = getchar()) != EOF && c != '\033') {  /* ESCAPE key('\033') is a safeguard to end input */
        /* print the input char as an hex number */
        printf("[%02x]", c);
    }

    if (cfg) { /* if we were able to set the terminal to raw mode */
        /* restore config */
        restore_cfg(fileno(stdin), cfg);

        /* and free it */
        free(cfg);
    }

    exit(EXIT_SUCCESS);
} /* main */

The full source code can be also downloaded from here . 完整的源代码也可以从这里下载。 You can use this program to see how input keys get mapped into characters, as you'll note that when you press the enter key, the raw input is [0d] (ascii char 13, CARRY RETURN) while in normal line mode you get '\\n' which is [0a] or ASCII LINE FEED, instead (you can check this if you redirect input from the pru.c text file). 您可以使用该程序查看输入键如何映射为字符,因为您会注意到,当您按enter键时,原始输入为[0d] (aschari char 13,CARRY RETURN),而在常规行模式下,您将获得'\\n'表示为[0a]或ASCII LINE FEED(如果您从pru.c文本文件中重定向输入,则可以进行检查)。 Also you'll see that you are unable to specify EOF from the terminal driver with Ctrl-D and that Ctrl-C does not come to help. 另外,您还将看到无法使用Ctrl-D从终端驱动程序中指定EOF ,并且Ctrl-C不能提供帮助。 Well, I have included a safeguard, by ending the program in case you press the ESC key, which generates an ASCII ESCAPE character ( \\033 ). 好吧,我已经包含了一项保护措施,可以在您按ESC键时结束程序,该程序会生成ASCII ESCAPE字符( \\033 )。 This is also commented in the source code. 在源代码中也对此进行了注释。

All of this processing is done by the kernel driver, so all unix implementations get the same line end characters or interpret the control characters the same way. 所有这些处理都是由内核驱动程序完成的,因此所有的UNIX实现都获得相同的行尾字符或以相同方式解释控制字符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM