【问题标题】:Encryption with substitution cipher does not generate valid ASCII output使用替换密码加密不会生成有效的 ASCII 输出
【发布时间】:2020-05-04 20:16:05
【问题描述】:

我不明白为什么它说“输出无效的 ASCII 文本”!

为了让您了解问题的背景,我发布了所描述的操作。 从这里开始!

在替换密码中,我们通过将每个字母替换为另一个字母来“加密”(即以可逆方式隐藏)消息。为此,我们使用一个密钥:在这种情况下,将字母表中的每个字母映射到我们加密时它应该对应的字母。要“解密”消息,消息的接收者需要知道密钥,以便他们可以反转过程:将加密文本(通常称为密文)翻译回原始消息(通常称为明文)。

例如,一个键可能是字符串NQXPOMAFTRHLZGECYJIUWSKDVB。这个 26 个字符的键意味着A(字母的第一个字母)应该转换成N(键的第一个字符),B(字母的第二个字母)应该转换成@ 987654325@(键的第二个字符),以此类推。

然后,像HELLO 这样的消息将被加密为FOLLE,根据密钥确定的映射替换每个字母。

让我们编写一个名为替换的程序,使您能够使用替换密码来加密消息。在用户执行程序时,他们应该通过提供命令行参数来决定他们将在运行时提供的秘密消息中的密钥。

以下是该程序可能如何工作的几个示例。例如,如果用户输入了YTNSHKVEFXRBAUQZCLWDMIPGJO的密钥和HELLO的明文:

$ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext:  HELLO
ciphertext: EHBBQ

如果用户提供VCHPRZGJNTLSKFBDQWAXEUYMOI 的密钥和hello, world 的明文,程序可能会这样运行:

$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext:  hello, world
ciphertext: jrssb, ybwsp

请注意,逗号和空格都没有被密码替换。只能替换字母字符!还要注意,原始消息的大小写已被保留。小写字母保持小写,大写字母保持大写。

键本身的字符是大写还是小写无关紧要。 VCHPRZGJNTLSKFBDQWAXEUYMOI 的键在功能上与 vchprzgjntlskfbdqwaxeuymoi 的键相同(就此而言,VcHpRzGjNtLsKfBdQwAxEuYmOi)。

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

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./substitution KEY");
        return 1;               
    }
    else if (argc == 2)
    {
        string text = argv[1];
        string storing = text;
        int counter = 0;
        int i = 0;
        bool number = true;
        bool flag = true;
        while (flag == true && i < 26)
        {
            if ((int)text[i] >= 48 && (int)text[i] <= 57)
            {
                 number = false;
            }
            if (((int)text[i] >= 65 && (int)text[i] <= 90) || ((int)text[i] >= 97 && (int)text[i] <= 122))
            {
                counter++;
                for ( int j = 0; j < counter - 1; j++)
                {
                    if ((int)storing[j] == (int)text[i] || (int)storing[j] + 32 == (int)text[i])
                    {
                        flag = false;
                    }
                }
            }
            i++;
        }
        if (number == false)
        {
            printf("Key must only contain alphabetic characters.");
            return 1;
        }
        if (flag == false)
        {
            printf("Key must not contain repeated characters.");
            return 1;
        }
        if (counter < 26)
        {
            printf("Key must contain 26 characters.");
            return 1;
        }
    }
    string plaintext = get_string("plaintext:");
    string key = argv[1];
    int counter;
    bool not_letter;
    bool capital1;
    bool capital;
    int crypto[strlen(plaintext)];
    for (int i = 0; i < strlen(plaintext); i++)
    {
        capital1 = false;
        capital = false;
        not_letter = false;
        if ((int)plaintext[i] >=65 && (int)plaintext[i] <= 90)
        {           
            counter =  (int)plaintext[i] - 65;            
            capital = true;
        }
        else if ((int)plaintext[i] >=97 && (int)plaintext[i] <= 122)
        {
            counter =  (int)plaintext[i] - 97;                          
            capital1 = true;
        }
        else
        {
            not_letter = true;
        }
        if (not_letter == true)
        {
            crypto[i] = (int)plaintext[i];
        }
        else if (capital == true)
        {
            if ((int)key[i] >=65 && (int)key[i] <= 90)
            {
                crypto[i] = (int)key[counter];
            }
            else if ((int)key[i] >=97 && (int)key[i] <= 122)
            {
                crypto[i] = (int)key[counter] - 32;
            }
        }
        else if (capital1 == true)
        {
            if ((int)key[i] >=65 && (int)key[i] <= 90)
            {
                crypto[i] = (int)key[counter] + 32;
            }
            else if ((int)key[i] >=97 && (int)key[i] <= 122)
            {
                crypto[i] = (int)key[counter];
            }  
        }
    }
    printf("ciphertext: ");
    for (int i = 0; i < strlen(plaintext); i++)
    {
        printf("%c", (char)crypto[i]);
    }
    printf("\n");
    return 0;
}

当我用 CS50 的 check50 cs50/problems/2020/x/substitution 测试程序时,它说:

:) substitution.c exists
:) substitution.c compiles
:) encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
! :( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
    output not valid ASCII text
! :( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
    output not valid ASCII text
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key

【问题讨论】:

  • 不要硬编码 ASCII 码。使用isalpha()tolower()等内置函数。
  • 并使用函数。一个用于验证密钥,一个用于制作字典或其他任何东西,一个用于编码字符串。简单的函数更容易理解和调试。
  • 当明文字符为大写且替换也为大写时,您的程序会产生非 ASCII 输出。我认为这应该为您指明正确的方向。
  • 其实比这更令人困惑。如果您提供纯文本abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,它会为A 生成一个非ASCII 替换。但如果明文只是ABCDEFGHIJKLMNOPQRSTUVWXYZ,则密文是正确的。
  • 所以你似乎让一些上/下状态在字符之间持续存在。

标签: c arrays string encryption cs50


【解决方案1】:

假设您已将密钥转换为大写。

然后让我们简化你的循环:

int keyIndex;
bool lowercase;
for (int i = 0; i < strlen(plaintext); i++) {
    if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') {
        keyIndex = plaintext[i] - 'A';
        lowercase = false;
    } else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') {
        keyIndex = plaintext[i] - 'a';
        lowercase = true;
    } else {
        // do not encrypt that character
        ciphertext[i] = plaintext[i];
        // and skip the rest of the loop
        continue;
    }

    ciphertext[i] = key[keyIndex];

    // revert back to lowercase, if necessary
    if (lowercase) {
        ciphertext[i] += 'a' - 'A';
    }
}

您遇到的问题是您没有将大问题分解为小问题。所以如果你例如提前创建一个全大写的密钥,以后就不需要考虑了。

保持应用程序的对称性也很重要。因此,如果您在if 的一部分中分配一个值,那么也在else 中执行此操作。如果您能够提前停止(例如需要保留角色时),请这样做。

当然,将密钥转换为全小写,然后在必要时将字符转换回大写也是同样有效的。

永远不要使用变量名,例如counteruppercase1。它使您的代码很难阅读;变量名称应尽可能清晰。

ciphertext 数组只是上面代码中的字符串/字符数组。如果您使用的是 C 而不是 C++,那么您通常会使用 char*char[],我想。

当然,这仍然是没有使用任何函数之间的分隔。它也不使用任何平台功能。如果您设法做到这一点,那么调试/维护您的应用程序将变得更加容易。

【讨论】:

    猜你喜欢
    • 2017-08-16
    • 2021-01-30
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多