实验 9 · 字符串加解密
用 26 字母固定置换表做加密;解密表在启动时一次性反推出来,避免每次解密都重新扫描映射表。
源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 1024
// 题目给的字母映射表,a~z 对应下面一串
// a b c d e f g h i j k l m n o p q r s t u v w x y z
// n g z q t c o b m u h e l k p d a w x f y i v r s j
static const char encrypt_map[26] = {
'n','g','z','q','t','c','o','b','m','u','h','e','l',
'k','p','d','a','w','x','f','y','i','v','r','s','j'
};
// 解密表由加密表反推,程序跑起来的时候建一次就行
static char decrypt_map[26];
// 把加密表反过来存一份,后面解密直接查
// 要是每次解密都现扫一遍加密表就太亏了
void build_decrypt_map(void)
{
for (int i = 0; i < 26; i++) {
int idx = encrypt_map[i] - 'a';
decrypt_map[idx] = (char)('a' + i);
}
}
// 加密:一个字符一个字符地扫,字母就查表,别的照抄
// 串说白了就是一串字符,遍历就是按下标往后走
void encrypt(const char *plain, char *cipher)
{
int len = (int)strlen(plain);
for (int i = 0; i < len; i++) {
char c = plain[i];
if (c >= 'a' && c <= 'z') {
cipher[i] = encrypt_map[c - 'a'];
} else if (c >= 'A' && c <= 'Z') {
// 大写的先当小写查表,再把结果变回大写,这样大小写不会丢
cipher[i] = (char)toupper((unsigned char)encrypt_map[c - 'A']);
} else {
// 空格、标点、数字这些不是字母的就原样放进去
cipher[i] = c;
}
}
cipher[len] = '\0'; // 别忘了 '\0',C 里的串就靠它来收尾
}
// 解密逻辑和加密几乎一模一样,就是换张表查
void decrypt(const char *cipher, char *plain)
{
int len = (int)strlen(cipher);
for (int i = 0; i < len; i++) {
char c = cipher[i];
if (c >= 'a' && c <= 'z') {
plain[i] = decrypt_map[c - 'a'];
} else if (c >= 'A' && c <= 'Z') {
plain[i] = (char)toupper((unsigned char)decrypt_map[c - 'A']);
} else {
plain[i] = c;
}
}
plain[len] = '\0';
}
// 顺手把映射表打出来看一眼,方便核对
void print_map(void)
{
printf("字母映射表(明文 -> 密文):\n");
printf(" 明文: ");
for (int i = 0; i < 26; i++) printf("%c ", 'a' + i);
printf("\n 密文: ");
for (int i = 0; i < 26; i++) printf("%c ", encrypt_map[i]);
printf("\n\n");
}
int main(void)
{
char plain[MAX_LEN];
char cipher[MAX_LEN];
char recovered[MAX_LEN];
build_decrypt_map();
print_map();
// 用 fgets 是想让输入可以带空格,scanf("%s") 碰到空格就停了
printf("请输入要加密的文本:");
if (fgets(plain, MAX_LEN, stdin) == NULL) {
printf("读取输入失败\n");
return 1;
}
// fgets 会把回车也读进来,这里把末尾那个换行去掉
size_t n = strlen(plain);
if (n > 0 && plain[n - 1] == '\n') plain[n - 1] = '\0';
encrypt(plain, cipher);
printf("加密结果:%s\n", cipher);
// 再解回去一遍,顺带验证一下加密和解密互为逆过程
decrypt(cipher, recovered);
printf("解密结果:%s\n", recovered);
if (strcmp(plain, recovered) == 0) {
printf("校验通过:解密结果与原文一致。\n");
} else {
printf("校验失败:解密结果与原文不一致。\n");
}
return 0;
}
运行示例
在 VS Code 打开源文件后按 Ctrl+Shift+B 运行项目预置的 Run current C file 任务,或在命令行:
1
2
gcc -finput-charset=UTF-8 -fexec-charset=UTF-8 homework/experiment_9.c -o homework/experiment_9.exe
.\homework\experiment_9.exe
本文由作者按照 CC BY 4.0 进行授权