持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
前言
此题来自于计算机网络实验的一道题,此题思路理解不难,实现的难点在于进制的转换。
知识点:位运算、掩码运算、文件读写
题目——计算文件的16位校验和
编写一个计算机程序用来计算一个文件的16位校验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到校验和中。
要求:
1)以命令行形式运行:check_sum infile
其中check_sum为程序名,infile为输入数据文件名。
2)输出:数据文件的效验和
附:校验和(checksum)
参见RFC1071 - Computing the Internet checksum
原理: 把要发送的数据看成16比特的二进制整数序列,并计算他们的和。若数据字节长度为奇数,则在数据尾部补一个字节的0以凑成偶数。
例子: 16位校验和计算,下图表明一个小的字符串的16位效验和的计算。
为了计算效验和,发送计算机把每对字符当成16位整数处理并计算校验和。如果校验和大于16位,那么把进位一起加到最后的校验和中。
实验原理分析与代码实现思路
①首先,生成一个名为test.txt的文件,并写入一段字符串"Hello World!"
②计算校验和
a)字符读取,并强制转化成ASCII码对应的序号。每次读取两个字符,直到文件结尾。当字符数量为奇数时,第二个字符为0。
b)采用位运算的方法,两个字符拼接成组。第一个字符的数值左移8位,相当于乘以2的8次方,与第二个字符的数值相加,得到的结果几位两个字母拼接之后的十进制数值,后面直接通过每组计算的数值进行计算即可。
c)十进制求和。将每一组的十进制数值相加,得到一个十进制整数checksum32 (int类型为32位,不用考虑求和后溢出问题)
d)进位处理与进制转换。通过掩码方法(位运算)可以将十进制数转换为二进制数,并加32位二进制分成高16位和低16位,再分别转成两个十进制数(对应的二进制16位)并相加得到真正的校验和checksum16。最后,通过位运算将其转换成16进制表示即可。
代码
#include <fstream>
#include <iostream>
using namespace std;
#define File "test.txt"
void writefile() {
ofstream file;
file.open(File);
file << "Hello World!";
file.close();
}
void checksum(const char* filename) {
int checksum32 = 0;
fstream file;
file.open(File, ios::in);
printf("读取的文件数据为:");
while (!file.eof()) {
char a = file.get();
printf("%c", a);
//printf("%d ", a);
if (a == -1) break; //到达文件结尾
int b = a;
//printf("%d ", b << 8);
int hex;
int d;
char c = file.get();
printf("%c", c);
//printf("%d ", c);
if (c == -1) { //奇数个字符
d = 0;
}
else { //偶数个字符
d = c;
}
hex = (b << 8) + d; //注意加括号!!!
checksum32 += hex;
//printf("%d\n", hex);
}
printf("\n\n");
printf("每两个字母为一组,组成16位。将每一组的10进制数求和,得到结果如下:\n");
printf("32位校验和的10进制为:%d\n", checksum32);
//通过掩码转化为2进制
printf("32位校验和的2进制数为:");
for (int i = 0; i < 32; ++i) {
if (i == 16) printf(" ");
int binaryKey = checksum32 & (1 << 31 - i);
if (binaryKey != 0) binaryKey = 1;
printf("%d", binaryKey);
}
int left; //进位
int right;
left = checksum32 & (0xffff0000);
left = left >> 16;
right = checksum32 & (0x0000ffff);
printf("\n\n");
printf("将32位二进制拆分成高16位进位数,低16位非进位数,得到结果如下:\n");
//printf("\nleft=%d ", left);
//printf("right=%d\n", right);
//printf("\n");
printf("高16位的二进制数为:");
for (int i = 0; i < 16; ++i) {
int binaryKey = left & (1 << 15 - i);
if (binaryKey != 0) binaryKey = 1;
printf("%d", binaryKey);
}
printf("\n");
printf("低16位的二进制数为:");
for (int i = 0; i < 16; ++i) {
int binaryKey = right & (1 << 15 - i);
if (binaryKey != 0) binaryKey = 1;
printf("%d", binaryKey);
}
printf("\n\n");
printf("将高位和地位的16进制相加,得到如下结果:\n");
//printf("\n");
int checksum16 = left + right;
printf("16位校验和的10进制为:%d\n", checksum16);
printf("16位校验和的2进制为:");
for (int i = 0; i < 16; ++i) {
int binaryKey = checksum16 & (1 << 15 - i);
if (binaryKey != 0) binaryKey = 1;
printf("%d", binaryKey);
}
printf("\n");
//转化为16进制为:
char checksumHex[5];
int hex = 0;
for (int i = 0; i <16; ++i) {
hex = hex + (checksum16&(1 << (15 - i)));
if ((i + 1) % 4 == 0) {
hex = hex >> (15 - i); //每四位的8421之和
//printf("测试:%d\n", hex);
if (hex < 10) checksumHex[i / 4] = hex + '0';
else checksumHex[i / 4] = hex - 10 + 'A';
hex = 0;
}
}
checksumHex[4] = '\0';
printf("\n16位校验和的16进制为:");
printf("%s\n", checksumHex);
}
int main()
{
//writefile();
//char*filename;
char*command;
char*filename;
command = new char[20];
filename = new char[20];
printf("请输入要执行的命令和处理的文件名(用空格分开):\n");
cin >> command >> filename;
//printf("输入的命令是:%s\n", command);
if (!strcmp(command, "check_sum")) { //字符串相等返回0
//printf("输入的文件名为:%s\n", filename);
try {
fstream file;
file.open(filename, ios::in);
printf("读取文件成功!\n");
}
catch (...) {
printf("文件打开失败,请确认文件是否存在!\n");
}
checksum(filename);
}
else {
printf("输入命令无效!\n");
}
//checksum(File);
}
实验结果
文件的字符串采用题目给定的例子,代码最终输出的结果也与题目给定结果相同,验证了算法的正确性。