关于 CS50 pset2 vigenere

Posted

技术标签:

【中文标题】关于 CS50 pset2 vigenere【英文标题】:About CS50 pset2 vigenere 【发布时间】:2018-12-26 02:09:05 【问题描述】:

为什么我的代码不会跳过空格并导致错误的加密顺序?

当我检查示例“Hello, World!”时,我的代码也计算了空间并转换为“Iekmo, Wnslc!”而不是“Iekmo,Vprke!”使用“baz”键

有人能解释一下背后的逻辑吗?非常感谢!

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

//getting user encryption key


int main(int argc, string argv[])


    if (argc != 2)
    
        printf("Usage: ./vigenere keyword\n");
        return 1;
    
    //check if all are alphabeticals
    else 
    
           for (int i = 1; i < argc; i++)
        
            for (int j = 0; j < strlen(argv[i]); j++)
            
                if (isalpha(argv[i][j]) == false)
                                
                    printf("Usage: ./vigenere keyword\n");
                    return 1;
                
            
        
      


    //getting plaintext divide it into each character
    string pt = get_string("plaintext: ");

    printf("ciphertext: ");
    //convert to ciphertext
    //C = (P + k) % 26
    for (int r = 0; r < strlen(pt); r++)
                               
        if (isupper(pt[r]))
        
            //making loop with j group corresponding to keyword
            int j = r % strlen(argv[1]);
            int key = tolower(argv[1][j]) - 97;
            printf("%c", (pt[r] - 65 + key) % 26 + 65);
        
        else if (islower(pt[r]))
        
            //making loop with j group corresponding to keyword
            int j = r % strlen(argv[1]);
            int key = tolower(argv[1][j]) - 97;
            printf("%c", (pt[r] - 97 + key) % 26 + 97);
        
        else
        
            printf("%c", pt[r]);
        

                 
       printf("\n");       


【问题讨论】:

您是否尝试过在调试器中单步执行代码以查看哪里出错了? stderr(标准错误)流上报告错误是个好主意。您可以通过将for (int j = 0; j &lt; strlen(argv[i]); j++) 替换为for (int j = 0; argv[i][j] != '\0'; j++) 来避免在每次迭代中调用strlen()(并且有些人不会像那样完整地写出!= '\0')。但是,这些都不会直接影响代码的正确性。您应该使用'a''A' 而不是97 和65;这使代码更具可读性和更容易理解。 您需要将“字符串r中的位置”与“加密字符数”分开。您需要一个额外的变量,仅当字符为字母时才会增加。 旁注:对于您的第一个 if,它执行 return。因此,您可以消除上面的 else 并仅删除代码。也就是说,if (whatever) return; else do_stuff 转到 if (whatever) return; do_stuff @Jonathan Leffler 感谢您的 cmets。但是我仍然无法弄清楚如何仅当它是字母时才增加我只能想到一个 for 循环来这样做,但它不适用于定义键并在 printf 中使用它。你能告诉我如何解决这个问题吗? 【参考方案1】:

问题在于int j = r % strlen(argv[1]); 的 j 计算。键的索引与r(消息的索引)无关。程序需要仅根据密钥(长度)迭代密钥。每次“使用”键索引时都需要增加它,并“包装”它,这样它就不会超过结尾。您可以考虑在r 循环之前声明j;每当您使用键索引时递增 j(提示:j++);并使用模运算符“包装” j(提示:j % strlen(argv[1])。我把实际代码留给你。

【讨论】:

感谢您的建议。我现在已经想通了。感谢您的帮助!【参考方案2】:

展示比解释容易得多:

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

int main(int argc, char **argv)

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

    for (int i = 1; i < argc; i++)
    
        for (int j = 0; argv[i][j] != '\0'; j++)
        
            if (isalpha(argv[i][j]) == false)
                            
                fprintf(stderr, "%s: non-alphabetic character '%c' (%d) in key\n",
                        argv[0], argv[i][j], argv[i][j]);
                return 1;
            
        
    

    string pt = get_string("plaintext:  ");

    printf("ciphertext: ");
    int k = 0;
    int keylen = strlen(argv[1]);
    for (int r = 0; pt[r] != '\0'; r++)
                               
        if (isupper(pt[r]))
        
            int j = k++ % keylen;
            int key = tolower(argv[1][j]) - 'a';
            printf("%c", (pt[r] - 'A' + key) % 26 + 'A');
        
        else if (islower(pt[r]))
        
            int j = k++ % keylen;
            int key = tolower(argv[1][j]) - 'a';
            printf("%c", (pt[r] - 'a' + key) % 26 + 'a');
        
        else
        
            printf("%c", pt[r]);
        

                 
     printf("\n");       
     return 0;

示例运行:

$ ./vig89 baz
plaintext:  Hello, World!
ciphertext: Iekmo, Vprke!
$

正如我在评论中指出的,您需要将“字符串中的位置,r”与“加密字符数”分开。您需要一个额外的变量,仅当字符为字母时才会增加。

在上面的代码中,k 是额外的变量(keylen 是另一个变量,但它只是记录键的长度而不是重复调用strlen())。 k 中的值在知道字符是字母而不是其他情况时递增。

我观察到处理argv[1] 可能是明智的,这样您就不必每次都进行tolower() 转换;您可以在验证关键字时这样做。

我还报告了标准错误的错误,并且没有在循环的条件部分使用strlen()。虽然如果您处理 3 个字母的键,成本并不高,但如果您开始计算每次迭代时 20 KiB 长的字符串的长度,您可能会开始发现开销(除非编译器设法将其优化掉——它可能,它可能不会)。我还在 I/O 中对齐了纯文本和密文。

还有很多其他可以/应该进行的更改。例如,第一个for (int i = 1; …) 循环是不需要的;您只有一个参数,因此您只需要内部 for (int j = 0; …) 循环。使用if (!isalpha(argv[i][j])) 也比将结果与false 比较更惯用,特别是因为isalpha 宏不能保证返回0 或1(它返回零或非零)——所以改变if (isalpha(argv[i][j]) == false)if (isalpha(argv[i][j] != true) 是不可靠的。我可能会创建一个简单的变量 char *key = argv[1];(或在 CS50 的上下文中使用 string key = argv[1];,尽管我不相信 CS50 typedef char *string; 是一个好主意)并在程序中使用它。

【讨论】:

非常感谢!!!!现在我懂了。这几天我一直在纠结背后的原因,非常感谢您的详细解释。

以上是关于关于 CS50 pset2 vigenere的主要内容,如果未能解决你的问题,请参考以下文章

Vigenere cs50 Pset2 末尾的额外字符

我的代码有啥问题? (Vigenere cypher cs50, pset2)

CS50-pset2-替换

CS50 Pset2。维吉内尔。大文本到低键,反之亦然问题

如何在不使用 argv 的情况下获取字符串数组 - CS50 pset2

CS50 PSET2:根本不知道如何实现 isalpha 以便检查每个命令行参数