【问题标题】:substitution cipher: can't acknowledge nonrepeating characters替换密码:无法确认非重复字符
【发布时间】:2023-09-28 17:21:01
【问题描述】:

我遇到了 CS50 的替换密码问题。我被困在如何验证密钥上。每当我将 26 个字符的键作为命令行参数传递时,即使该键没有任何字符,程序也会输出“您不得重复任何字符”。我的程序正确地检查了密钥的长度和命令行参数的存在。它只是不承认有效的非重复密钥。

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

bool validateKey(char key[]);
string substitute(char key[], string plaintext);

int main(int argc, string argv[]) {
   if(strlen(argv[1]) == 26) { //key typed after prgm name will be used to encrypt data
       if(validateKey(argv[1])) {
            
            string plaintext = get_string("Plaintext: ");
            string ciphertext = substitute(argv[1], plaintext);
            printf("Ciphertext: %s", ciphertext);
            
       }
   }
   else if(argv[1] == NULL) {
       printf("Usage: ./substitution key\n");
   }
   else {
        printf("Key must contain 26 characters.\n");
   }
}

bool validateKey(char key[]) {
    for(int i = 0; i < 26; i++) {
        if(!isalpha(key[i])) {
            printf("Key must only contain alphabetic characters.\n");
            return false;
        }
    }

    /*
        an array of counters to keep track of how many times a letter occurs in the cipher
        each counter should be set to 1 if key doesn't have repeating letters
    */
    int cntr[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 

    for (int i = 0; i < 26; i++) {
        key[i] = islower(key[i]); //make all the letters in the key lowercase to make it easier to work with
        switch(key[i]) {
            case 'a':
                cntr[0] += 1;
            case 'b':
                cntr[1] += 1;
            case 'c':
                cntr[2] += 1;
            case 'd':
                cntr[3] += 1;
            case 'e':
                cntr[4] += 1;
            case 'f':
                cntr[5] += 1;
            case 'g':
                cntr[6] += 1;
            case 'h':
                cntr[7] += 1;
            case 'i':
                cntr[8] += 1;
            case 'j':
                cntr[9] += 1;
            case 'k':
                cntr[10] += 1;
            case 'l':
                cntr[11] += 1;
            case 'm':
                cntr[12] += 1;
            case 'n':
                cntr[13] += 1;
            case 'o':
                cntr[14] += 1;
            case 'p':
                cntr[15] += 1;
            case 'q':
                cntr[16] += 1;
            case 'r':
                cntr[17] += 1;
            case 's':
                cntr[18] += 1;
            case 't':
                cntr[19] += 1;
            case 'u':
                cntr[20] += 1;
            case 'v':
                cntr[21] += 1;
            case 'w':
                cntr[22] += 1;
            case 'x':
                cntr[23] += 1;
            case 'y':
                cntr[24] += 1;
            case 'z':
                cntr[25] += 1;
        }
    }

    for(int i = 0; i < 26; i++) {
        if(cntr[i] != 1) {
            printf("Key must not contain repeated characters.\n");
            return false;
        }
    }

    return true;
}

string substitute(char key[]) {
    return "";
}

【问题讨论】:

  • break 添加到每个case
  • 一种更紧凑的方法是将整个switch 块替换为一行:cntr[key[i] - 'a'] += 1;
  • 整个switch 语句可以只替换为cntr[key[i] - 'a'] += 1
  • key[i] = islower(key[i]); -> key[i] = tolower(key[i]);。您可能想要执行char k = tolower(key[i]);switch(k) 之类的操作,以避免修改原始密钥。
  • @kaylum:上次我检查过,您必须在 C 的初始化程序中提供至少一个值(C++ 允许您省略它)。 MSVC 不需要 IIRC(它允许在 C 中使用很多 C++ 主义),但它是非标准的。至少根据cppreference's page on C array initialization,它至少需要一个表达式,所以我认为我没记错规则。

标签: c cs50


【解决方案1】:

“最小”修复是在每次递增后添加一个break,这样您的case 'a' 就不会执行case 'b' 及以后的代码。您还需要将islower 调用更改为tolower(否则您的所有键值都将变为01)。

也就是说,switch 语句本身已经荒谬地过长了,实际上应该简化为单行:

cntr[tolower(key[i]) - 'a'] += 1;

这是安全的,因为您已经检查了所有输入字符是否通过isalpha

【讨论】:

  • 我能够根据您的建议解决问题。但是,只要我不提供密钥,我的程序就会产生错误。我希望它提供返回码 1 而不是“分段错误”。
  • @Programmer:那是因为您访问argv[1](在您的strlenmain 的第一行)没有验证argc 是否至少为2。您需要检查参数是否存在在尝试访问它之前完全没有。