C语言实现简单凯撒密码算法
- **实验2:传统密码技术 【实验目的】 通过本次实训内容,学习常见的传统密码技术,通过编程实现简单代替密码中的移位密码算法,加深对传统密码技术的了解,为深入学习密码学奠定基础。
- 【技能要求】
- 分析简单代替密码中的移位密码算法的功能需求,详细设计实现简单代替密码中的移位密码算法的数据结构和流程,给出测试用例和测试步骤,得出测试和结论。
- 简单代替密码中的移位密码算法必须提供加密和解密两个接口:int encrypt()和int decrypt()。当加密或者解密成功时,返回CRYPT_OK,失败时返回CRYPT_ERROR。
- 【实验内容】
- 利用自己熟悉的程序设计语言,实现简单代替密码中的移位密码。要求程序给出源代码以及相应的注释,实验结果截图。**
根据实验要求,我们将实现一个简单的移位密码(也称为凯撒密码)算法。这种加密方法是通过将字母表中的每个字母向前或向后移动固定数量的位置来进行加密和解密的。例如,如果移位数为3,那么A会被替换为D,B会变成E,以此类推。
规范代码演示
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define CRYPT_OK 0
#define CRYPT_ERROR -1
// 加密函数
int encrypt(char *text, int shift) {
if (shift < 0 || shift > 25) {
return CRYPT_ERROR; // 移位数必须在0到25之间
}
for (int i = 0; text[i] != '\0'; ++i) {
if (isalpha(text[i])) { // 只处理字母字符
char base = isupper(text[i]) ? 'A' : 'a';
text[i] = ((text[i] - base + shift) % 26) + base; // 计算新的字符位置
}
}
return CRYPT_OK;
}
// 解密函数
int decrypt(char *text, int shift) {
return encrypt(text, 26 - (shift % 26)); // 解密就是使用相反方向的移位
}
int main() {
char text[100];
int shift;
// 获取用户输入的文本
printf("请输入要加密的文本: ");
fgets(text, sizeof(text), stdin);
text[strcspn(text, "\n")] = 0; // 去掉换行符
// 获取移位数
printf("请输入移位数(0-25): ");
if (scanf("%d", &shift) != 1 || shift < 0 || shift > 25) {
printf("无效的移位数。\n");
return CRYPT_ERROR;
}
// 加密
if (encrypt(text, shift) == CRYPT_OK) {
printf("加密后的文本: %s\n", text);
} else {
printf("加密失败。\n");
return CRYPT_ERROR;
}
// 解密
if (decrypt(text, shift) == CRYPT_OK) {
printf("解密后的文本: %s\n", text);
} else {
printf("解密失败。\n");
return CRYPT_ERROR;
}
return 0;
}
测试用例和步骤
- 运行程序。
- 输入要加密的文本,例如 “Hello, World!”。
- 输入一个有效的移位数,例如 3。
- 检查输出的加密文本是否正确,例如对于移位3,应得到 “Khoor, Zruog!”。
- 检查解密后的文本是否与原始文本相同。
运行截图
注意事项
- 程序假设输入文本不会超过99个字符。
- 程序只处理英文字符,非字母字符保持不变。
- 使用
fgets
来读取包含空格的字符串,而strcspn
用于去除末尾的换行符。 decrypt
函数实际上调用了encrypt
函数,但使用了相反的移位数来实现解密。
这个程序可以作为一个基础示例,你可以根据需要添加更多的功能或改进错误处理逻辑。
核心代码加密逻辑
移位密码(凯撒密码)的核心思路。
移位密码的基本概念
移位密码是一种简单的替换加密方法。它通过将字母表中的每个字母向前或向后移动固定数量的位置来进行加密。例如,如果移位数是3,那么A会被替换成D,B会替换成E,以此类推。对于字母表的末尾,Z会绕回到A继续计算。
实现步骤
-
遍历字符串:
- 你需要逐个检查输入文本中的每一个字符。
- 如果遇到非字母字符(如空格、标点符号等),则保持不变。
- 如果遇到字母字符,则进行移位操作。
-
处理大小写:
- 英文有大写字母和小写字母,所以需要分别处理。
- 大写字母从’A’到’Z’,小写字母从’a’到’z’。
- 为了方便处理,我们可以先确定当前字母是大写还是小写,然后基于相应的基准字母(大写是’A’,小写是’a’)进行移位。
-
计算新的字符位置:
- 计算当前字母与基准字母之间的距离。
- 加上移位数。
- 使用模运算(% 26)来确保结果在0到25之间。
- 最后再加上基准字母,得到新的字符。
代码详解
下面是对核心代码的进一步简化解释:
for (int i = 0; text[i] != '\0'; ++i) {
if (isalpha(text[i])) { // 只处理字母字符
char base = isupper(text[i]) ? 'A' : 'a';
text[i] = ((text[i] - base + shift) % 26) + base; // 计算新的字符位置
}
}
逐行解释
-
遍历字符串:
for (int i = 0; text[i] != '\0'; ++i) {
- 这一行代码使用
for
循环遍历字符串text
中的每个字符,直到遇到字符串结束符\0
。
- 这一行代码使用
-
只处理字母字符:
if (isalpha(text[i])) {
isalpha
函数检查当前字符是否为字母。如果是字母,进入花括号内的代码块;如果不是,跳过该字符。
-
确定基准字母:
char base = isupper(text[i]) ? 'A' : 'a';
isupper
函数检查当前字符是否为大写字母。- 如果是大写字母,
base
设置为'A'
;如果是小写字母,base
设置为'a'
。 - 这样可以确保我们对大写和小写字母分别进行正确的移位。
-
计算新的字符位置:
text[i] = ((text[i] - base + shift) % 26) + base;
text[i] - base
:计算当前字符与基准字母之间的距离。例如,如果text[i]
是'D'
且base
是'A'
,那么'D' - 'A'
等于3。+ shift
:加上移位数。例如,如果shift
是3,那么3 + 3
等于6。% 26
:取模26,以确保结果在0到25之间。这样可以处理移位超过字母表长度的情况。+ base
:再加上基准字母base
,得到最终的新字符。例如,如果base
是'A'
,那么6 + 'A'
就是'G'
。- 最终,
text[i]
被替换为新的字符。
示例
假设text
是 “Hello”,shift
是3:
- 对于
H
(ASCII 72),base
是'A'
(ASCII 65),72 - 65 + 3
等于10,10 % 26
等于10,10 + 65
等于75,所以H
变成K
。 - 对于
e
(ASCII 101),base
是'a'
(ASCII 97),101 - 97 + 3
等于7,7 % 26
等于7,7 + 97
等于104,所以e
变成h
。 - 其他字符类似处理。
最终,“Hello"加密后会变成"Khoor”。
这个过程对于解密也是类似的,只是移位的方向相反。
附加
关于isalpha, iswalpha
-
isalpha(c)
- 返回值:
isalpha()
函数返回一个非零值(通常为1),如果参数c
位于范围’A-Z’或’a-z’内。这意味着c
是一个英文字母。 - 参数:
c
是要测试的整数值。 - 依赖性:
isalpha()
的结果取决于当前locale(区域设置)的LC_CTYPE类别设置。可以通过setlocale()
函数更改locale设置。
- 返回值:
-
iswalpha(c)
- 返回值:
iswalpha()
函数仅在满足以下条件的情况下返回非零值:c
是一个宽字符,且iswupper()
或iswlower()
也为真。也就是说,c
是一个由实现定义的集合中的任何宽字符,对于这些字符,iswcntrl()
,iswdigit()
,ispunct()
, 或isspace()
都不为真。 - 参数:
c
是要测试的宽字符。 - 依赖性:
iswalpha()
的结果独立于locale,不受locale影响。
- 返回值:
这两个函数都返回0,如果参数c
不满足测试条件。