題目
編寫一個計算機程序用來計算一個文件的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;
}
結果截圖
-
數據為例子(答案相同)
-
數據增加一行