簡體   English   中英

接受C中的單個字符?

[英]Accepting a single character in C?

我打算做的是輸入字符並將其用作模式。 我已經嘗試過使用getchar(),但是它不起作用。 我聽說過使用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");
}

您需要將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");
}

您可以將%cscanf

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

這消除了對兩個getchar調用的需要。

需要空間; 它告訴scanf跳過空格。

您遇到的問題是您對getchar(3)假設不正確。 您認為getchar()將返回輸入流中按下的下一個鍵,但是您錯誤地認為它將在沒有緩沖或系統處理的情況下完成(終端驅動程序為程序提供了完整的行,或者更糟糕的是,從文件中讀取,必須緩沖完整的緩沖塊,因此您不會從輸入流中丟失任何字符)

您錯誤地假設為輸入到程序而需要按的行尾不計入輸入流。

實際發生的是:

  • 您輸入了完整的一行(因為內核驅動程序以這種方式工作),所以您按字符,然后什么也沒看到,而不是按return鍵之后。
  • 一旦按下它,您將有一個以上的字符(取決於按下返回鍵之前按下的字符數)保留在緩沖區中,直到被程序占用為止。 通常,當您執行更多的getchar()scanf()語句時,會發生這種情況。

這種緩沖機制的思想是允許程序員通過字符處理大量文本來處理字符 ,而不會因每次字符讀取而進行系統調用(這是一項昂貴的操作)的開銷,因此請考慮將getchar()視為示例函數讓新用戶了解編程領域,但這是對經驗豐富的程序員的有效提示,而無需考慮緩沖大量文本。

使用stdio軟件包,每個字符都很重要,因此在將輸入提供給getchar(3)時,您必須緩慢而謹慎地思考。

下一個問題是: 對,那么我如何解決並停止我的程序,直到按下某個鍵? 使用此處公開的工具集的第一個答案是,請小心輸入的內容 ,而不是要求任何鍵,而要用戶按回車鍵,然后執行類似的操作:

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 */

或者,如果你願意,你可以寫一個函數只是要做到這一點(因為可能有實現這么多的標准,無人包含在標准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 */

為了在原始模式下等待任何字符 ,首先需要確保輸入來自終端(您不能在普通文件上執行以下操作),然后必須將終端驅動程序切換到原始模式,因此每個字符立即將其提供給程序,並且不執行任何行編輯處理,然后將stdin描述符設置為完全沒有緩沖。 只有這樣,您才能通過鍵入鍵入getchar(3)來接收單個字符。我認為這超出了此問題的范圍,因為執行此操作的代碼比上面的復雜得多。

編輯

以下是該程序的完整示例,該程序使用原始輸入來處理鍵入的字符。

/* 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 */

完整的源代碼也可以從這里下載。 您可以使用該程序查看輸入鍵如何映射為字符,因為您會注意到,當您按enter鍵時,原始輸入為[0d] (aschari char 13,CARRY RETURN),而在常規行模式下,您將獲得'\\n'表示為[0a]或ASCII LINE FEED(如果您從pru.c文本文件中重定向輸入,則可以進行檢查)。 另外,您還將看到無法使用Ctrl-D從終端驅動程序中指定EOF ,並且Ctrl-C不能提供幫助。 好吧,我已經包含了一項保護措施,可以在您按ESC鍵時結束程序,該程序會生成ASCII ESCAPE字符( \\033 )。 在源代碼中也對此進行了注釋。

所有這些處理都是由內核驅動程序完成的,因此所有的UNIX實現都獲得相同的行尾字符或以相同方式解釋控制字符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM