让我给你一个关于凯撒密码的详细解释,以便理解这个公式。我还将展示超简单的代码示例,以及更高级的单行代码。
最大的问题是潜在的溢出。所以,我们需要解决这个问题。
那么我们需要了解加密和解密是什么意思。如果加密将所有内容向右移动,解密将再次将其向左移动。
- 因此,如果使用“def”和 key=1,加密后的字符串将为“efg”。
- 如果使用 key=1 进行解密,则会再次将其移至左侧。结果:“定义”
我们可以观察到,我们只需要移位 -1,因此键的负数。
所以,基本上加密和解密都可以用相同的程序来完成。我们只需要反转键。
现在让我们看看溢出问题。目前我们只从大写字符开始。字符具有关联的代码。例如,在 ASCII 中,字母“A”用 65 编码,“B”用 66 编码,以此类推。因为我们不想用这样的数字进行计算,所以我们将它们归一化。我们只是从每个字符中减去“A”。那么
- 'A' - 'A' = 0
- 'B' - 'A' = 1
- 'C' - 'A' = 2
- 'D' - 'A' = 3
你看到了模式。如果我们现在想用密钥 3 加密字母“C”,我们可以执行以下操作。
'C' - 'A' + 3 = 5 然后我们再次添加 'A' 以取回字母,我们将得到 5 + 'A' = 'F'
这就是全部的魔法。
但是如何处理“Z”之外的溢出。这可以通过简单的模除法来处理。
让我们看看 'Z' + 1。我们做 'Z' - 'A' = 25,然后 +1 = 26 现在模 26 = 0 然后加上 'A' 将是 'A'
等等等等。结果公式为:(c-'A'+key)%26+'A'
接下来,否定键呢?这也很简单。假设一个 'A' 和 key=-1
结果将是“Z”。但这与向右移动 25 相同。因此,我们可以简单地将负键转换为正转移。简单的陈述将是:
if (key < 0) key = (26 + (key % 26)) % 26;
然后我们可以用一个简单的 Lambda 调用我们的转换函数。一种用于加密和解密的功能。只需使用倒置键即可。
使用上面的公式,甚至不需要检查负值。它适用于正值和负值。
所以,key = (26 + (key % 26)) % 26; 将始终有效。
一些扩展信息,如果您使用 ASCII 字符表示。请查看任何 ASCII 表。您会看到任何大写和小写字符相差 32。或者,如果您查看二进制:
char dez bin char dez bin
'A' 65 0100 0001 'a' 97 0110 0001
'B' 66 0100 0010 'b' 98 0110 0010
'C' 67 0100 0011 'b' 99 0110 0011
. . .
所以,如果你已经知道一个字符是字母,那么大小写的唯一区别就是第 5 位。如果我们想知道,如果char 是小写,我们可以通过屏蔽这个位来得到. c & 0b0010 0000 等于 c & 32 或 c & 0x20。
如果我们想对大写或小写字符进行操作,我们可以将“大小写”屏蔽掉。使用c & 0b00011111 或c & 31 或c & 0x1F,我们将始终获得大写字符的等价物,已经规范化为从一个开始。
char dez bin Masking char dez bin Masking
'A' 65 0100 0001 & 0x1b = 1 'a' 97 0110 0001 & 0x1b = 1
'B' 66 0100 0010 & 0x1b = 2 'b' 98 0110 0010 & 0x1b = 2
'C' 67 0100 0011 & 0x1b = 3 'b' 99 0110 0011 & 0x1b = 3
. . .
因此,如果我们使用一个字母字符,将其屏蔽,然后减去 1,那么对于任何大写或小写字符,我们都会得到 0..25。
另外,我想重复密钥处理。正密钥将加密一个字符串,负密钥将解密一个字符串。但是,如上所述,否定键可以转换为肯定键。示例:
Shifting by -1 is same as shifting by +25
Shifting by -2 is same as shifting by +24
Shifting by -3 is same as shifting by +23
Shifting by -4 is same as shifting by +22
因此,很明显我们可以通过:26 + key 来计算始终为正的密钥。对于否定键,这将为我们提供上述偏移量。
对于正键,我们会在 26 上产生溢出,我们可以通过模 26 除法来消除:
'A'--> 0 + 26 = 26 26 % 26 = 0
'B'--> 1 + 26 = 27 27 % 26 = 1
'C'--> 2 + 26 = 28 28 % 26 = 2
'D'--> 3 + 26 = 29 29 % 26 = 3
--> (c + key) % 26 将消除溢出并产生正确的新加密/解密字符。
而且,如果我们将它与上述关于否定键的智慧结合起来,我们可以写成:((26+(key%26))%26),它将适用于所有正负键。
将其与掩蔽相结合,可以得到以下程序:
const char potentialLowerCaseIndicator = c & 0x20;
const char upperOrLower = c & 0x1F;
const char normalized = upperOrLower - 1;
const int withOffset = normalized + ((26+(key%26))%26);
const int withOverflowCompensation = withOffset % 26;
const char newUpperCaseCharacter = (char)withOverflowCompensation + 'A';
const char result = newUpperCaseCharacter | (potentialLowerCaseIndicator );
当然,以上众多语句都可以转换成一个Lambda:
#include <string>
#include <algorithm>
#include <cctype>
#include <iostream>
// Simple function for Caesar encyption/decyption
std::string caesar(const std::string& in, int key) {
std::string res(in.size(), ' ');
std::transform(in.begin(), in.end(), res.begin(), [&](char c) {return std::isalpha(c) ? (char)((((c & 31) - 1 + ((26 + (key % 26)) % 26)) % 26 + 65) | (c & 32)) : c; });
return res;
}
int main() {
std::string test{ "aBcDeF xYzZ" };
std::cout << caesar(test, 5);
}
为了便于理解,最后一个函数也可以写得更详细:
std::string caesar1(const std::string& in, int key) {
std::string res(in.size(), ' ');
auto convert = [&](const char c) -> char {
char result = c;
if (std::isalpha(c)) {
// Handling of a negative key (Shift to left). Key will be converted to positive value
if (key < 0) {
// limit the key to 0,-1,...,-25
key = key % 26;
// Key was negative: Now we have someting between 0 and 26
key = 26 + key;
};
// Check and remember if the original character was lower case
const bool originalIsLower = std::islower(c);
// We want towork with uppercase only
const char upperCaseChar = (char)std::toupper(c);
// But, we want to start with 0 and not with 'A' (65)
const int normalized = upperCaseChar - 'A';
// Now add the key
const int shifted = normalized + key;
// Addition result maybe bigger then 25, so overflow. Cap it
const int capped = shifted % 26;
// Get back a character
const char convertedUppcase = (char)capped + 'A';
// And set back the original case
result = originalIsLower ? (char)std::tolower(convertedUppcase) : convertedUppcase;
}
return result;
};
std::transform(in.begin(), in.end(), res.begin(), convert);
return res;
}
如果您想查看仅包含最简单语句的解决方案,请参阅以下内容。
#include <iostream>
#include <string>
using namespace std;
string caesar(string in, int key) {
// Here we will store the resulting encrypted/decrypted string
string result{};
// Handling of a negative key (Shift to left). Key will be converted to positive value
if (key < 0) {
// limit the key to 0,-1,...,-25
key = key % 26;
// Key was negative: Now we have someting between 0 and 26
key = 26 + key;
};
// Read character by character from the string
for (unsigned int i = 0; i < in.length(); ++i) {
char c = in[i];
// CHeck for alpha character
if ((c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z')) {
// Check and remember if the original character was lower case
bool originalIsLower = (c >= 'a' and c <= 'z');
// We want to work with uppercase only
char upperCaseChar = originalIsLower ? c - ('a' - 'A') : c;
// But, we want to start with 0 and not with 'A' (65)
int normalized = upperCaseChar - 'A';
// Now add the key
int shifted = normalized + key;
// Addition result maybe bigger then 25, so overflow. Cap it
int capped = shifted % 26;
// Get back a character
char convertedUppcase = (char)capped + 'A';
// And set back the original case
result += originalIsLower ? convertedUppcase + ('a' - 'A') : convertedUppcase;
}
else
result += c;
}
return result;
}
int main() {
string test{ "aBcDeF xYzZ" };
string encrypted = caesar(test, 5);
string decrypted = caesar(encrypted, -5);
cout << "Original: " << test << '\n';
cout << "Encrpyted: " << encrypted << '\n';
cout << "Decrpyted: " << decrypted << '\n';
}