【问题标题】:How should I make users to put in the info that I want?我应该如何让用户输入我想要的信息?
【发布时间】:2020-04-24 01:49:07
【问题描述】:

我刚开始学习计算机科学。

我正在通过在哈佛在线教授的 CS50 学习。 好吧,我正在解决这个问题,我需要在命令行中从用户那里获取密钥, 然后是纯文本,然后将该文本转换为 ASCII 中的关键数字量以生成密文。

这是我到目前为止所得到的。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./caesar key\n");
    }

    {
        string plaintext = get_string("plaintext:  ");

        int key = atoi(argv[1]);
        int n = strlen(plaintext);
        char chr[n];

        printf("ciphertext: ");
        for (int i = 0; i < n; i++)
        {
            chr[i] = plaintext[i];
            if (isalpha(chr[i]))
            {
                if (isupper(chr[i]))
                {
                    chr[i] = (chr[i] - 65 + key) % 26 + 65;
                    printf("%c",chr[i]);    
                }
                else if (islower(chr[i]))
                {
                    chr[i] = (chr[i] - 97 + key) % 26 + 97;
                    printf("%c",chr[i]);    
                }            
            }
            else
            {
                printf("%c",chr[i]);        
            }
        }
    printf("\n");
    }
}

好吧,我知道这看起来很松散,但是伙计,这是我在全职工作的同时进行编程的第二周。

无论如何,我正在尝试让用户使用 ./caesar "任意数字的键"。

如果用户放入任何其他东西,那么我想打印 "用法:./凯撒键\n"

到目前为止,我能想到的唯一方法是使用 argc 进行 if 语句!= 2 这样我至少可以确保用户在程序名称之上只输入一个命令。

但问题是,如果用户放入其他内容,例如 ./凯撒你好 ./凯撒YolO

程序仍在运行。

我正在努力弄清楚我能做些什么来防止这种情况发生。

非常感谢您抽出宝贵时间阅读本文并提供帮助。

【问题讨论】:

  • 看起来您在最初的 if 之后缺少一个 else
  • 我建议使用'A' 代替65'a' 代替97
  • 我很确定你的 main 应该是 int main(int argc, char* argv[]) 参见 this SO Q/A - 除非我在 C++ 方面落后了......
  • C 中没有string 类型。
  • @1005hoon RobertS - 恢复 Monica 的观点很好,这应该是 C 还是 C++?

标签: c cs50 caesar-cipher


【解决方案1】:

重新表述您的问题:您希望对用户输入执行验证,其中唯一合法的输入值是数字字符串:1234534000 都可以被认为是有效的,但 hello world 和 @ 987654325@ 和 one hundred thirty two 无效。

在包括 C 在内的大多数编程语言中,字符串或多或少只是单个 char 元素的数组(换句话说,char 是原始数据类型,但 string 不是......根本没有string 数据类型。提示:看看你声明main 的方式。那么如何利用它来验证该程序的输入呢?很简单:输入字符串中的每个字符都必须来自域[0123456789](加上最后的空终止符\0...)。

所以要解决这个问题,你只需要实现一个检查来验证输入的值是否具有该属性。有很多方法可以做到这一点,其中最简单的可能是:

int num;
int y = scanf("%d", &num);

然后检查y 的值。它将设置为scanf 读取的有效输入的数量(因此,如果给出了数字,它将为 1)。

注意:
如 cmets 中所述,这也将接受 123abc 之类的内容(它会去掉数字并忽略末尾的字符)。如果您绝对只能输入数字,scanf 可能不是正确的功能。在这种情况下,使用fgets 将输入读取为字符串,然后遍历每个输入以检查它是否来自有效的输入域可能是更好的方法。

【讨论】:

  • 但如果输入是,例如,“123abc”,那么scanf 仍然会在输入无效时返回 1。不,我不是拒绝你的答案的人,
  • 随意使用我的回答中的%1s 部分来解决@LxerLx 提出的问题(也不是反对者......)
  • 好点。我添加了一条注释来解释scanf 在这种情况下的行为。我认为这对于 OP 的目的来说仍然是一个很好的方法,因为它可以最大限度地减少复杂性,只要理解这个警告,所以我现在就把它留在这里,不管反对者是否反对 :)
【解决方案2】:

这就是旧的atoi 函数被strtol 取代的原因。前者只尝试从给定的字符串转换一个初始数字部分,而后者也告诉转换后的部分之后还剩下什么。

因此,为了确保用户提供了一个数字作为唯一参数,您可以这样做:

int main(int argc, char *argv[]) {
    long key;
    int reject = 0;

    if (argc != 2) {                         // ensure one argument
        reject = 1;
    }
    else {
        char *end;
        key = strtol(argv[1], &end, 10);    // ensure not empty and numeric
        if ((*end != 0) || (end == argv[1])) reject = 1;
     }
    if (reject) {
        printf("Usage: %s key\n", argv[0]);
    return 1;
}

sscanf 也可以使用。它甚至允许数字后有空白字符,这不应该发生,因为 argv 数组是空格分隔的:

int main(int argc, char *argv[]) {
    int key;
    char empty[2];

    if ((argc != 2) || (sscanf(argv[1], "%d%1s", &key, empty) != 1)) {
        printf("Usage: %s key\n", argv[0]);
        return 1;
    }
    ...

【讨论】:

  • 使用strtol 时,结果值可能超出范围。 errno 应与 ERANGE 核对。
  • @LxerLx:如果值超出范围,则始终返回 LONG_MAX(或 LONG_LMIN 为负值)。所以它可能不是给定的值,但仍然可以用作键。无论如何,为凯撒密码使用大于 26 的密钥并没有真正意义......
  • 如果使用大于 26 的键没有真正意义,那么超出该范围的值,即负值和大于 26 的值也应该被拒绝。而且,“所以它可能不是给定的值......”通常是一个大问题。
  • @LxerLx:我必须承认我在吹毛求疵...当然你是对的,应该始终测试超出范围的值:-)。我的评论只是坚持strtol 即使在超出范围的情况下也会返回合理的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多