Trying to write a binary file from a struct. Unfortunately, it is writing out excessive spaces since my strings are all 20 characters long. I would use pointers, but my textbook specifically states not to use pointers (i.e. Remember: Only use fixed-size data members when writing to a binary file. Do not use pointers or classes that contain pointers as data members).
To make matters worse, the numbers I am trying to print out (i.e. 8 and 40) just appear as an 'A' and 'B'.
The output looks like this
Employee.dat (Binary file I am writing to)
Pauline Nordin A B
I want it to look like this, however.
Pauline Nordin 8.00 40.00
Here is my code:
Excerpt from protocol.cpp
//Open file
std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);
//Check if file is open
if(BinaryOpen.is_open())
{
//Priming read
BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
numberOfEmployees++;
//Read file
while(!BinaryOpen.eof())
{
BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
numberOfEmployees++;
//Close file
BinaryOpen.close();
}
//Write to binary file
std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);
//Check if file opened
if(BinaryWrite.is_open())
{
BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
sizeof(Employee) * numberOfEmployees);
//Close file
BinaryWrite.close();
}
else
std::cout << "\nWrite file did not open! " << std::endl;
}
else
std::cout << "\nFile did not open! " << std::endl;
}
Below are all the files for my program:
employee.txt (i.e. file I am reading from)
Pauline Nordin 8.00 40.00
main.cpp
#include <iostream>
#include "protocol.h"
#include "employee.h"
int main()
{
int menuChoice = 0;
int numberOfEmployees = 0;
//Create array of employees
Employee favoriteEmployees[NUMBER_OF_EMPLOYEES];
//To prevent garbage being printed out
for(int i = 0; i < BUFFER_LENGTH; i++)
{
favoriteEmployees[0].firstName[i] = 0;
favoriteEmployees[0].lastName[i] = 0;
favoriteEmployees[0].hourlyWage = 0;
favoriteEmployees[0].hoursWorked = 0;
}
PrintMenu();
GetMenuChoice(menuChoice);
ExecuteMenuChoice(menuChoice, favoriteEmployees, numberOfEmployees);
return 0;
}
protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include "employee.h"
const int NUMBER_OF_EMPLOYEES = 10;
//Function declarations
void PrintMenu();
void GetMenuChoice(int &menuChoice);
void ExecuteMenuChoice(int menuChoice, Employee favoriteEmployees[], int &numberOfEmployees);
#endif
protocol.cpp
#include <fstream>
#include <iostream>
#include "employee.h"
#include "protocol.h"
//Function definitions
void PrintMenu()
{
std::cout << "\n\nChapter 17 -- Learn By Doings " << std::endl;
std::cout << "\n1. Learn By Doing 17.2 " << std::endl;
std::cout << "2. Learn By Doing 17.3 " << std::endl;
std::cout << "3. Learn By Doing 17.4 " << std::endl;
std::cout << "4. Exit " << std::endl;
std::cout << ' ' << std::endl;
}
void GetMenuChoice(int &menuChoice)
{
std::cin >> menuChoice;
}
void ExecuteMenuChoice(int menuChoice, Employee favoriteEmployees[], int &numberOfEmployees)
{
switch(menuChoice)
{
case 1:
{
//Open file in append mode
std::ifstream BinaryOpen("name.txt", std::ios::app | std::ios::binary);
//Open file in write mode
/*std::ofstream BinaryOpen("name.txt", std::ios::out | std::ios::binary);*/
//Check if file is open
if(BinaryOpen.is_open())
{
//Perform appropriate file operatings
std::cout << "\nFile opened! " << std::endl;
//Close file
BinaryOpen.close();
}
//Else
else
std::cout << "\nFile did not open! " << std::endl;
}
break;
case 2:
{
//Open file
std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);
//Check if file is open
if(BinaryOpen.is_open())
{
//Priming read
BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
numberOfEmployees++;
//Read file
while(!BinaryOpen.eof())
{
BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
numberOfEmployees++;
//Close file
BinaryOpen.close();
}
//Write to binary file
std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);
//Check if file opened
if(BinaryWrite.is_open())
{
BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
sizeof(Employee) * numberOfEmployees);
//Close file
BinaryWrite.close();
}
else
std::cout << "\nWrite file did not open! " << std::endl;
}
else
std::cout << "\nFile did not open! " << std::endl;
}
break;
case 3:
break;
case 4:
break;
default:
std::cout << "\nInvalid input. Please enter an integer from 1 to 4. " << std::endl;
}
}
**employee.h**
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
const int BUFFER_LENGTH = 20;
struct Employee
{
char firstName[BUFFER_LENGTH];
char lastName[BUFFER_LENGTH];
float hourlyWage;
float hoursWorked;
};
#endif
This is why it's printing garbage:
BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
sizeof(Employee) * numberOfEmployees);
Those probably aren't spaces you are seeing, but NULL characters or other unprintables.
What you need to do is write the file fields:
BinaryWrite << favoriteEmployees[i].firstName << " ";
BinaryWrite << favoriteEmployees[i].lastName << " ";
BinaryWrite << std::setprecision(2) << favoriteEmployees[i].hourlyWage << " ";
BinaryWrite << std::setprecision(2) << favoriteEmployees[i].hoursWorked << std::endl;
It's probably a bit misleading to call these binary files, because they really just contain text. An actual binary file would contain fixed-length, length-prefixed or zero-terminated strings, and binary representations of numbers (which you inadvertently did in your original code).
That is probably a bad way to do this. Like Mr. Universe was saying in the comments those arrays have all those additional blanks in them that have no data. I would change the case 2 code to something more like the following:
//#include <iomanip> for the stream formatting used when printing to the file
case 2:
{
//Open file
std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);
//Check if file is open
if(BinaryOpen.is_open())
{
//Read file
char eol[10];
while(BinaryOpen.good())
{
BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
BinaryOpen >> eol;
numberOfEmployees++;
}
//Close file
BinaryOpen.close();
//Write to binary file
std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);
//Check if file opened
if(BinaryWrite.is_open())
{
for (int i = 0; i < numberOfEmployees; i++)
{
BinaryWrite << favoriteEmployees[i].firstName << " ";
BinaryWrite << favoriteEmployees[i].lastName << " ";
BinaryWrite << setiosflags(std::ios::fixed) << std::setprecision(2) << favoriteEmployees[i].hourlyWage << " ";
BinaryWrite << setiosflags(std::ios::fixed) << std::setprecision(2) << favoriteEmployees[i].hoursWorked << std::endl;
}
//Close file
BinaryWrite.close();
}
else
std::cout << "\nWrite file did not open! " << std::endl;
}
else
std::cout << "\nFile did not open! " << std::endl;
}
break;
As in my comment above. I have whipped up a quick example which will give you an idea of how to do this.
void SaveString(fstream& file_stream, string output)
{
// Note that any good string class will have an implicit conversion to char*
if (output.size() == 0)
return;
file_stream << (unsigned char)output.size();
for (unsigned char i=0; i<output.size(); i++)
file_stream << output[i];
// DONE (in theory)
}
Then all you need to do for the get is get 1 unsigned char at the start of your strings and then use that number in a for loop to getting chars and adding them to the string your loading into.
Related
the title says it all
This is my code:
I have a class called House, where i define the values.
class HOUSE
{
public:
int id;
string 1;
string 2;
string 3;
int an;
};
template<class Type>
class table
{
public:
vector<Type> V;
//double inceput;
//double sfirsit;
//int comparatii;
//int interschimbari;
public:
table();
void print();
void liniar();
};
template<class Type>
table<Type>::table()
{
ifstream file("file.txt");
ifstream file1("file1.txt");
if (file.fail() || file1.fail())
{
cerr << "Eroare la deschiderea fisierului!" << endl;
_getch();
exit(1);
}
HOUSE* value = new HOUSE;
while (!file.eof() || file1.fail())
{
file >> value->id;
file >> value->tara;
file >> value->brand;
file >> value->culoare;
file >> value->an;
this->V.push_back(*value);
}
file.close();
}
The print function for the values
template<class Type>
void table<Type>::print()
{
cout << endl << setw(50) << "AFISAREA DATELOR" << endl;
cout << setw(5) << "Id" << setw(15) << "1" << setw(20) << "2" << setw(17) << "3" << setw(20) << "an" << endl << endl;
for (int i = 0; i < this->V.size(); i++)
{
cout << setw(5) << this->V.at(i).id << setw(15)
<< this->V.at(i).1<< setw(17)
<< this->V.at(i).2<< setw(17)
<< this->V.at(i).3<< setw(25)
<< this->V.at(i).an << endl;
}
cout << endl << "Dimensiunea tabelului n= " << V.size() << endl;
}
{
file >> value->id;
file >> value->1;
file >> value->2;
file >> value->3;
file >> value->an;
this->V.push_back(*value);
}
file.close();
}
in main
int main() {
table<MOBILE>* file = new table<MOBILE>();
table<MOBILE>* file1 = new table<MOBILE>();
file ->print();
file1 ->print();
This is full code as requested.
Need make it somehow print the data from file1 and file2 .
thx
The problem is idk if called right. Because
file ->print();
file1 ->print();
both print data from the file only
There are no errors at all
There's lots and lots wrong with your code. but I'm going to ignore that and just answer what I think is your actual question.
You want two table objects, one of which reads from file.txt and the other reads from file1.txt. To do that you should pass the file name into the table::table constructor, so it knows which file to read from. Like this
template<class Type>
class table
{
...
public:
table(const char* filename); // constructor takes filename parameter
...
};
template<class Type>
table<Type>::table(const char* filename)
{
ifstream file(filename); // open filename
if (file.fail())
...
int main() {
table<MOBILE>* file = new table<MOBILE>("file.txt"); // read from file.txt
table<MOBILE>* file1 = new table<MOBILE>("file1.txt"); // read from file1.txt
file ->print();
file1 ->print();
}
While working on this project for my younger cousin, I encountered many difficulties. I was wondering if someone could help me use char to output complete sentences (or help me fix these bugs.) I'm a relatively new programmer (about 8 months). Here's my code/ what I have attempted. Most of the bugs cause the program to freeze once a sentence is imputed, or the files not responding when opened.
#include<iostream>
#include<fstream>
#include<cctype>
#include<iomanip>
class legoset
{
char setcatname[25];
char name[50];
char legoinclude[25];
char legotype[25];
public:
void create_category();
void show_category() const;
void modify();
void report() const;
int retacno() const;
};
void legoset::create_category()
{
std::cout << "Please enter a category name : \n";
std::cin >> setcatname;
//std::cin.getline(setcatname, 25);
std::cout << "Please enter your username! \n";
std::cin >> name;
std::cin.ignore();
std::cin.getline(name, 50);
std::cout << name << " , is it a vehicle or building (V/B)?\n";
std::cin >> legotype;
legotype[25] = toupper(legotype[25]);
std::cin.getline(legotype, 25);
std::cout << "\n Please enter the name of the lego set. \n";
std::cin >> legoinclude;
//std::cin.getline(legoinclude, 25);
std::cout << "\n\n Category Created Successfully!!!";
return;
}
void legoset::show_category() const
{
std::cout << "Category : \n" << setcatname;
std::cout << "Username Of Holder \n: " << name;
std::cout << " Lego type (B/V) : " << legotype;
std::cout << " Lego set (with details) : " << legoinclude;
return;
}
void legoset::modify()
{
std::cout << "Category : \n" << setcatname[25];
std::cout << "\nModify Holder's name : ";
std::cin.ignore();
std::cin.getline(name, 50);
std::cout << "\nModify A Building or vehicle class ( B/V ) : ";
std::cin >> legotype[25];
legotype[25] = toupper(legotype[25]);
std::cout << "\nModify Lego set (with details) : ";
std::cin >> legoinclude[25];
}
void legoset::report() const
{
std::cout << setcatname[25] << std::setw(10) << " " << name << std::setw(10) << " " << legotype[25] << std::setw(6) << legoinclude[25] << std::endl;
}
int legoset::retacno() const
{
return setcatname[25];
}
void write_legoset(); //function to write record in binary file
void display_sp(int); //function to display account details given by user
void modify_set(int); //function to modify record of file
void delete_set(int); //function to delete record of file
void display_all(); //function to display all account details
void intro(); //introductory screen function
int main()
{
char choice;
int num;
intro();
do
{
system("cls");
std::cout << "\n\n\n\tMAIN MENU";
std::cout << "\n\n\t01. New Category";
std::cout << "\n\n\t02. ADD A NEW SET";
std::cout << "\n\n\t03. ALL USERS HOLDER LIST";
std::cout << "\n\n\t04. DELETE A CATEGORY";
std::cout << "\n\n\t05. MODIFY A CATEGORY";
std::cout << "\n\n\t06. EXIT";
std::cout << "\n\n\tSelect Your Option (1-6) ";
std::cin >> choice;
system("cls");
switch (choice)
{
case '1':
write_legoset();
break;
case '2':
std::cout << "\n\n\tEnter The category Name : "; std::cin >> num;
display_sp(num);
break;
case '3':
display_all();
break;
case '4':
std::cout << "\n\n\tEnter The Category Name : "; std::cin >> num;
delete_set(num);
break;
case '5':
std::cout << "\n\n\tEnter The Category Name : "; std::cin >> num;
modify_set(num);
break;
case '6':
std::cout << "\n\n\tThanks for using lego managemnt system!";
std::exit;
break;
default: std::cout << "\a";
}
std::cin.ignore();
std::cin.get();
} while (choice != '6');
return 0;
}
//***************************************************************
// function to write in file
//****************************************************************
void write_legoset()
{
legoset lego;
std::ofstream outFile;
outFile.open("legoset.dat", std::ios::binary | std::ios::app);
lego.create_category();
outFile.write(reinterpret_cast<char *> (&lego), sizeof(legoset));
outFile.close();
}
//***************************************************************
// function to read specific record from file
//****************************************************************
void display_sp(int n)
{
legoset lego;
bool flag = false;
std::ifstream inFile;
inFile.open("legoset.dat", std::ios::binary);
if (!inFile)
{
std::cout << "File could not be open !! Press any Key...";
return;
}
std::cout << "\nLEGOSET DETAILS\n";
while (inFile.read(reinterpret_cast<char *> (&lego), sizeof(legoset)))
{
if (lego.retacno() == n)
{
lego.show_category();
flag = true;
}
}
inFile.close();
if (flag == false)
std::cout << "\n\nLego set does not exist in this file";
}
//***************************************************************
// function to modify record of file
//****************************************************************
void modify_set(int n)
{
bool found = false;
legoset lego;
std::fstream File;
File.open("legoset.dat", std::ios::binary | std::ios::in | std::ios::out);
if (!File)
{
std::cout << "File could not be open !! Press any Key...";
return;
}
while (!File.eof() && found == false)
{
File.read(reinterpret_cast<char *> (&lego), sizeof(legoset));
if (lego.retacno() == n)
{
lego.show_category();
std::cout << "\n\nPlease Enter The New Details For This Category." << std::endl;
lego.modify();
int pos = (-1)*static_cast<int>(sizeof(legoset));
File.seekp(pos, std::ios::cur);
File.write(reinterpret_cast<char *> (&lego), sizeof(legoset));
std::cout << "\n\n\t Category Updated!";
found = true;
}
}
File.close();
if (found == false)
std::cout << "\n\n Category Not Found ";
}
//***************************************************************
// function to delete record of file
//****************************************************************
void delete_set(int n)
{
legoset lego;
std::ifstream inFile;
std::ofstream outFile;
inFile.open("legoset.dat", std::ios::binary);
if (!inFile)
{
std::cout << "File could not be open !! Press any Key...";
return;
}
outFile.open("Temp.dat", std::ios::binary);
inFile.seekg(0, std::ios::beg);
while (inFile.read(reinterpret_cast<char *> (&lego), sizeof(legoset)))
{
if (lego.retacno() != n)
{
outFile.write(reinterpret_cast<char *> (&lego), sizeof(legoset));
}
}
inFile.close();
outFile.close();
remove("legoset.dat");
rename("Temp.dat", "legoset.dat");
std::cout << "\n\n\tCategory Deleted ..";
}
//***************************************************************
// function to display all accounts deposit list
//****************************************************************
void display_all()
{
legoset lego;
std::ifstream inFile;
inFile.open("legoset.dat", std::ios::binary);
if (!inFile)
{
std::cout << "File could not be open !! Press any Key...";
return;
}
std::cout << "\n\n\t\tUSER HOLDER LIST\n\n";
std::cout << "====================================================\n";
std::cout << "A/c no. NAME Type Balance\n";
std::cout << "====================================================\n";
while (inFile.read(reinterpret_cast<char *> (&lego), sizeof(legoset)))
{
lego.report();
}
inFile.close();
}
void intro()
{
std::cout << "\n\n\n\t LEGOSET";
std::cout << "\n\n\tMANAGEMENT";
std::cout << "\n\n\t SYSTEM";
std::cout << "\n\n\n\nMADE BY : Philippe Barry";
std::cin.get();
}
//***************************************************************
// END OF PROJECT
//***************************************************************
Note that array indices in C++ go from 0 to one less than the array size. Thus index 0 is the first element, index 1 is the second, etc. Thus if you declare a char array to be size 25, then accessing index 25 is past the end of the array and results in undefined behavior — your program may crash, freeze up, or literally anything else. Additionally, accessing index 24 would just give you the “null character” that comes after every string. If you’re sure that the input will be 24 characters long (and you shouldn’t be), then index 23 would contain the last character.
You really shouldn’t be using static-length char arrays in C++, anyway. Your current code, even if it worked most of the time with the char arrays, would fail when the input string was larger than the size of the array. Replace the types of all the char arrays with std::string, and the input function should work.
Furthermore, as Sam Varsavchik nicely put, don’t write an entire program at once. That makes it a nightmare to debug. Write your code in chunks — first write the input function, and a debug function that prints out the values of all the member variables. Debug that bit first, then go on to the rest.
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:
I have an assignment that wants me to write a program that reads a text file, then outputs a modified version of that file using a version of Ceasar cipher that shifts the characters of the file that the user calls based on the shift amount that they input. For example if my file reads "hi" and they shift it by 1 it should read "ij". But when I run the program it doesnt recognize when I call in1 or out1 and I just get an error message. Can anyone help me figure out what I'm doing wrong or offer any advice on how to move forward? Thank you in advance!
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cctype>
using namespace std;
int main()
{
//Declare user variables
int shift, file1chars = 0;
char filename[25];
ifstream in1;
ofstream out1;
do
{
in1.clear(); //clear status flags
//Prompt user to enter name of input file and amount of shift
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
//Open file name
in1.open(filename);
//Error message if no file
if (!in1)
cout << "That is not a valid file. Try again\n";
} while (!in1);
do
{
out1.clear(); //clear status flags
//prompt user to input out file
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
out1.open(filename);
//Error message if no file
if (!out1)
cout << "That is not a valid file. Try again\n";
} while (!out1);
//write some code to the input file
in1 >> "hi"
//write the integers in a different order to the output file
out1 << //idk where to go from here to initiate shift
//prompt user to enter shift
cout << "Please intput the shift amount: ";
cin >> shift;
cout << "Processing complete" << endl;
//Call file (?)
//Tell user file input is complete and is now printing statistics
cout << "\nShifted input file Complete. Now printing statistics " << endl;
//Show statistics for file
cout << "\nStatistics for file: " << filename << endl;
cout << "------------------------------------------------";
//Show characters in file and stats before shift
cout << "\n\nTotal # of characters in file: " << file1chars << endl;
cout << "Statistics before shift: " << endl;
//Show file before shift
//Show user stats after shift
cout << "\nStatistics after shift: " << endl;
//File after shift
//Close files
out1.close();
in1.close();
return 0;
}
Instead of looking at your code for line by line to see where the problem(s) could be, I ask you to think about your requirements and write code that expresses the intent as closely as possible. Create functions that follow the intended functionality.
Get the input file name.
Get the output file name.
Read the contents of the input file.
Transform the contents of the input file to create the output string.
Write the output string to the output file.
In pseudo code,
int main()
{
infile = get_input_filename()
outfile = get_output_filename()
contents = get_file_contents(infile)
output = transform_input(contents)
write_output(outfile, output)
}
Convert that to C++ code:
// Declare the functions.
std::string get_input_filename();
std::string get_output_filename();
std::string get_file_contents(std::string const& infile)
std::string transform_input(std::string const& contents)j
void write_output(std::string const& outfile, std::string const& output);
// Use them in main.
int main()
{
std::string infile = get_input_filename();
std::string outfile = get_output_filename();
std::string contents = get_file_contents(infile);
std::string output = transform_input(contents);
write_output(outfile, output);
}
// Implement the functions.
std::string get_input_filename()
{
std::string filename;
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_output_filename()
{
std::string filename;
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_file_contents(std::string const& infile)
{
std::ifstream inf(infile);
std::string contents;
int c;
while ( (c = inf.get()) != EOF )
{
contents += c;
}
return contents;
}
std::string transform_input(std::string const& contents)
{
std::string res;
// Do the needful to transform contents to res.
return res;
}
void write_output(std::string const& outfile, std::string const& output)
{
std::ofstream outf(outfile);
outf.write(output.c_str(), output.size();
}
If you are able to use a class or struct and functions I would propose something like this:
main.cpp
#include <string>
#include <iostream>
#include <fstream>
#include "CaesarShift.h"
int main() {
std::string filename;
std::cout << "Please enter the name of the input file. ";
std::cin >> filename;
std::ifstream fileIn;
std::string text;
fileIn.open( filename );
if ( !fileIn.is_open() ) {
std::cout << "Failed to open file: " << filename << "." << std::endl;
}
fileIn >> text;
fileIn.close();
CaesarText caesarText;
caesarText.addText( text );
std::cout << "Contents of the Caesar Text before peforming a Caesar Shift:\n"
<< caesarText << std::endl;
int amount = 0;
std::cout << "Please enter the amount to shift the text ";
std::cin >> amount;
std::cout << "Now performing the Caesar Shift: " << std::endl;
caesarShift( caesarText, amount );
std::cout << "Caesar Text after performing a Caesar Shift:\n"
<< ceasarText << std::endl;
std::ofstream fileOut;
fileOut.open( std::string( "shifted_" + filename ) );
if ( !fileOut.is_open() ) {
std::cout << "Failed to open shifted_" << filename << std::endl;
}
// Uncomment to print original text to file otherwise only modified text will be printed.
// fileOut << caesarText.originalText() << std::endl;
fileOut << caesarText.shiftedText() << std::endl;
fileOut.close();
system( "PAUSE" );
return 0;
}
CaesarShift.h
#ifndef CAESAR_SHIFT_H
#define CAESAR_SHIFT_H
class CaesarText {
std::string _originalText;
std::string _shiftedText;
public:
caesarText() = default;
explicit CeasarText( const std::string& text ) :
_originalText( text ) {}
void addText( const std::string& text ) {
_originalText = text;
}
std::string originalText() const {
return _originalText;
}
std::string shiftedText() const {
return _shiftedText;
}
friend void caesarShift( caesarText& c, int amount );
friend std::ostream& operator<<( std::ostream& out, const caesarText& ceasarText );
};
#endif // !CAESAR_SHIFT_H
CaesarShift.cpp
#include "CaesarShift.h"
#include <string>
#include <iostream>
#include <algorithm>
// Overloaded ostream operator
std::ostream& operator<<( std::ostream& o, const CaesarText& c ) {
o << "Original Text: " << c._originalText << "\n";
o << "Shifted Text: " << c._shiftedText << "\n";
return o;
}
// public friend function (visible in main) not actually a part of the class
// but performs operations on it.
// To perform the Caesar Shift here are 3 possible variations of implementing the same task.
void caesarShift( Caesar Text& text, int amount ) {
// Bound amount to the number of characters in the alphabet
// the same value used in any of the three variations below.
amount %= 26;
// Older c++ style loop.
/*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}*/
// Modern C++ style loop
/*for ( auto& c : text._originalText ) {
text._shiftedText += c + amount;
}*/
// std::transform( s1.begin, s1.end, back_inserter( s2 ), lamda as predicate );
/*std::transform( text._originalText.begin(), text._originalText.end(),
std::back_inserter( text._shiftedText ),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);*/
}
As for the 3 different variations of performing the Caesar Shift you can just uncomment the appropriate block section.
My goal is to take the text from "filename" that I have in my main directory, take the odd lines and send them over the filenameA, and the even lines to filenameB. From there, I want to splice them back into a new file. How can I create a while loop to do so?
// This program splits a file into two files from main directory
// and then splices the original file with a new name.
#include<iostream>
#include<fstream>
using namespace std;
void pause();
int main()
{
char filename[] = "Lab2Test.txt";
char filenameA[] = "LabTest-FA.txt";
char filenameB[] = "LabTest-FB.txt";
ifstream origin(filename);
ofstream fA(filenameA);
ofstream fB(filenameB);
if (! origin)
{
cout << filename << " could not be opened." << endl;
return -1;
}
string s;
int i=0;
while(getline(origin, s))
{
if(i % 2 == 1) //odd - write to LabTest-FA
fA << s << endl;
else
fB << s << endl;
i++;
}
}
void pause()
{
cin.sync();
cout << "Press any key to continue..." << endl;
cin.ignore();
}
First pick a book on C++ and learn a bit more about the language, you have lots of mistakes in the code.
Here is a working program, the best I can come up with.
// This program splits a file into two files from main directory
// and then splices the original file with a new name.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void pause()
{
cin.sync();
cout << "Press any key to continue..." << endl;
cin.ignore();
}
int main()
{
char filename[] = "Lab2Test.txt";
char filenameA[] = "LabTest-FA.txt";
char filenameB[] = "LabTest-FB.txt";
char filenew[] = "Lab2Test2.txt";
ifstream origin(filename);
fstream fA(filenameA, std::fstream::in | std::fstream::out | std::fstream::trunc);
fstream fB(filenameB, std::fstream::in | std::fstream::out | std::fstream::trunc);
ofstream fnew(filenew);
if (!origin)
{
cout << filename << " could not be opened." << endl;
return -1;
}
string s;
int i = 0;
while( getline(origin, s) )
{
if(i % 2 == 1) // odd - write to LabTest-FA
fA << s << endl;
else // even - write to LabTest-FB
fB << s << endl;
i++;
}
fA.flush(); // write to disk
fB.flush();
fA.seekg(0, ios::beg); // rewind the files to the beginning
fB.seekg(0, ios::beg);
string s1, s2;
while( getline(fB,s1) )
{
fnew << s1 << endl;
if(getline(fA,s2))
fnew << s2 << endl;
}
pause();
}