当我按下“q”键时,如何停止接受用户的输入?

2024-01-14

我整天研究这个问题,并看到所有其他类似的问题,但不知怎的,我错过了一些东西,我的代码根本不起作用。我注意到有一个错误;当程序按升序对数字进行排序时,通常不会读取第一个和最后一个数字。我只是好奇为什么会出现这个问题以及如何让它发挥作用。我想我的代码完全是荒谬的,所以我不想浪费你的时间,如果你能给出一些提示,这对我来说就足够了:)

#include <stdio.h>

int main ()
{
    int i,b,c,n;
    int number[200];
    char option;
 do
    {
        printf("Please enter numbers you wanna input: ");
        scanf("%d", &n);
        for(int i=0; i<n; ++i) {
        scanf("%d",&number[i]);
        }
    option = getchar ();
    while(getchar() != '\n');
}
while (option != 'q');


for (i=0; i<n; ++i)
    {
        for (b=i+1; b<n; ++b) 
          {
            if (number[i]>number[b])
            {
                c=number[i]; //i used c as temp.
                number[i]=number[b];
                number[b]=c;
            }
          }
    }
    printf("Sorted order of given numbers is this: \n");
    for (i=0; i<n; ++i)
        printf("%d\n",number[i]);

    return 0; }

正如@MikeCAT 所解释的,您当前的问题是:

do
{
    printf("Please enter numbers you wanna input: ");
    scanf("%d", &n);
    for(int i=0; i<n; ++i) {
        scanf("%d",&number[i]);
    }
    option = getchar ();         /* reads '\n' left in stdin by scanf */
    while(getchar() != '\n');    /* stdin empty -- getchar() blocks waiting for next input */
} while (option != 'q');

它揭示的真正问题是scanf() 充满陷阱并且应该避免用于用户输入,直到您充分理解它并知道为什么它不应该用于用户输入。这意味着了解每个转换说明符以及它是否会消耗前导空白,并进一步了解发生的情况匹配失败以及您必须采取哪些措施来丢弃有问题的输入以纠正问题。这甚至没有达到无法验证每个输入和转换的程度。

这些只是目前为止的冰山一角scanf()陷阱去——这就是为什么所有新的 C 程序员都被鼓励使用面向行的输入函数如fgets()或 POSIXgetline()对于所有用户输入。主要好处是面向行的输入函数每次都会消耗一整行用户输入——因此可能会留下未读的字符stdin只需等待下一个输入就可以消除(假设为输入提供了合理大小的数组)fgets())

Using fgets()对于用户输入

无论您使用哪种类型的输入函数,都不能使用任何正确的输入函数,除非您检查退货。除非您检查返回结果,否则您无法确定用户输入是否成功或失败。如果您在验证输入成功之前盲目地使用您认为保存输入的变量 - 您正在要求未定义的行为。规则:验证每个输入和每个转换...

那么如何使用fgets()?这真的很容易。只需提供一个足够大的缓冲区(字符数组)来容纳最长的预期用户输入,然后乘以 4(或合理的值)。指针是不要吝惜缓冲区大小。宁愿太长 10,000 个字符,也不愿太短 1 个字符。对于一般用户来说,1K 缓冲区就足够了(1024 字节)。如果猫踩到键盘,它甚至可以保护您。

如果您在内存有限的微控制器上进行编程,请将缓冲区大小减少到最大预期输入的 2 倍。 (让猫远离)

当需要将填充的字符数组转换为数字时,最简单的方法是调用sscanf()使用缓冲区作为输入(类似于您使用的方式scanf())。但这样做的好处是,如果转换失败也没关系,不会留下任何未读的内容stdin(读取已经发生,因此转换不可能影响输入流的状态)。如果允许同时输入多个整数,则strtol()可以处理从开始到结束的缓冲区转换值的工作。

使用面向行的函数读取字符串输入时唯一需要注意的是该函数将读取并包含'\n'作为它填充的缓冲区的一部分。如果您将该值存储为字符串,则需要删除它。你可以这样做strcspn()它返回拒绝列表中包含的任何字符之前的字符数。所以你只需使用"\n"作为您的拒绝列表,它会告诉您直到换行符的字符数。然后你只需使用该数字来覆盖'\n' with '\0'修剪'\n'从最后。例如,如果您正在读入名为的缓冲区line,那么您只需执行以下操作即可修剪'\n'从输入的末尾开始:

    char line[1024];
    
    if (fgets (line, sizeof line, stdin))
        line[strcspn (line, "\n")] = 0;

(note:在您的情况下,您只是简单地转换其中包含的内容line到一个数字——无需修剪'\n'反正,sscanf()将忽略空格,并且'\n'是空白)

那么在您的情况下,读取您的输入需要什么?只需声明一个缓冲区(字符数组)来保存用户输入并使用fgets()处理来自用户的所有用户输入。您可以为所有输入重复使用相同的缓冲区。您可以通过创建一个简短的函数来让自己的生活更轻松,该函数将要填充的数组和提示作为参数。然后如果没有提示NULL显示提示并读取用户输入——检查返回。

If fgets() returns NULL it means EOF was reached before any input was received (and it is perfectly valid for the user to cancel input by generating a manual EOF with Ctrl + d, or Ctrl + z on windows). So check the return, if the user canceled input, just handle that gracefully. You could write a getstr() function to help as:

#include <stdio.h>
#include <stdlib.h>     /* for qsort */

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXI  200
#define PROMPT "No. of intgers to read (max 200): "
...
/* fill array s with string, display prompt if not NULL
 * returns pointer to string on success, NULL otherwise
 */
char *getstr (char *s, const char *prompt)
{
    if (prompt)                                         /* if prompt not NULL, display */
        fputs (prompt, stdout);
    
    if (!fgets (s, MAXC, stdin)) {                      /* read entire line of input */
        puts ("(user canceled input)");                 /* handlie EOF case */
        return NULL;
    }
    
    return s;                   /* convenience return of s for immediate use if needed */
}

int main (void) {
    
    char buf[MAXC];                                     /* array to hold ALL input */
    int number[MAXI] = {0},                             /* initialize all arrays */
        i = 0, n = 0;
    
    while (i == 0 || i != n) {                          /* loop until numbers filled */
        if (!getstr (buf, PROMPT))                      /* validate EVERY user-input */
            return 0;
    ...

允许用户退出'q'(或在空行上按 [Enter])

当用以下内容填充数组时fgets()这使得响应任何角色并采取特殊行动变得非常非常容易。您刚刚填充了一个字符数组。如果您想检查特殊字符 - 只需检查数组中的第一个字符(元素)即可!这意味着您需要检查的只是中的字符buf[0](或者等价地只是*buf-- 这是缩写*(buf + 0)以指针表示法)

因此,为了允许用户在输入时退出'q'(或在空行上按回车键)您所需要的只是:

        if (buf[0] == 'q' || *buf == '\n')              /* exit on 'q' or empty-line */
            return 0;

您可以在任何时候使用它main()。如果您正在签入一个函数,您只需选择一个指示用户退出的返回(例如返回类型为int,只需选择一个整数,比如-1表示用户退出,保存0对于其他一些失败和1表示成功)。但既然你的逻辑是main()只是从返回main()很好。既然退出不是错误,return 0;(相当于exit (EXIT_SUCCESS);).

从数组转换整数值

如上所述,阅读后fgets(),如果用户没有退出,那么你的下一个工作就是将缓冲区中的数字转换为整数值。使用sscanf()类似于您尝试使用的方式scanf()很好。唯一的区别是sscanf()它是否将保存数字的缓冲区作为其第一个参数,例如

        if (sscanf (buf, "%d", &n) != 1) {              /* validate EVERY conversion */
            fputs ("  error: invalid integer input.\n", stderr);
            continue;
        }

在这里,如果转换失败,您将处理错误,然后continue返回循环顶部,允许用户“重试”。如果转换成功,则您的验证尚未结束。输入必须是正值,大于0且小于或等于200-- 否则与0没有什么可输入的,任何少于或多于的东西200你会调用未定义的行为尝试在数组边界之前或之外写入。只需添加该验证:

        if (n <= 0 || 200 < n) {                        /* validate input in range */
            fprintf (stderr, "  error: out of range, (0 < n <= %d)\n", MAXI);
            continue;
        }

与上次转换相同,如果用户出错,则处理错误并continue;允许用户“再试一次”。一旦您获得了要保存在数组中的整数的有效数字,读取各个值与读取要输入的整数的数量完全相同。在这里,您只需循环直到用户正确输入所有值:

        for (i = 0; i < n;) {                           /* loop reading n integers */
            printf ("number[%2d]: ", i+1);              /* prompt */
            if (!getstr(buf, NULL) ||                   /* read/validate input */
                *buf == 'q' || *buf == '\n')            /* quit on 'q' or empty line */
                return 0;
            if (sscanf (buf, "%d", &number[i]) != 1) {  /* validate conversion */
                fputs ("  error: invalid integer input.\n", stderr);
                continue;
            }
            i += 1;                                     /* only increment on good input */
        }

(note:价值如何i仅当用户提供有效输入时才会递增)

C 中的排序(qsort)

你正在学习C,无论你需要排序什么,C都提供了qsort()作为标准库的一部分,它将对您需要的任何数组进行排序(并且比尝试“进行更全面的测试并且更不容易出错”凭空创造出一种自己......". qsort()对任何类型对象的数组进行排序。唯一让新 C 程序员翻白眼的是需要编写一个compare函数告诉qsort()如何对数组进行排序。 (当你的眼睛向前滚动时,这真的很简单)

每个比较函数都有相同的声明:

int compare (const void *a, const void *b)
{
    /* cast a & b to proper type,
     *    return:  -1  - if a sorts before b
     *              0  - if a & b are equal
     *              1  - if b sorts before a
     */
}

const void *what??放松。a and b只是指向数组中元素的指针。 (qsort() uses a void*类型,以便它可以将任何类型对象传递给比较函数)编写比较函数的工作只是将它们转换回正确的类型,然后编写上面的逻辑。在你的情况下a是一个指向int (e.g. int*),所以你需要做的就是转换为(int*)然后取消引用指针以获取整数值,例如

/* qsort compare function, sort integers ascending
 * using result of (a > b) - (a < b) prevents overflow
 * use (a < b) - (a > b) for descending.
 */
int cmpint (const void *a, const void *b)
{
    int ia = *(int*)a,      /* a & b are pointers to elements of the array to sort */
        ib = *(int*)b;      /* cast to correct type and dereference to obtain value */
    
    return (ia > ib) - (ia < ib);   /* return difference:  -1 - a sort before b
                                     *                      0 - a and b are equal
                                     *                      1 - b sorts before a
                                     */
}

(note:只是返回ia - ib可以工作,但是如果ia是一个很大的负值并且ib一个大的正值,反之亦然。因此,您可以使用两个比较运算的差异来消除这种机会 - 尝试一下,选择两个数字ia and ib看看效果如何...)

现在如果我的数组是number我有n其中的元素,我命名了我的比较函数cmpint使用起来有多困难qsort()对中的值进行排序number array??

    qsort (number, n, sizeof *number, cmpint);          /* sort numbers */

(done!)

总而言之*

如果你把它全部包装到你的程序中,你最终会得到:

#include <stdio.h>
#include <stdlib.h>     /* for qsort */

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXI  200
#define PROMPT "No. of intgers to read (max 200): "

/* qsort compare function, sort integers ascending
 * using result of (a > b) - (a < b) prevents overflow
 * use (a < b) - (a > b) for descending.
 */
int cmpint (const void *a, const void *b)
{
    int ia = *(int*)a,      /* a & b are pointers to elements of the array to sort */
        ib = *(int*)b;      /* cast to correct type and dereference to obtain value */
    
    return (ia > ib) - (ia < ib);   /* return difference:  -1 - a sort before b
                                     *                      0 - a and b are equal
                                     *                      1 - b sorts before a
                                     */
}

/* fill array s with string, display prompt if not NULL
 * returns pointer to string on success, NULL otherwise
 */
char *getstr (char *s, const char *prompt)
{
    if (prompt)                                         /* if prompt not NULL, display */
        fputs (prompt, stdout);
    
    if (!fgets (s, MAXC, stdin)) {                      /* read entire line of input */
        puts ("(user canceled input)");                 /* handlie EOF case */
        return NULL;
    }
    
    return s;                   /* convenience return of s for immediate use if needed */
}

int main (void) {
    
    char buf[MAXC];                                     /* array to hold ALL input */
    int number[MAXI] = {0},                             /* initialize all arrays */
        i = 0, n = 0;
    
    while (i == 0 || i != n) {                          /* loop until numbers filled */
        if (!getstr (buf, PROMPT))                      /* validate EVERY user-input */
            return 0;
        if (buf[0] == 'q' || *buf == '\n')              /* exit on 'q' or empty-line */
            return 0;
        if (sscanf (buf, "%d", &n) != 1) {              /* validate EVERY conversion */
            fputs ("  error: invalid integer input.\n", stderr);
            continue;
        }
        if (n <= 0 || 200 < n) {                        /* validate input in range */
            fprintf (stderr, "  error: out of range, (0 < n <= %d)\n", MAXI);
            continue;
        }
        for (i = 0; i < n;) {                           /* loop reading n integers */
            printf ("number[%2d]: ", i+1);              /* prompt */
            if (!getstr(buf, NULL) ||                   /* read/validate input */
                *buf == 'q' || *buf == '\n')            /* quit on 'q' or empty line */
                return 0;
            if (sscanf (buf, "%d", &number[i]) != 1) {  /* validate conversion */
                fputs ("  error: invalid integer input.\n", stderr);
                continue;
            }
            i += 1;                                     /* only increment on good input */
        }
    }
    
    qsort (number, n, sizeof *number, cmpint);          /* sort numbers */
    
    puts ("\nsorted values:");                          /* output results */
    for (i = 0; i < n; i++)
        printf (i ? " %d" : "%d", number[i]);           /* ternary to control space */
    putchar ('\n');                                     /* tidy up with newline */
    
}

使用/输出示例

按预期使用:

$ ./bin/fgets_n_integers+sort
No. of intgers to read (max 200): 10
number[ 1]: 321
number[ 2]: 8
number[ 3]: -1
number[ 4]: 4
number[ 5]: -2
number[ 6]: 0
number[ 7]: -123
number[ 8]: 123
number[ 9]: 6
number[10]: 2

sorted values:
-123 -2 -1 0 2 4 6 8 123 321

滥用不良输入(故意)并使用'q'中途退出:

$ ./bin/fgets_n_integers+sort
No. of intgers to read (max 200): bananas
  error: invalid integer input.
No. of intgers to read (max 200): 0
  error: out of range, (0 < n <= 200)
No. of intgers to read (max 200): 201
  error: out of range, (0 < n <= 200)
No. of intgers to read (max 200): 5
number[ 1]: bananas again!!!!!!!!!!!!!!!!!
  error: invalid integer input.
number[ 1]: twenty-one
  error: invalid integer input.
number[ 1]: 21
number[ 2]: 12
number[ 3]: done
  error: invalid integer input.
number[ 3]: really
  error: invalid integer input.
number[ 3]: q

当你编写任何输入例程时——尝试打破它!如果失败,找出原因,修复并重试。当你尝试了你能想到的每一个糟糕的极端情况并且你的输入例程继续工作时——你可以对它感觉相当好——直到你给它的用户做了一些完全奇怪的事情并找到了一个新的极端情况(修复那个也是)

为您留下的极端情况

如果用户想要输入会发生什么1数组中的数字?有排序的理由吗?返回并查看代码并找出可以阻止用户输入的位置1作为有效输入或,仅输出用户输入的第一个数字并跳过排序等......由您决定。

和往常一样,这件事的结束比我预期的要长得多。但是看看你的程序被困在哪里——没有捷径可以帮助你了解你需要做什么以及为什么不需要几个额外的(几十个)段落。所以这里有很多。放慢速度,消化它,与鸭子交谈(参见如何调试小程序 https://ericlippert.com/2014/03/05/how-to-debug-small-programs/——别笑,这有效)

然后,如果您还有其他问题,请在下面发表评论,我很乐意为您提供进一步帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我按下“q”键时,如何停止接受用户的输入? 的相关文章

  • ROWNUM 的 OracleType 是什么

    我试图参数化所有现有的 sql 但以下代码给了我一个问题 command CommandText String Format SELECT FROM 0 WHERE ROWNUM lt maxRecords command CommandT
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 检查 url 是否指向文件或页面

    我们需要以下内容 如果文件确实是文件 则从 URL 下载该文件 否则 如果它是一个页面 则什么也不做 举个简单的例子 我有以下命令来下载文件 My Computer Network DownloadFile http www wired c
  • char指针或char变量的默认值是什么[重复]

    这个问题在这里已经有答案了 下面是我尝试打印 char 变量和指针的默认值 值的代码 但无法在控制台上看到它 它是否有默认值或只是无法读取 ASCII 范围 include
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr

随机推荐