I'm writing a simple encryption program in C++ to encrypt a text-based file.
It's using a simple XOR cipher algorithm, but this produces ASCII control characters in the output file. When I try to read from the newly encrypted file with std::ifstream, it stumbles upon character #26, it stops and becomes unable to read the rest of the file.
Example if I try to encrypt this text:
This is just a simple sample
text with two rows and one sentence.
It turns it to this
/[[[[[
[[[ [[[U
When I try to read that file in my program, it can't read past the character at position 15, so I get a half encrypted file.
How can I fix this?
Here's the code:
#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;
}
In Decrypt(), after the first call to std::getline(), sourceFile.good() is false and sourceFile.fail() is true, which is why you stop reading subsequent lines from the encrypted file.
The reason is because the encrypted file has an encoded 0x1A byte in it, and depending on your platform and STL implementation, that character likely gets interpreted as an EOF condition, thus enabling the std::ifstream's eofbit state, terminating further reading.
In my compiler's STL implementation on Windows, when std::ifstream reads from a file, it ultimately calls a function named _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);
}
}
When it tries to read an 0x1A character, fgetc() returns EOF, and when _Fgetc() returns false, std::getline() sets the eofbit on the std::ifstream and exits.
Check your compiler's STL for similar behavior.
This behavior is because you are opening the encrypted file in text mode. You need to open the encrypted file in binary mode instead:
std::ifstream sourceFile(..., std::ifstream::binary);
Also, you should enable binary mode on the encrypted file in Encrypt() as well:
std::ofstream outputFile(..., std::ofstream::binary | std::ofstream::trunc);
Try something more like this instead:
#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;
}
That being said, keep in mind that when using XOR, some of the encrypted characters might end up being \r (0x0D) or \n (0x0A), which will interfere with std::getline() when decrypting the file later on, producing a decrypted output that does not match the original text input.
Since you should be treating the encrypted file as binary, you should not be reading/writing the file as text at all. Choose a different format for your encrypted output that does not rely on line-break semantics in text vs binary mode.
For example:
#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;
std::string::size_type lineLen;
long numLines = 0;
while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
{
lineLen = line.length();
for (std::string::size_type i = 0; i < lineLen; ++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.write((char*)&lineLen, sizeof(lineLen));
outputFile.write(line.c_str(), lineLen);
++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;
std::string::size_type lineLen;
long numLines = 0;
while (sourceFile.read((char*)&lineLen, sizeof(lineLen))) // This iterates over the whole file, once for each line
{
line.resize(lineLen);
if (!sourceFile.read(&line[0], lineLen))
break;
for (std::string::size_type i = 0; i < lineLen; ++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;
}
ASCII value 26 is EOF on some operating systems.
You should probably treat your encrypted file as a byte stream rather than a text file for reading and writing. That means either using read() and write() functions of the IOStream or at the very least opening the files in binary mode.
If you're just enciphering your text instead of encrypting, maybe choose a different cipher (eg. ROT13) that is closed on the set of printable ASCII or UTF-8 characters.
I compiled your code in Linux (minus all the Windows stuff)...
I get this when encrypting your sentence with your code:
/[[[[[
[[[ [[[U
It also decrypts back to the original sentence. Without the goofy characters, it is the same as your output so your actual issue seems related to the encoding of the file and the program you are using to view the results. Stephan is correct in saying you should be reading/writing bytes instead of text. This can cause all sorts of issues with the characters you create. For example, line feeds and carriage returns since you are using getline().
Edit: Strange. After editing this answer, all the odd characters disappeared. Here is a screenshot:
int main(int argc, char *argv[]) {
ifstream inFile;
int numOfLines = 0, numOfTokens = 0, numOfStrings = 0, maxStringLength = 0, l = 0, fileCount=0, mostCommonCount=0;
string inputFile, mostCommonList="", word;
for(int i = 1; i < argc; i++){
if(strpbrk(argv[i] , "-")){
if(flags.find(string(argv[i]))!=flags.end()) flags[string(argv[i])] = true;
else{
cerr << "INVALID FLAG " << argv[i] << endl;
exit(1);
}
}
else{
inFile.open(argv[i]);
fileCount++;
if(!inFile && fileCount==1){
cerr << "UNABLE TO OPEN " << argv[i] << endl;
exit(1);
}
else{
string line;
while(getline(inFile, line)) inputFile+=line+='\n';
if(fileCount>1){
cerr << "TOO MANY FILE NAMES" << endl;
exit(1);
}
}
}
}
int linenum = 0;
TType tt;
Token tok;
while((tok = getNextToken(&inFile, &linenum))!=DONE && tok != ERR){
tt = tok.GetTokenType();
word = tok.GetLexeme();
if(flags["-v"]==true){
(tt == ICONST||tt==SCONST||tt==IDENT) ? cout<<enumTypes[tok.GetTokenType()]<<"("<< tok.GetLexeme()<<")"<<endl : cout<< enumTypes[tok.GetTokenType()]<<endl;
}
if(flags["-mci"]==true){
if(tt==IDENT){
(identMap.find(word)!=identMap.end()) ? identMap[word]++ : identMap[word]=1;
if(identMap[word]>mostCommonCount) mostCommonCount = identMap[word];
}
}
if(flags["-sum"]==true){
numOfTokens++;
if(tt==SCONST){
numOfStrings++;
l = word.length();
if(l > maxStringLength) maxStringLength = l;
}
}
}
if(tok==ERR){
cout << "Error on line" << tok.GetLinenum()<<"("<<tok.GetLexeme()<<")"<<endl;
return 0;
}
if(flags["-mci"]==true){
cout << "Most Common Identifier: ";
if(!identMap.empty()){
word ="";
for(auto const& it : identMap){
if(it.second==mostCommonCount) word += it.first + ",";
}
word.pop_back();
cout << word << endl;
}
}
if(flags["-sum"]){
numOfLines = tok.GetLinenum();
numOfLines = tok.GetLinenum();
cout << "Total lines: " << numOfLines << endl;
cout << "Total tokens: " << numOfTokens << endl;
cout << "Total strings: " << numOfStrings << endl;
cout << "Length of longest string: " << maxStringLength << endl;
}
inFile.close();
return 0;
}
For some reason this code is running infinitely. I cannot figure out the source of error. I also do not know whether this file or the other linked file is causing this error so I posted the main program code. I think is one of the switch statements that causing this error but I am not sure. FYI: I am supposed to make a lexical analyzer so I had three files one lexigh.h (contains all the data types and all the functions), getToken.cpp(file that defines the functions from lexigh.h) and the main program which calls the methods and tests it.
I want to create small program to understand things I need better.
This code can write words to text document, new line under previous in sequential order and keep lines there after starting program again.
Now before adding a new word or phrase to the file, I want to find if the word already exists in my document, if exist, don't add it, but get exist equal one on output, read it from file, and main thing here is somehow also find line under or above current exist line. For example: if exist line index is 3, I want to see +1 line 4 or -1 line 2. If new word doesn't exist in text document just add it.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
std::ofstream outfile("doc.txt", std::ios_base::app);
int main()
{
std::string t;
for (int i = 0; i < 10; i++)
{
cout << "Add new phrase: " << endl;
std::getline(std::cin, t);
cout << t << endl;
outfile << t << std::endl;
}
_getch();
return 0;
}
EDIT:
using namespace std;
std::ofstream outfile("doc.txt", std::ios_base::app);
int main()
{
int length = 100;
std::ifstream infile("doc.txt", std::ifstream::in);
infile.seekg(0, infile.end);
size_t len = infile.tellg();
infile.seekg(0, infile.beg);
char *buf = new char[len];
infile.read(buf, length);
infile.close();
std::string writtenStr(reinterpret_cast<const char *>(buf), len);
std::string t;
for (int i = 0; i < 10; i++)
{
std::getline(std::cin, t);
if (writtenStr.find(t) != std::string::npos)
{
cout << "Line [" << t << "] exist." << endl;
}
else
{
cout << "Line [" << t << "] saved." << endl;
writtenStr += t;
outfile << t << std::endl;
}
}
_getch();
return 0;
}
I'd read the file into a string when the program starts. Then, check the string for the phrase each time i want to add a new word. If the string doesn't contain the phrase, add it to the string and the file, and a delimiter of your choice if desired. For example:
int main()
{
// Read existing file into a string
std::ifstream infile("doc.txt", std::ifstream::in);
infile.seekg(0, infile.end);
size_t len = infile.tellg();
infile.seekg(0, infile.beg);
char *buf = new char[len];
infile.read(buf,length);
infile.close();
std::string writtenStr(reinterpret_cast<const char *>(buf), len);
// Open file for output
std::ofstream outfile("doc.txt", std::ios_base::app);
std::string t;
for (int i = 0; i < 10; i++)
{
// Get new phrase
std::getline(std::cin, t);
// Check if phrase is already in file;
if (writtenStr.find(t) == std::string::npos)
{
cout << "Could not add new phrase: " << endl;
cout << t << endl;
cout << "Phrase already exists in file." << endl;
}
else
{
cout << "Add new phrase: " << endl;
cout << t << endl;
writtenStr += t;
outfile << t << std::endl;
}
}
_getch();
return 0;
}
Basically when I close the program the vectors are put in a txt file and when the program starts it reads from that file.
If I put some information in the txt file and start the program, it works fine (ifstream works)
But I cannot add things to the file/create a file if there is not a file already (ofstream does not work)
Here is my code:
Library::Library()
{
ifstream fin("libBooks.txt");
string inputLine;
if (fin.fail())
{
lastBookIDUsed = 0;
}
getline(fin, inputLine);
lastBookIDUsed = stringToInt(inputLine);
while (!fin.eof())
{
getline(fin, inputLine);
if ((!fin.eof())&&(inputLine.length()!=0))
{
vector<string> token = tokenize(inputLine);
addBook(token[1], token[2], token[3], stringToInt(token[0]), stringToInt(token[4]));
}
else
{
break;
}
}
fin.close();
ifstream finPatron("libPatrons.txt");
string inputLinePatron;
if (finPatron.fail())
{
lastPatronIDUsed = 0;
}
getline(finPatron, inputLinePatron);
lastPatronIDUsed = stringToInt(inputLinePatron);
while (!finPatron.eof())
{
getline(finPatron, inputLinePatron);
if ((!finPatron.eof()) && (inputLinePatron.length() != 0))
{
vector<string> token = tokenize(inputLinePatron);
vector<int> TEMPbooksCheckedOutID;
//Create a vector for the IDs of books this patron has
int numOfBooksCheckedOut = token.size() - 5;
if (numOfBooksCheckedOut < 1)
{
for (int i = 0; i < numOfBooksCheckedOut; i++)
{
TEMPbooksCheckedOutID.push_back(stringToInt(token[4 + i]));
}
}
addPatron(token[1], token[2], token[3], stringToInt(token[0]), TEMPbooksCheckedOutID, stringToInt(token[4 + numOfBooksCheckedOut]));
}
else
{
break;
}
}
finPatron.close();
}
Library::~Library()
{
ofstream fout("libBooks.txt");
if (!fout.is_open())
std::cout << "Could not open libBooks" << endl;
fout << lastBookIDUsed << endl;
vector<Book>::iterator iter;
iter = vBook.begin();
while (iter != vBook.end())
{
if (!(*iter).isDeleted)
{
fout << (*iter).id << " ";
fout << (*iter).name << ";";
fout << (*iter).author << ";";
fout << (*iter).subject << ";";
fout << (*iter).checkOutBy << endl;
}
iter++;
}
fout.close();
ofstream foutPatron("libPatrons.txt");
if (!foutPatron.is_open())
std::cout << "Could not open libPatrons" << endl;
foutPatron << lastPatronIDUsed << endl;
vector<Patron>::iterator iterPatron;
iterPatron = vPatron.begin();
while (iterPatron != vPatron.end())
{
if (!(*iterPatron).isDeleted)
{
foutPatron << (*iterPatron).id << " ";
foutPatron << (*iterPatron).name << ";";
foutPatron << (*iterPatron).phoneNumber << ";";
foutPatron << (*iterPatron).email << ";";
vector<int>::iterator iter2;
iter2 = (*iterPatron).booksCheckedOutID.begin();
while (iter2 != (*iterPatron).booksCheckedOutID.end())
{
foutPatron << (*iter2) << ";";
iter2++;
}
foutPatron << (*iterPatron).hasAtLeastOneBook << endl;
}
iterPatron++;
}
foutPatron.close();
}
Thanks!
I have have the following problem:
When I drag and drop a file to my tool (exe) when ifstream fails to open the file.
If I give it manually though the console it works!
I don't get where the diffenence is, because I am cutting the path and passing just the filename.
Have a look at the code:
int main(int argc, char* argv[]) {
if (argc < 2) {
cout
<< "ERROR: Wrong amount of arguments! Give at least one argument ...\n"
<< endl;
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
exit(1);
return 0;
}
vector<string> files;
for (int g = 1; g < argc; g++) {
string s = argv[g];
cout<<"parameter at: " << g << " = " << argv[g] << "\n" << endl;
string filename = "";
int pos = s.find_last_of("\\", s.size());
if (pos != -1) {
filename.append(s.substr(pos + 1));
// cout<<" cutted path: " << s.substr(0,s.size()-filename.size()) << endl;
// cout << "argv[1] " << argv[1] << endl;
cout << "\n filename: " << filename << "\t pos: " << pos << endl;
files.push_back(filename);
}
files.push_back(s);
}
for (unsigned int k = 0; k < files.size(); k++)
{
cout << "files.at( " << k << " ): " << files.at(k).c_str() << endl;
Converter a(files.at(k).c_str());
a.getCommandsFromCSV();
a.saveConvertedFile();
}
cout << "\n" << "Programm finished...\n\n" << endl;
cin.ignore();
return 0;
}
It fails already on the constructor:
Converter::Converter(const char* file) {
filename = file;
myfile.open(filename.c_str(), ios_base::in);
cout << (myfile ? "open successful on constructor " : "some error on constructor");
cin.ignore();
trace_raw = "";
}
You have any idea why?
UPDATE:
The file as parameter works now. The solution was to leave the full path.
Anyway I have the same error on a hard coded file. I thought it may be the same that's why I added .\ at the beginning of the file name... without success.
The code:
void GenericCommandConverter::getATCommandsFromCSV() {
cout << "\t| +++++++++++getATCommandsFromCSV() started+++++++++++++ |"
<< endl;
/*
* CSV file name is hardcoded
*/
string filename_csv = ".\\test.csv";
string commands = "";
int pos_start = 0;
int pos_end = 0; // "|"
int substrLength = 0;
int separator_count = 0;
char c;
vector<string> lines;
vector<string> commandList;
vector<vector<string> > linesSeparated;
ifstream csvFile;
csvFile.open(filename_csv.c_str(), ios_base::in);
cout << (myfile ? "open successful on getATCommandsFromCSV " : "some error on getATCommandsFromCSV ");
cin.ignore();
...
UPDATE2:
The solution was: on dropping a file to the exe, the "root" folder changes to the one where the dropped file comes from. Giving the hardcoded file the path from the *.exe solved it!
I am guessing your current directory is wrong. Don't cut the path off. Anyway you should do error checking / debugging to see why it couldn't open the file. Diligent debugging is essential for solving problems without having to make blind guesses.