Vigenere Cipher - 公式解释

Vigenere Cipher - Formula Explanation

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

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

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

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

该行的用途是给出键[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.

用于 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中的每个字符是一个接一个地用作移位值,a 移位 0b 移位 1 等)。以下是步骤:

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

这样写会减少冗余并提高可读性:

for (int i = 0, j = 0; txt[i] != '[=11=]'; 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] != '[=12=]'; 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;
}