【问题标题】:Vigenere Cipher - Formula ExplanationVigenere Cipher - 公式解释
【发布时间】:2017-01-21 21:48:44
【问题描述】:

首先没有人可以问这种问题,所以请原谅我

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

int main(int argc, string argv[]) {
    string key = argv[1];
    int l = strlen(argv[1]);
    if (argc != 2) {
        return 0;
    }
    for (int i = 0, n = strlen(key); i < n; i++) {
        if (!isalpha(key[i])) {
            return 0;
        }
        key[i] = tolower(key[i]);
        key[i] = key[i] - 97;
    }
    string txt = GetString();
    for (int k = 0, p = strlen(txt); k < p; k++) {
        if (isalpha(txt[k])) {
            if (isupper(txt[k])) {
                printf("%c", (((txt[k] - 65) + (key[k % l])) % 26 + 65));
            }
            if (islower(txt[k])) {
                printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));
            }
        } else
        if (!isalpha(txt[k])) {
            printf("%c", txt[k]);
        }
    }
    printf("\n");
    return 0;
}

这两行代码我不太明白

key[i] = key[i] - 97;
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));

能否简单解释一下我们为什么使用第一个以及第二个如何工作?

【问题讨论】:

  • 您可能需要检查ascii code 97,它似乎是'a'。而 26 可能与字母表中的字符数有关。

标签: c cs50 vigenere


【解决方案1】:

用于 Vigenere 密码的密钥应该是全字母。第一个表达式将字符串转换为偏移量数组,0 表示a1 表示b,等等。97 是'a' 的ASCII 码。写起来会更易读:

for (int i = 0, n = strlen(key); i < n; i++) {
    if (!isalpha((unsigned char)key[i])) {
        printf("key '%s' must contain only letters\n", key);
        return 1;
    }
    key[i] = tolower((unsigned char)key[i]);
    key[i] = key[i] - 'a';
}

对于第二个表达式,如果字符 txt[k] 是小写字母,printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97)); 通过添加移位值来计算并打印转置后的字母(key 中的每个字符都用作移位值后一个其他,0 移动 a1 移动 b 等)。步骤如下:

  • 程序计算字母索引txt[k] - 9797'a'的ASCII码,
  • 然后它添加移位值key[k % l],以循环方式循环key 中的值,
  • 取模 26 得到 0 到 25 之间的字母索引。
  • 最后添加97'a' 的 ASCII 值,将索引转换回小写字母。

这样写会更少冗余,更易读:

for (int i = 0, j = 0; txt[i] != '\0'; i++) {
    int c = (unsigned char)txt[i];
    if (isupper(c)) {
        c = (c - 'A' + key[j++ % l]) % 26 + 'A';
    } else
    if (islower(c)) {
        c = (c - 'a' + key[j++ % l]) % 26 + 'a';
    }
    putchar(c);
}

另请注意,在检查命令行上是否传递了足够多的参数之前,不应将argv[1] 传递给strlen()

这是程序的修改版本:

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

int main(int argc, string argv[]) {
    if (argc != 2) {
        printf("missing key argument\n");
        return 1;
    }
    string key = argv[1];
    int klen = strlen(key);
    if (klen == 0) {
        printf("key cannot be empty\n");
        return 1;
    }
    for (int i = 0; i < klen; i++) {
        if (!isalpha((unsigned char)key[i])) {
            printf("key '%s' must contain only letters\n", key);
            return 1;
        }
        key[i] = tolower((unsigned char)key[i]) - 'a';
    }

    string txt = GetString();
    for (int i = 0, j = 0; txt[i] != '\0'; i++) {
        int c = (unsigned char)txt[i];
        if (isupper(c)) {
            c = (c - 'A' + key[j++ % klen]) % 26 + 'A';
        } else
        if (islower(c)) {
            c = (c - 'a' + key[j++ % klen]) % 26 + 'a';
        }
        putchar(c);
    }
    putchar('\n');
    return 0;
}

【讨论】:

  • "unsigned char" 重要吗?
  • @kryin10: islower() 和朋友只为unsigned char 类型的值和特殊值EOF 定义。如果char 类型默认在您的系统上签名,这是最有可能的,并且如果您键入一些扩展字符,例如é,这些具有负值,islower() 具有未定义的行为。将 char 参数转换为 (unsigned char) 是避免此问题的标准方法。
  • 感谢您的帮助。我认为该程序不会跳过空格。我尝试添加 if is not space 和 ' ' 行,但它似乎不起作用。有什么建议吗?
  • @kryin10: 程序不跳过空格是什么意思?字符串中任何不是字母的字符都将原样输出。
  • 抱歉造成混淆。我的意思是,空格必须被忽略。程序只应考虑字母。 ||键为“abc”,文本“AAA aaa” -> 输出 -> ABC bca
【解决方案2】:
key[i] = key[i] - 97;

该行的用途是给出 key[i],它的值表示 ascii 中字符的值,它是我们字母表中的索引。然后,'a' 将被赋予值 0,'b' 被赋予值 1 ....,'z' 被赋予值 25。

至于第二行,

printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97))

它打印ascii值为的字符

(((txt[k] - 97) + (key[k % l])) % 26 + 97))

97的减法与上面解释的目的相同。

% 26 是模数,即 ((txt[k] - 97) + (key[k % l])) 除以 26(整数除法)时的余数。然后,再次添加 97 以将结果的顺序或索引转换为相应的 ascii 值。 此页面可能会让您更深入地了解 C 中的字符表示。

至于k、i和l的含义,我让你自己掌握密码的内部功能,但整个加密发生在你想要解释的第二行。

PS : '65' 的部分是一样的,但都是大写字母,因为 'A' 在 ascii 中的值是 65。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-02
    • 2014-12-18
    • 1970-01-01
    • 2016-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多