Vigenere Cipher - 公式解释

时间:2017-01-21 21:48:44

标签: c cs50 vigenere

首先,没有人可以提出这样的问题所以请原谅我

#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));

是否有一个简单的解释,为什么我们使用第一个以及第二个如何工作?

2 个答案:

答案 0 :(得分:1)

用于Vigenere cypher的密钥应该是所有字母。第一个表达式将字符串转换为偏移数组,0a1b等.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 a1b等。以下是步骤:

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

以这种方式编写它会减少冗余和可读性:

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;
}

答案 1 :(得分:0)

key[i] = key[i] - 97;

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

至于第二行,

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

打印出ascii值为

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

97的减法具有与上述相同的目的。

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

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

PS:带有&#39; 65&#39;的零件是一样的,但用大写字母,因为&#39; A&#39; ascii中的值是65。

相关问题