今私は、テキストをシーザー方式で暗号化するプログラムを作っています。
ただし文字をずらすのではなく、文字コードをずらしています。
純粋に定数分だけずらすことはできたので、ずらす値を乱数で取得する、ちょっとした改良版のようなものを作っています。
同じシード値を与えて逆方向にずらせば平文が得られる形です。
プログラムとしては、標準入力でファイルパスを取得し、(encrypted)という名前を付加した新規ファイルに暗号化結果を書き込み、
もしパスに(encrypted)が含まれていれば復号処理にする流れにしました。
#include <iostream>
#include <fstream>
#include <string>
#include <typeinfo>
#include <algorithm>
#include <random>
#include <iomanip>
using namespace std;
const string ENCRYPTION_SUFFIX("(encrypted)");
const string DECRYPTION_SUFFIX("(decrypted)");
enum class Operation{
Encryption,
Decryption
};
int main()
{
string fileInPath, fileOutPath;
cout.setf(ios_base::hex, ios_base::basefield);
cout.setf(ios_base::showbase);
mt19937 mt19937(0xffff);
uniform_int_distribution<> dist(1, 3);
while (true){
// パス取得(stdin)
cout << "type the path of the file you want to encrypt (you can also Drag and Drop the file).\n"
"type \"0\" to quit this program.\n";
getline(cin, fileInPath);
// 0が入力されれば終了
if (fileInPath == "0")
break;
// ""をはずす
const auto endIt = remove(fileInPath.begin(), fileInPath.end(), '\"');
fileInPath.erase(endIt, fileInPath.end());
// ファイル名に(encrypted)が含まれていれば、復号化処理にする
const Operation operation = fileInPath.find(ENCRYPTION_SUFFIX, fileInPath.find_last_of("\\/")) != string::npos ? Operation::Decryption : Operation::Encryption;
try{
// 入力元oepn
ifstream filein(fileInPath.c_str());
filein.exceptions(ifstream::badbit);
// 出力先open(サフィックス追加)
fileOutPath = fileInPath;
fileOutPath.insert(fileOutPath.rfind("."), operation == Operation::Encryption ? ENCRYPTION_SUFFIX : DECRYPTION_SUFFIX);
ofstream fileout(fileOutPath.c_str());
fileout.exceptions(ofstream::badbit);
// 暗号化(シーザー)
string buf;
while (getline(filein, buf)){
// cout << buf << "\n";
if (operation == Operation::Encryption && ofstream::eofbit)
buf += "\n";
for (auto&& elem : buf){
// cout << setw(10) << static_cast<int>(elem) << " to ";
elem += operation == Operation::Encryption ? dist(mt19937) : dist(mt19937) * -1;
// cout << setw(10) << static_cast<int>(elem) << "\n";
}
// 0x5cがある場合はエスケープされないように直後に追加する
size_t pos = 0;
while ((pos = buf.find('\\', pos)) != string::npos){
buf.insert(pos++, "\\");
}
fileout << buf;
// cout << buf << "\n";
}
}
catch (exception& e){
cerr << "[ " << typeid(e).name() << " ] exception was thrown because of [ " << e.what() << " ]\n" << endl;
return -1;
}
clog << (operation == Operation::Encryption ? "Encryption" : "Decryption") << " is successfully completed.\n\n";
}
clog << "end program..." << endl;
return 0;
}
このようなテキストファイルをあてがうと、2行目を読み込んだ時に、(プログラムの)75行目のwhile文で無限ループに陥ってしまいます。(永久に'\\'が見つかってしまいposがどんどん増えていきます)
コメント部分はデバッグ表示用なのですが、はずして確かめてみると、
やはり暗号化した結果0x5cが出現したときに問題が起きているように思います(これが含まれないテキストは正常に暗号化・復号化できました)。
posの進め方が問題なのかと思い、posを前置にしたり、インクリメント自体なしも試したのですが、動作は変わらず、
+2や、前置インクリメントしてその後別に+1だと止まらなくはなったものの正しい結果が得られなくなりました。
(このようになります)
(ちなみに75行目からのwhile文なしの場合はこうなりました)
insertした後で位置を進めておけば、ちょうど挿入した直後の位置になるかと思ったのですが、意図したとおりになりません…。
どのようにするのが良いでしょうか?