【问题标题】:CS50 Caesar program is working but check50 says it isn'tCS50 Caesar 程序正在运行,但 check50 说它不是
【发布时间】:2020-08-20 05:00:07
【问题描述】:

我创建了这个程序,但我在 CS50 上遇到错误,表明我没有正确执行任何操作。

要求如下:

在名为 caesar 的目录中的名为 caesar.c 的文件中实现您的程序。

您的程序必须接受一个命令行参数,一个非负整数。为了便于讨论,我们称它为 k。

如果您的程序在没有任何命令行参数或多个命令行参数的情况下执行,您的程序应该打印您选择的错误消息(使用 printf)并从 main 返回值 1(这往往立即表示错误)。

如果命令行参数的任何字符不是十进制数字,您的程序应该打印消息 Usage: ./caesar key 并从 main 返回值 1。

不要假设 k 会小于或等于 26。您的程序应该适用于 k 小于 2^31 - 26 的所有非负整数值。换句话说,您不必担心如果如果用户选择的 k 值太大或几乎太大而无法放入 int,您的程序最终会中断。 (回想一下 int 可能会溢出。)但是,即使 k 大于 26,程序输入中的字母字符也应该在程序输出中保持字母字符。例如,如果 k 为 27,则

根据http://www.asciichart.com/[asciichart.com],即使 [ 在 ASCII 中距离 A 27 个位置,A 也不应该变为 [; A 应该变成 B,因为 B 离 A 有 27 个位置,前提是你从 Z 绕到 A。

您的程序必须输出明文:(不带换行符),然后提示用户输入明文字符串(使用 get_string)。

您的程序必须输出密文:(不带换行符)后跟明文对应的密文,明文中的每个字母字符“旋转”k 个位置;非字母字符应原样输出。

你的程序必须保持大小写:大写字母,虽然轮换,但必须保持大写字母;小写字母虽然旋转,但必须保持小写字母。

输出密文后,应打印换行符。然后你的程序应该通过从 main 返回 0 来退出。

我的代码:

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(int argc, string argv[])
{
    //check if k inputed
    if (argc != 2)
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }
    //value k is the number after ./caesar
    int k = atoi(argv[1]) % 26;
    int x = 0;
    int s = strlen(argv[1]);
    //check if k is a positive integer
    if (k < 0)
    {
        printf("Usage: .caesar key\n");
        return 1;
    }
    else
    {
        //check for arguments
        for (int i = 0; i < s; i++)
        {
            if (isalpha (argv[1][i]))
            {
                continue;
            }
            else if (isalnum (argv[1][i]))
            {
                x++;
            }
            else
            {
                continue;
            }
        }
        if (x != s)
        {
            printf("Usage: ./caesar key\n");
        }
        else if (x == s)
        {
            //get plaintext
            string plain_text = get_string("plaintext: ");
            printf("ciphertext: ");
            for (int y = 0; y <= strlen(plain_text); y++)
            {
                //change letters
                if (isalpha(plain_text[y]))
                {
                    char p = plain_text[y];
                    int cipher_int = p + k;
                    if (isupper(p))
                    {
                        while(cipher_int >= 90)
                        {
                            cipher_int -= 26;
                        }
                            char cipher_text = cipher_int;
                            printf("%c", cipher_text);
                    }
                    if (islower(p))
                    {
                        while(cipher_int >= 122)
                        {
                            cipher_int -= 26;
                        }
                            char cipher_text = cipher_int;
                            printf("%c", cipher_text);
                    }
                }
                else
                {
                    printf("%c", plain_text[y]);
                }
            }
            printf("\n");
        }
    }
    return 0;
}

【问题讨论】:

  • CS50 是否指定你在 StackOverflow 中询问如何解决你的作业?
  • 您执行的第二个测试(对于参数中的所有字符都是数字)不打印请求的消息,它打印 .caesar 而不是 ./caesar
  • @RadekDulny:您可以通过点击分数下方的灰色复选标记来接受其中一个答案

标签: c encryption cs50 caesar-cipher


【解决方案1】:

您的包装似乎无法正常工作。我发现当我使用 3 作为 key 并输入“The quick fox jumps over the lazy brown dog”时。作为纯文本,“brown”在应该是“eurzq”时变成了“eur`q”。我认为你在包装比较中使用了&gt;=,而你应该使用&gt;

【讨论】:

    【解决方案2】:

    您对数字的检查非常麻烦,如果参数不正确,不会导致程序按要求返回1

    这是一个更简单的测试:

            //check for arguments
            for (int i = 0; i < s; i++) {
                if (!isdigit((unsigned char)argv[1][i])) {
                    printf("Usage: ./caesar key\n");
                    return 1;
                }
            }
    

    还请注意,当索引 == 字符串的长度时,您应该停止编码循环。因此运营商应该是&lt;

    另一个问题是使用isalpha()&lt;ctype.h&gt; 中的类似函数和char 值。这些函数对于负值是未定义的(EOF 除外)。某些平台默认将 char 定义为已签名,如果用户键入非 ASCII 文本,则使 isalpha(plaintext[y]) 具有未定义的行为。将参数转换为 (unsigned char) 以避免此问题。

    此外,您不应使用硬编码的 ASCII 值,例如 90122,使用字符常量,例如 'a''z' 以获得更好的可读性。这样做会使编码循环中的另一个错误更加明显:while(cipher_int &gt;= 90) 应该是 if (cipher_int &gt; 'A')while(cipher_int &gt;= 122) 应该是 if(cipher_int &gt; 'z')

    这是修改后的版本:

    #include <cs50.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    int main(int argc, string argv[])
    {
        // check for a single command line argument
        if (argc != 2) {
            printf("Usage: ./caesar key\n");
            return 1;
        }
        char *arg = argv[1];
        if (*arg == '\0') {
            printf("caesar: key cannot be an empty string\n");
            return 1;
        }
        // check that the argument is a non negative number
        for (size_t i = 0; arg[i]; i++) {
            if (!isdigit((unsigned char)arg[i])) {
                printf("Usage: ./caesar key\n");
                return 1;
            }
        }
        // value k is the shift number after ./caesar
        int k = atoi(argv[1]) % 26;
    
        // get plaintext
        string plain_text = get_string("plaintext: ");
        printf("ciphertext: ");
        for (size_t i = 0; plain_text[i] != '\0'; i++) {
            unsigned char c = plain_text[i];
            // change letters
            if (islower(c)) {
                putchar('a' + ((c - 'a') + k) % 26);
            } else
            if (isupper(c)) {
                putchar('A' + ((c - 'A') + k) % 26);
            } else {
                putchar(c);
            }
        }
        printf("\n");
        return 0;
    }
    

    【讨论】:

    • 实际上,密钥验证确实有效,尽管它不是一个简单的实现。无论如何,使用isdigit() 会更干净。
    • OP 的密钥验证是计算位数,然后将计数与字符串长度进行比较。因此,至少“a”和“1a”未能通过验证。空字符串大小写确实通过并导致键为 0。
    • @FredLarson:确实如此,但以这种扭曲的方式,几乎是偶然的。此外,如果命令行参数在默认签名char 的平台上具有负字符,则isalpha(argv[1][i]) 具有未定义的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    • 2022-12-08
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多