如何在 vigenere cipherkey cs50 pset2 中重用(循环)密钥

Posted

技术标签:

【中文标题】如何在 vigenere cipherkey cs50 pset2 中重用(循环)密钥【英文标题】:How to reuse(loop) key in vigenere cipherkey cs50 pset2 【发布时间】:2017-08-13 17:54:41 【问题描述】:

我正在为 Vigenere 密码编写程序。我让程序成功打印密文。但是,我无法循环密钥。因此,如果我的密钥是 'abc' 而我的纯文本是 hello,它应该打印 'hfnlp' 而不是 'hfn'。

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

int main(int argc, string argv[])

    if(argc != 2)
    
        printf("\aError\n");
        return 1;
    
    else
    
        string a = argv[1]; // converts argv[1]

        printf("plaintext: ");
        string b = get_string(); // takes the plaintext

        printf("ciphertext: ");

        for(int c = 0, d = strlen(a); c < d; c++)
        
            for(int e = 0, f = strlen(b); e < f; e++)
            
                if(islower(a[c]))
                    
                        printf("%c\n",  b[e] + ( (a[c] - 97) % 26) ); // works for lowercase letters
                        return 0;
                    
                    else if(isupper(a[i]))
                    
                        printf("%c\n",  b[e] + ( (a[c] - 65) % 26) ); // works for uppercase letter
                    
                    else
                    
                         printf("%c", b[e]); // works for non alphabetical inputs
                    

                    if(true)
                        break;
            
   

    printf("\n");


【问题讨论】:

当你到达钥匙的末端时,你必须回到起点。模运算符% 经常用于管理这个,尽管一个简单的条件也可以。您需要一个计数器来单步执行纯文本;您需要一个单独的计数器来多次循环访问密钥(通常,除非纯文本比密钥短)。你也只需要一个循环,而不是两个嵌套循环——它应该单步执行纯文本(代码中的b)。请注意,初始化 string a = argv[1]; 中没有“转换”——您只是将指针复制到更方便的名称。 另外,错误信息应该打印到标准错误中,当命令行出现问题时,指示如何使用程序通常比仅仅说“错误”要好。 fprintf(stderr, "Usage: %s key\n", argv[0]); 会更好;你甚至可以使用fprintf(stderr, "Usage: %s key &lt; plain-text\n", argv[0]); 来指示如何将纯文本输入到程序中。 @JonathanLeffler 我删除了 if(true) 语句的代码。但是现在,它打印出错误的密码字母。公式写在代码中。但不是得到“一些”字母更正。它输出错误。 [对不起,如果我的英语有错误] 【参考方案1】:

您选择的单字母变量名称很奇怪;它使您的代码更难使用。我也不喜欢长名称,但中等长度的变量名称(2-8 个字符 - 除了一些风格化的单字母名称(cijkp , s) — 通常是合适的)。

您遇到了麻烦,因为如果您的键是 6 个字符,而您的字符串是 24 个字母字符,由于循环结构,您将尝试输出 144 个字母字符。您只需要一个循环来遍历纯文本中的字符。您有一个单独的变量,它在密钥的长度上循环,当它用完时重置回开始。在这段代码中,密钥长度在keylen(您使用d)中,密钥的偏移量(索引)在keyoff(您使用c)——但密钥仍在a因为那是你用的。留给我自己的设备,我可能会使用text(或者plain)代替btextlen代替f,我会使用i而不是@987654338 @ 循环变量。如果我想使用短索引,我可能会使用k 而不是keyoff。我也可以在原地编辑字符串并在最后打印整个字符串。

此代码还确保键中的字母字符为小写。它不能确保密钥都是 alpha;可以说应该这样做并且这样做是微不足道的,因为无论如何都会扫描密钥。就目前而言,这是一个 GIGO 的案例——垃圾进,垃圾出。

代码通过减去aA将输入字母(a-zA-Z)转换为“字母偏移量”,将键字母转换为字母表的偏移量,将两者相加偏移量模 26(字母表中的字母数),并将偏移量转换回适当大小写的字母。

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

int main(int argc, string argv[])

    if (argc != 2 || strlen(argv[1]) == 0)
    
        fprintf(stderr, "Usage: %s key < text\n", argv[0]);
        return 1;
    

    string a = argv[1];
    int keylen = strlen(a);
    for (int i = 0; i < keylen; i++)
        a[i] = tolower((unsigned char)a[i]);
    printf("key: %s\n", a);

    printf("plaintext: ");
    string b = get_string();

    printf("ciphertext: ");

    int keyoff = 0;
    // Step through each character of the plain text.  Encrypt each
    // alpha character with the (lower-case) key letter at a[keyoff],
    // incrementing keyoff.  Don't increment key offset when processing
    // non-alpha data.
    for (int e = 0, f = strlen(b); e < f; e++)
    
        if (islower(b[e]))
            printf("%c", ((b[e] - 'a') + (a[keyoff++] - 'a')) % 26 + 'a');
        else if (isupper(b[e]))
            printf("%c", ((b[e] - 'A') + (a[keyoff++] - 'a')) % 26 + 'A');
        else
            printf("%c", b[e]);
        if (keyoff >= keylen)
            keyoff = 0;
    
    printf("\n");
    return 0;

编译成程序vc41并运行时,会产生,例如:

$ vc41 abcdef
key: abcdef
plaintext: The quick brown Fox jumped over the lazy Dog.
ciphertext: Tig tyncl dusbn Gqa nzmqgg saes vki qaaa Gsl.
$

我生成了一个 8 个字母的随机密钥(它是 GZlfmTMk)并在多个“完整字母”字符串上运行代码:

$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: Pack my box with five dozen liquor jugs.
ciphertext: Vznp yr nyd vtyt yufk czeqg xswtzw vnsc.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: The five boxing wizards jump quickly.
ciphertext: Zgp kuoq luwtss pujgqox vnyz wtthwek.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: How vexingly quick daft zebras jump.
ciphertext: Nnh aqquxmkj vgbou jzqy lxnbgr uzyi.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: Bright vixens jump; dozy fowl quack.
ciphertext: Hqtltm hsddyx vnyz; jnkd rhiv wtlhw.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: The quick brown fox jumps over the lazy dog.
ciphertext: Zgp vgbou hqzbz yah ptxue hhox ssj xtli jnr.
$

(我会注意到,在使用 GCC 7.1.0 运行 macOS Sierra 10.12.6 的 Mac 上,此代码链接不包含(新)CS50 库 - 有一个系统函数 get_string() 具有不同的满足参考但程序崩溃的CS50版本的接口。但是man get_string没有记录它,所以我不确定该名称的系统功能实际上是什么;我没有更积极地追逐它,或者发现问题有多广泛。这让我很头疼,旧的 CS50 库没有。抱怨......)

【讨论】:

【参考方案2】:

这样修复

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

int main(int argc, string argv[])
    if(argc != 2 || !*argv[1])
        printf("\aError:The number of command arguments is incorrect.\n");
        printf("Usage: %s key_string\n", argv[0]);
        return 1;
    
    //Since it is `return 1;` in the if-statement, 
    //the else clause is unnecessary (unnecessarily deepening the nest)
    string key = argv[1];//It does not convert.
    size_t i, key_len;
    unsigned char curr_char;
    for(i = 0; (curr_char = key[i]) != '\0'; ++i)
        if(!isalpha(curr_char))
            printf("\aError:Only the alphabet can be specified as the key.\n");
            return 1;
        
        key[i] -= islower(curr_char) ? 'a' : 'A';//Convert to Deviation
    
    key_len = i;
    i = 0;

    printf("plaintext : ");
    string plain = get_string();

    printf("ciphertext: ");

    for(size_t j = 0; (curr_char = plain[j]) != '\0'; ++j)//Scan of plain text should be the main loop.
        if(isalpha(curr_char))
            char base_char = islower(curr_char) ? 'a' : 'A';//Avoid using magic numbers

            putchar(base_char + (curr_char - base_char + key[i]) % 26);//Make the same process one
            if(++i == key_len)
                i = 0;//reset key index
         else 
            putchar(curr_char);//non alphabetical inputs
        
    
    printf("\n");
    free(plain);

【讨论】:

这种方式取决于字符代码的顺序。

以上是关于如何在 vigenere cipherkey cs50 pset2 中重用(循环)密钥的主要内容,如果未能解决你的问题,请参考以下文章

CS50 Vigenere - 输出不正确

CS50 Vigenere - 奇怪的图案

为啥 Vigenere 失败 (CS50x)?

我的 CS50 Vigenere 代码有啥问题?

UndefinedBehaviorSanitiser 问题 - CS50 Vigenere

Vigenere.c CS50 浮点异常(核心转储)