我用C ++编写一个简单的加密程序来加密基于文本的文件。
它使用简单的XOR密码算法,但这会在输出文件中生成ASCII控制字符。当我尝试使用std::ifstream
读取新加密的文件时,它会遇到字符#26
,它会停止并无法读取文件的其余部分。
如果我尝试加密此文本的示例:
This is just a simple sample text with two rows and one sentence.
将其转为此
/[[[[[ [[[ [[[U
当我尝试在程序中读取该文件时,它无法读取位置15处的字符,因此我获得了一半加密文件。
我该如何解决这个问题?
以下是代码:
#include <iostream>
#include <Windows.h>
#include <string>
#include <fstream>
void Encrypt(char encryptionKey, std::string filename)
{
std::ifstream sourceFile(filename);
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::string sourceLine;
std::string outputLine;
long numLines = 0;
if (sourceFile.is_open())
{
std::cout << "Opening file: " + filename + " for encryption" << std::endl;
while (sourceFile.good()) // This iterates over the whole file, once for each line
{
sourceLine = ""; //Clearing the line for each new line
outputLine = ""; //Clearing the line for each new line
std::getline(sourceFile, sourceLine);
for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
{
char focusByte = sourceLine[i] ^ encryptionKey;
std::cout << " focusByte: " << focusByte << std::endl;
outputLine.push_back(focusByte);
//std::cout << sourceLine << std::flush;
}
numLines++;
outputFile << outputLine << std::endl;
}
}
sourceFile.close();
outputFile.close();
}
void Decrypt(unsigned int encryptionKey, std::string filename)
{
std::ifstream sourceFile(filename);
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::string sourceLine;
std::string outputLine;
long numLines = 0;
if (sourceFile.is_open())
{
std::cout << "Opening file: " + filename + " for decryption" << std::endl;
while (sourceFile.good()) // This iterates over the whole file, once for each line
{
if (sourceFile.fail() == true)
std::cout << "eof" << std::endl;
sourceLine = ""; //Clearing the line for each new line
outputLine = ""; //Clearing the line for each new line
std::getline(sourceFile, sourceLine);
for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
{
char focusByte = sourceLine[i] ^ encryptionKey;
std::cout << " focusByte: " << focusByte << std::endl;
outputLine.push_back(focusByte);
}
numLines++;
outputFile << outputLine << std::endl;
}
}
sourceFile.close();
outputFile.close();
}
int main(int argument_count,
char * argument_list[])
{
system("color a");
std::string filename;
if (argument_count < 2)
{
std::cout << "You didn't supply a filename" << std::endl;
}
else
{
filename = argument_list[1];
std::cout << "Target file: " << filename << std::endl;
std::cout << "Press e to encrypt the selected file, Press d to decrypt the file > " << std::flush;
char choice;
while (true)
{
std::cin >> choice;
if (choice == 'e')
{
Encrypt(123, filename);
break;
}
else if (choice == 'd')
{
Decrypt(123, filename);
break;
}
else
{
std::cout << "please choose option e or d for encryption respectivly decryption" << std::endl;
}
}
}
std::cout << "\nPaused, press Enter to continue > " << std::flush;
system("Pause");
return EXIT_SUCCESS;
}
答案 0 :(得分:3)
在Decrypt()
中,在第一次调用std::getline()
后,sourceFile.good()
为false且sourceFile.fail()
为真,这就是您停止从加密文件中读取后续行的原因。
原因是因为加密文件中有一个编码的0x1A
字节,并且根据您的平台和STL实现,该字符可能被解释为EOF条件,从而启用std::ifstream
& #39; s eofbit
状态,终止进一步阅读。
在我的编译器在Windows上的STL实现中,当std::ifstream
从文件中读取时,它最终会调用名为_Fgetc()
的函数:
template<> inline bool _Fgetc(char& _Byte, _Filet *_File)
{ // get a char element from a C stream
int _Meta;
if ((_Meta = fgetc(_File)) == EOF) // <-- here
return (false);
else
{ // got one, convert to char
_Byte = (char)_Meta;
return (true);
}
}
当它尝试阅读0x1A
个字符时,fgetc()
会返回EOF
,当_Fgetc()
返回false时,std::getline()
会设置eofbit
在std::ifstream
并退出。
检查编译器的STL是否有类似的行为。
此行为是因为您是opening the encrypted file in text mode。您需要以binary
模式打开加密文件:
std::ifstream sourceFile(..., std::ifstream::binary);
此外,您还应在binary
中的加密文件上启用Encrypt()
模式:
std::ofstream outputFile(..., std::ofstream::binary | std::ofstream::trunc);
尝试更像这样的东西:
#include <Windows.h>
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
void Encrypt(char encryptionKey, const std::string &filename)
{
std::string::size_type pos = filename.find_last_of("\\");
std::string out_filename = filename.substr(0, pos+1) + "Encrypted" + filename.substr(pos + 1);
std::ifstream sourceFile(filename.c_str());
std::ofstream outputFile(out_filename.c_str(), std::ofstream::binary | std::ofstream::trunc);
if (sourceFile.is_open())
{
std::cout << "Opened file: " + filename + " for encryption" << std::endl;
std::string line;
long numLines = 0;
while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
{
for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
{
char focusByte = line[i] ^ encryptionKey;
std::cout << " focusByte: " << focusByte << std::endl;
line[i] = focusByte;
//std::cout << line << std::flush;
}
outputFile << line << std::endl;
++numLines;
}
}
}
void Decrypt(char encryptionKey, const std::string &filename)
{
std::string::size_type pos = filename.find_last_of("\\");
std::string out_filename = filename.substr(0, pos+1) + "Decrypted" + filename.substr(pos + 1);
std::ifstream sourceFile(filename.c_str(), std::ifstream::binary);
std::ofstream outputFile(out_filename.c_str(), std::ofstream::trunc);
if (sourceFile.is_open())
{
std::cout << "Opened file: " + filename + " for decryption" << std::endl;
std::string line;
long numLines = 0;
while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
{
for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
{
char focusByte = line[i] ^ encryptionKey;
std::cout << " focusByte: " << focusByte << std::endl;
line[i] = focusByte;
}
outputFile << line << std::endl;
++numLines;
}
std::cout << "eof" << std::endl;
}
}
int main(int argument_count, char* argument_list[])
{
std::system("color a");
std::string filename;
if (argument_count < 2)
{
std::cout << "Enter a file to process: " << std::flush;
std::getline(std::cin, filename);
}
else
{
filename = argument_list[1];
}
if (filename.empty())
{
std::cout << "You didn't supply a filename" << std::endl;
return EXIT_FAILURE;
}
std::cout << "Target file: " << filename << std::endl;
std::cout << "Press e to encrypt the file" << std::endl;
std::cout << "Press d to decrypt the file" << std::endl;
char choice;
while (true)
{
std::cout << "> " << std::flush;
std::cin >> choice;
if (choice == 'e')
{
Encrypt(123, filename);
break;
}
else if (choice == 'd')
{
Decrypt(123, filename);
break;
}
else
{
std::cout << "please choose option e or d for encryption or decryption, respectively" << std::endl;
}
}
std::cout << std::endl << "Paused, press Enter to continue" << std::flush;
std::system("pause");
return EXIT_SUCCESS;
}
话虽如此,请记住,在使用XOR时,某些加密字符可能最终为\r
(0x0D)或\n
(0x0A),这会干扰{{1}当稍后解密文件时,产生与原始文本输入不匹配的解密输出。
由于您应该将加密文件视为二进制文件,因此您不应该将文件作为文本读取/写入。为加密输出选择不同的格式,不依赖于文本与二进制模式中的换行语义。
例如:
std::getline()
答案 1 :(得分:2)
在某些操作系统上,ASCII值26是EOF。
您应该将加密文件视为字节流而不是文本文件进行读写。这意味着要么使用IOStream的read()
和write()
函数,要么至少以二进制模式打开文件。
如果您只是加密文本而不是加密,可以选择在可打印的ASCII或UTF-8字符集上关闭的其他密码(例如ROT13)。
答案 2 :(得分:1)