C++ XOR加密程序遇ASCII控制字符致std::ifstream读取中断的解决方法
解决XOR加密后文件读取提前终止的问题
你遇到的问题很典型:默认的文本模式流会把ASCII 26(Ctrl+Z,也就是你说的#26)当作文件结束符,导致读取加密文件时提前停止。加密后的内容本质是二进制数据(包含任意字节值),不能用文本模式处理,必须切换到二进制模式来读写文件。
问题根源
当你用std::ifstream默认打开文件时,它处于文本模式:
- 在Windows系统中,文本模式会将ASCII 26(
0x1A)识别为EOF,一旦遇到这个字符,流就会停止读取。 - 文本模式还会自动转换换行符(比如把
\n转成\r\n),这会破坏加密后的二进制数据,导致解密出错。
而XOR加密会生成包含任意字节的内容,很容易出现像ASCII 26这样的控制字符,所以必须用二进制模式来处理这些文件。
解决方案
你需要对代码做以下几个关键修改:
1. 用二进制模式打开所有文件
在创建std::ifstream和std::ofstream时,添加std::ios::binary标志,告诉流以二进制模式处理数据,不做任何字符转换。
2. 替换getline为二进制读取方式
std::getline是为文本设计的,它依赖换行符分割内容,而加密后的二进制数据可能包含任意字节(包括换行符或没有换行符),所以应该直接读取整个文件的字节内容,而不是按行读取。
修改后的代码示例
下面是调整后的Encrypt和Decrypt函数,以及对应的修改说明:
加密函数修改
void Encrypt(char encryptionKey, std::string filename) { // 以二进制模式打开源文件 std::ifstream sourceFile(filename, std::ios::binary); // 以二进制模式创建输出文件 std::ofstream outputFile( filename.substr(0, filename.find_last_of("\\")) + "\\Encrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc | std::ios::binary ); if (sourceFile.is_open() && outputFile.is_open()) { std::cout << "Opening file: " + filename + " for encryption" << std::endl; // 一次性读取整个文件的内容到字符串中 std::string sourceContent( (std::istreambuf_iterator<char>(sourceFile)), std::istreambuf_iterator<char>() ); std::string outputContent; outputContent.reserve(sourceContent.size()); // 预分配内存,提升效率 for (char c : sourceContent) { char encryptedByte = c ^ encryptionKey; outputContent.push_back(encryptedByte); } // 写入加密后的内容 outputFile.write(outputContent.data(), outputContent.size()); std::cout << "Encrypted " << sourceContent.size() << " bytes successfully." << std::endl; } else { std::cerr << "Failed to open file(s) for encryption." << std::endl; } sourceFile.close(); outputFile.close(); }
解密函数修改
void Decrypt(unsigned int encryptionKey, std::string filename) { // 同样以二进制模式打开加密文件 std::ifstream sourceFile(filename, std::ios::binary); std::ofstream outputFile( filename.substr(0, filename.find_last_of("\\")) + "\\Decrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc | std::ios::binary ); if (sourceFile.is_open() && outputFile.is_open()) { std::cout << "Opening file: " + filename + " for decryption" << std::endl; // 一次性读取所有加密字节 std::string encryptedContent( (std::istreambuf_iterator<char>(sourceFile)), std::istreambuf_iterator<char>() ); std::string decryptedContent; decryptedContent.reserve(encryptedContent.size()); for (char c : encryptedContent) { char decryptedByte = c ^ encryptionKey; decryptedContent.push_back(decryptedByte); } // 写入解密后的内容 outputFile.write(decryptedContent.data(), decryptedContent.size()); std::cout << "Decrypted " << encryptedContent.size() << " bytes successfully." << std::endl; } else { std::cerr << "Failed to open file(s) for decryption." << std::endl; } sourceFile.close(); outputFile.close(); }
额外优化点
- 原来的
while (sourceFile.good())判断方式容易出错,因为good()的状态是在读取操作之后才更新的,可能导致最后一次读取无效数据。用std::istreambuf_iterator一次性读取整个文件更可靠。 - 预分配字符串内存(
reserve)可以避免频繁的内存分配,提升程序效率。 - 添加了错误检查(判断文件是否成功打开),让程序更健壮。
这样修改后,你的程序就能完整读取加密后的文件,不会因为遇到ASCII控制字符而提前终止了。
内容的提问来源于stack exchange,提问作者Tim Roberts
相关产品推荐
相关产品推荐

