题目
编写一个计算机程序用来计算一个文件的16位效验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到效验和中。
- 要求:
1. 以命令行形式运行:check_sum infile。其中check_sum为程序名,infile为输入数据文件名。
2. 输出:数据文件的效验和 - 附:效验和(checksum)
- 原理:把要发送的数据看成16比特的二进制整数序列,并计算他们的和。若数据字节长度为奇数,则在数据尾部补一个字节的0以凑成偶数。
- 例子:16位效验和计算,下图表明一个小的字符串的16位效验和的计算。为了计算效验和,发送计算机把每对字符当成16位整数处理并计算效验和。如果效验和大于16位,那么把进位一起加到最后的效验和中。
本地数据样式
分析
- 这道题的校验和没有要求最后取反,实际上求和后是要取反的;
- 原理不难理解,就是数据转换成十六进制,然后十六进制求和。四位十六进制,所以也要求进位加到结果中;
- 难点在于字符转十六进制,十六进制有进位相加(我这里是用string存储十六进制数,可能网上还有更直接的十六进制的存储方式和计算方式);
- 先用char类型数组把数据逐个存进数组中;
- 将每两个char字符转为四位十六进制,其中会先将每个char字符先转为二位十六进制;
- 最后进行十六进制的求和,我没有选择将进位留到最后再相加,而是每一次运算都完成进位的相加,因为经过多次运算后,不能确定进位的位数。
程序流程图
代码
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
//命令行输入
void waitInput(string command, string &infile) {
cout << "Please input check_sum infile, infile is the file name!" << endl;
cin >> command >> infile;
while (command != "check_sum") {
cout << "Wrong command! Please reenter!" << endl;
cin >> command >> infile;
}
cout << endl;
}
//一个字符转二位十六进制
string CToH(char charIndex) {
//char强制转换为int
int temp = charIndex;
//十六进制的一二位(现在是十进制)
int first = temp / 16;
int second = temp % 16;
//十进制转十六进制(用srting存)
string res;
string resFirst;
string resSecond;
if (first < 10) resFirst.append(to_string(first));
if (second < 10) resSecond.append(to_string(second));
if (first >= 10) {
switch (first) {
case 10: resFirst.append("A"); break;
case 11: resFirst.append("B"); break;
case 12: resFirst.append("C"); break;
case 13: resFirst.append("D"); break;
case 14: resFirst.append("E"); break;
case 15: resFirst.append("F"); break;
}
}
if (second >= 10) {
switch (second) {
case 10: resSecond.append("A"); break;
case 11: resSecond.append("B"); break;
case 12: resSecond.append("C"); break;
case 13: resSecond.append("D"); break;
case 14: resSecond.append("E"); break;
case 15: resSecond.append("F"); break;
}
}
res.append(resFirst);
res.append(resSecond);
return res;
}
//每对字符转为四位十六进制
string makePairChar(char firstChar, char secondChar) {
//两string相加即可
return CToH(firstChar) + CToH(secondChar);
}
//四位十六进制相加
void sum(string &first, string second) {
//进位
int carryInt = 0;
for (int i = 3; i >= 0; i--) {
//先将计算的该位转为十进制
int firstInt;
int secondInt;
int res;
if(first[i] <= '9') firstInt = (int)first[i] - '0';
else firstInt = (int)first[i] - 55;
if (second[i] <= '9') secondInt = (int)second[i] - '0';
else secondInt = (int)second[i] - 55;
//获得十进制结果
res = firstInt + secondInt + carryInt;
//进位归零
carryInt = 0;
//结果转换为char,存进string
if (res < 10) first[i] = res + '0';
else if (res >= 10 && res <= 15) first[i] = 'A' + res - 10;
else {
if (res > 15 && res < 26) first[i] = res - 16 + '0';
else first[i] = 'A' + res - 26;
carryInt = 1;
}
}
//若四位都运算完成后,有第五位的进位,则继续和进位运算,消除进位
if (carryInt) {
string carry = "0001";
sum(first, carry);
}
}
int main() {
//等待命令行输入
string infile, command;
waitInput(command, infile);
//打开文件
ifstream dataFile(infile);
if (!dataFile.is_open()) {
cout << "dataFile文件打开失败!" << endl;
return 1;
}
//将文件所有字符存进数组
//最后一个字符为-1,不存进数组
cout << "文件数据:" << endl;
vector<char> dataArray;
while (!dataFile.eof()){
char str = dataFile.get();
cout << str;
if ((int)str == -1) continue;
dataArray.push_back(str);
}
cout << endl << endl;
//用完关闭
dataFile.close();
//把每对字符当成16位整数处理
//用string数组存每对字符的16进制数
vector<string> pairChar;
//判断奇数个字符还是偶数个字符
int remainder = dataArray.size() % 2;
//若是奇数个,循环走到倒数第二个
//若是偶数个,循环走到倒数第一个
for (unsigned int i = 0; i < dataArray.size() - remainder; i += 2) {
string pairCharIndex = makePairChar(dataArray[i], dataArray[i + 1]);
pairChar.push_back(pairCharIndex);
}
//若数据字节长度为奇数,则在数据尾部补一个字节的0以凑成偶数
//一字节0也就是空字符,空字符就是\0
if (remainder) {
string pairCharIndex = makePairChar(dataArray[dataArray.size() - 1], '\0');
pairChar.push_back(pairCharIndex);
}
//计算校验和
string res = pairChar[0];
for (unsigned int i = 1; i < pairChar.size(); i++) sum(res, pairChar[i]);
//打印结果
cout << "校验和:" << endl;
cout << res << endl;
return 0;
}
结果截图
-
数据为例子(答案相同)
-
数据增加一行