Stuck on a decoding program - c++

For my C++ class our latest lab is to write a decoding program like so:
Write a program to read a file containing an encrypted message, which
must be decoded and printed on the screen. Use the following key to
decode: input text : abcdefghijklmnopqrstuvwxyz decoded text:
iztohndbeqrkglmacsvwfuypjx
That means each 'a' in the input text should be replaced with an 'i',
each 'b' with a 'z' and so forth. Punctuation and space should be
kept as is. You will notice that all the letters in the text are
lowercase, so the second step will be to fix the captalization. First
letter of each sentence should be capitalized. Print the decoded
text to the screen. You must use an object-oriented approach on this
lab. The specification of class Message is given in header file
Message.h. You need to implement each of the member functions in the
Message.cpp, which you will turn in. The main function is also given
so you can test your class, but you don't have to turn it in. I have
added in the class definition in "Message.h" that explain how to
implement each member function. Constructor: Should open the text
file and determine its size. To do this, call getFileSize() which is
implemented in "Message.h". Check for errors when opening the input
file and don't forget to close it at the end. If there is an error,
such as the file can't be located, make sure to set length to zero.
Otherwise, use the file size to allocate space for message.
Destructor: should free the space allocated for message.
decode: decodes the message according to the given key.
fixCapitalization: capitalizes the first letter of each sentence.
dump: prints the content of message on the screen
isEmpty: returns whether message is empty of not.
You only have to turn in message.cpp. You have to make sure it works
with the header file that I'm providing, since it will be used to
compile your message.cpp.
The header file provided was:
class Message
{
private:
char *message; // holds the message
int length; // holds the the message length
static const short ALPHABET_SIZE = 26;
char code[ALPHABET_SIZE]; // holds the cypher alphabet
// iztohndbeqrkglmacsvwfuypjx
// ex: an 'a' in the original message should be converted to 'i', 'b' should be converted to 'z' and so forth
// returns the input file size in bytes
std::streamsize getFileSize(std::fstream &file) const
{
std::streamsize fsize = 0;
file.seekg (0, std::ios::end);
fsize = file.tellg();
file.seekg (0, std::ios::beg); // moves file pointer back to the beginning
return fsize;
}
public:
/*
* This constructor tries to open the file whose name is passed
* to it in filename. If file opens successfully, calls function
* getFileSize to determine how many bytes should be allocated
* for the message. Allocates space for message and reads the
* content from the file into it. Closes the file at the end.
* Member variable length should be set to the file size.
* If file cannot be found, length should be set to zero.
*/
Message(std::string filename);
// The destructor frees the space allocated to message
virtual ~Message();
// Decodes the message
void decode();
// Capitalizes first letter in each sentence
void fixCapitalization();
// Prints the content of message on the screen
void dump() const;
// Returns true if the message is empty
bool isEmpty() const;
};
What I'm really stuck on is how to use a constructor to open the file, and read the size of it.
I can't even figure out how to open the file with the constructor at all.
I tried to use ifstream in my constructor like so:
Edited 2/24/17 10:36 a.m.:
#include <iostream>
#include <fstream>
#include <string>
#include "Message.h"
using namespace std;
int main()
{
// create a message object with the content of Encrypted.txt
Message m ("Encrypted.txt");
if (m.isEmpty())
{
std::cout << "Could not read message";
return EXIT_FAILURE;
}
std::cout << "Original message: " << std::endl;
m.dump();
std::cout << std::endl << std::endl;
m.decode();
m.fixCapitalization();
std::cout << "Decoded message: " << std::endl;
m.dump();
std::cout << std::endl << std::endl;
return EXIT_SUCCESS;
}
Message::Message(std::string filename)
{
fstream infile;
infile.open(filename.c_str(), std::ifstream::in);
// read contents
// close the file
if (!infile)
{
length=0;
}else
{
// getFileSize and allocate so much space for message
std::streamsize length = getFileSize(infile);
message = new char[length + 1];
//copy contents from file to message
for (int i = 0; i < length + 1; i++)
{
infile >> new char[i];
}
}
infile.close();
}
Message::~Message()
{
}
void Message::decode()
{
}
void Message::fixCapitalization()
{
}
void Message::dump() const
{
cout << message;
}
bool Message::isEmpty() const
{
if (length == 0)
{
return true;
}else
return false;
}
This is what I've put together so far. When I run the program it tells me "Could not read message" so I assume that the file was not opened properly. Can someone check over my code for me and tell me where I went wrong?

char *message = new char;
*message = message[length];
allocates space for one char, and then reads the invalid location message[length], which is undefined.
You need to allocate length + 1 characters.
I would write it like this:
Message::Message(std::string filename)
: message(nullptr),
length(0)
{
std::ifstream infile(filename);
if (infile)
{
length = getFileSize(infile);
message = new char[length + 1];
// ...
}
}
The destructor of std::ifstream takes care of closing the stream if it was open, so you don't need to do that by hand. (Tell your teacher about this; they seem to be stuck in a C mindset.)
Side note: a more useful getFileSize would restore the previous read position and not assume that it was at the beginning:
std::streamsize getFileSize(std::fstream &file) const
{
std::streamsize old_pos = file.tellg();
file.seekg (0, std::ios::end);
std::streamsize fsize = file.tellg();
file.seekg (old_pos, std::ios::beg); // moves file pointer back to the previous position
return fsize;
}

This should help you:
Message::Message(std::string filename)
{
std::ifstream infile;
infile.open(filename.c_str(), std::ifstream::in);
// read contents & initialize other member variables.
if (infile.is_open()) {
// getFileSize and allocate so much space for message
length = getFileSize(infile);
message = new char[length + 1];
//copy contents from file to message
}
// close the file finally
infile.close()
}
EDIT for usage
int main () {
Message m("Encrypted.txt");
// other operations on m
return 0;
}

Related

How can I add a zero at the end of a string?

I'm trying to read some text out of a file called "file.dat". The problem is, that the string in the file does not include a zero at the end as for standard C. So I need something that adds the zero, so I can work with the string without getting random symbols after the string when I print it.
void cSpectrum::readSpectrum(const std::string &filename, double
tubeVoltage, double &minEnergy, std::string &spectrumName)
{
//Object with the name "inp" of the class ifstream
ifstream inp(filename, ios::binary);
//Checks if the file is open
if (!inp.is_open()) {
throw runtime_error("File not open!");
}
cout << "I opened the file!" << endl;
//Check the title of the file
string title;
char *buffer = new char[14];
inp.read(buffer, 14);
cout << buffer << endl;
}
At the moment I get the following output, I would like to get it without the ²²²²┘.
I opened the file!
x-ray spectrum²²²²┘
Simply allocate +1 more char for your array, but don't read into that char, just set it to 0:
char buffer[15];
inp.read(buffer, 14);
buffer[14] = '\0';
cout << buffer << endl;
Or, simply don't use a char[] at all, use std::string instead, see:
What is the best way to read an entire file into a std::string in C++?
I did it with the std::string now. If you want you can replace the 14 by an integer variable.
void cSpectrum::readSpectrum(const std::string & filename, double tubeVoltage, double
& minEnergy, std::string const & spectrumName){
ifstream inp(filename, ios::binary);
//Checks if the file is open
if (!inp.is_open()) {
throw runtime_error("ERROR: Could not open the file!");
}
//Reads the title
string title(14, '\0');
inp.read(&title[0], 14);
//If it is not the correct file throw an ERROR
if (title != spectrumName)
throw runtime_error("ERROR: Wrong file title");
readSpectrum(inp, tubeVoltage, minEnergy, spectrumName);
}

How can I store a string(from a file with n number of lines) in a dynamic array? C++

Newb here... taking a C++ class on data structures. I am making a program that takes a list of chores from a text file and stores them in a dynamic array.
//In header/ In class:
private:
/* var to keep len of list */
int len = 99; // Not sure what to set this to or if I need to even set it.
/* add appropriate data structure to store list */
string *arr = new string[len];
//In .cpp:
ListOfChores::ListOfChores(string fileName) {
ifstream file(fileName, ifstream::in);
string line;
if (file.is_open()) //Checking if the file can be opened
{
while (!file.eof()) // To get all the lines.
{
getline(file, line); // Gets a single line
arr[len] = line; // Store a line in the array
len++; // Increases the array size by one
}
file.close(); // Closes file
}
else cout << "Unable to open file" << endl; // Gives error if the file can't be opened
}
But I am getting an error for storing a line in the array. It says "Access violation reading location." There is another function executed in the main.cpp for printing the lines.
You overrun your array buffer at once because len is already 99. You should have a notion of capacity and length. Capacity is the maximum you can store without reallocating, and length is the actual number of data lines.
Please avoid this C-style array in C++ code. Use vector, which has been around for at least 20 years (STL) if I'm not mistaken.
(you're not a lost cause, you are already using std::string :))
Check this:
#include <vector>
//In header/ In class:
private:
/* add appropriate data structure to store list */
std::vector<string> arr; // define a vector
//In .cpp:
ListOfChores::ListOfChores(string fileName) {
ifstream file(fileName, ifstream::in);
string line;
if (file.is_open()) //Checking if the file can be opened
{
while (getline(file, line))
{
arr.push_back(line);
}
file.close(); // Closes file
}
else cout << "Unable to open file" << endl; // Gives error if the file can't be opened
}
Now arr.size() holds the number of lines, it is no longer limited to 99 lines but to the max. program memory capacity. You can still access line number 13 by arr[12] or arr.at(12) for boundary checked access.
proper way to iterate through it (C++11) for instance to print all lines:
for (auto s : arr)
{
std::cout << s << std::endl;
}
Now, if you REALLY have to use an array, you can emulate/mimic what vector does (well, not as performant I'm sure, but does the job):
private:
int len=0;
int capacity=100;
string *arr = new string[capacity];
now in the code, just before inserting (untested, but the idea is right):
if (len>=capacity)
{
string *narr = new string[capacity+100];
for (int i = 0; i < capacity; i++)
{
narr[i] = arr[i];
}
delete [] arr;
arr = narr;
capacity += 100; // growth
}
(you cannot use realloc or memcpy because you're handling objects in the arrays)

String manipulation and File IO in C++?

What I am trying to do is open a file containing a string, replace every character in that file with [character + 37], and output it on a different file "output.txt". What I'm guessing is a problem with the at function...
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
void encrypt(string text, int size) {
int i;
for (i = 0; i < size; i++) {
text.at(i) = text.at(i) + 37;
}
}
int main()
{
string string;
int length = string.length();
ifstream infile;
infile.open("input.txt");
if (infile.fail()) {
cerr << "Error Opening File. " << endl;
exit(1);
}
infile >> string;
infile.close();
encrypt(string, length);
ofstream outfile;
outfile.open("output.txt");
if (infile.fail()) {
cerr << "Error Opening File. " << endl;
exit(1);
}
outfile << string;
outfile.close();
return 0;
}
First thing, you should change your encrypt function string parameter to reference type:
void encrypt(string& text, int size) {
otherwise string text is a local variable in this function, and any changes will be lost after it ends.
Another thing is that you dont need int size, use text.size() instead. Also I see you are passing as size, result of this code:
string string;
int length = string.length();
here length will always be zero, also - as you can see your variable name is the same as the type std::string, why you use such name? This is one of the reason you should not use using namespace std;
Third thing, after :
ofstream outfile;
outfile.open("output.txt");
you check if it is in fail state by using infile instead of outfile
if (infile.fail()) {
better change this check to if (!infile) {
As said by #marcinj, your encrypt routine only modifies a copy of original string, and the copy is lost when function exists.
But anyway, what you do is rather dangerous: your encrypt function can take printable characters and transform then into non printable ones while you use the text interface of streams. Even if it works, the file might not contain a correct string when done.
For example the character ù (which is common in french) will become a space which is the string delimiter, and even worse, the õ (common in portuguese) will become a \n not speaking of the Û that will give a null character!

Reading a single character from a file returns special characters?

Using fstreams I'm attempting to read single characters from a specified location in a file and append them onto a string. For some reason, reading in these characters returns special characters. I've tried numerous things, but the more curious thing that I found while debugging was that changing the initial value of the char temp; will cause the whole string to change to that value.
int Class::numbers(int number, string& buffer) {
char temp;
if (number < 0 || buffer.length() > size) {
exit(0);
}
string fname = name + ".txt";
int start = number * size;
ifstream readin(fname.c_str());
readin.open(fname.c_str(), ios::in)
readin.seekg(start);
for (int i = 0; i < size; ++i) {
readin.get(temp);
buffer += temp;
}
cout << buffer << endl;
readin.close();
return 0;
}
Here is an example screenshot of the special characters being outputted: http://i.imgur.com/6HCI7TT.png
Could the issue be where I'm starting using seekg? It seems to start in the appropriate position. Another thing I've considered is that maybe I'm reading some invalid place into the stream and it's just giving me junk characters from memory.
Any thoughts?
WORKING SOLUTION:
int Class::numbers(int number, string& buffer) {
char temp;
if (number < 0 || buffer.length() > size) {
exit(0);
}
string fname = name + ".txt";
int start = number * size;
ifstream readin(fname.c_str());
readin.open(fname.c_str(), ios::in)
readin.seekg(start);
for (int i = 0; i < size; ++i) {
readin.get(temp);
buffer += temp;
}
cout << buffer << endl;
readin.close();
return 0;
}
Here is the working solution. In my program I had already had this file name open, so opening it twice was likely to cause issues I suppose. I will do some further testing on this in my own time.
For ASCII characters with a numeric value greater than 127, the actual character rendered on screen depends on the code page of the system you are currently using.
What is likely happening is that you are not getting a single "character" as you think you are.
First, to debug this, use your existing code to just open and print out an entire text file. Is your program capable of doing this? If not, it's likely that the "text" file you are opening isn't using ASCII, but possibly UTF or some other form of encoding. That means when you read a "character" (8-bits most likely), you're just reading half of a 16-bit "wide character", and the result is meaningless to you.
For example, the gedit application will automatically render "Hello World" on screen as I'd expect, regardless of character encoding. However, in a hex editor, a UTF8 encoded file looks like:
UTF8 Raw text:
0000000: 4865 6c6c 6f20 776f 726c 642e 0a Hello world..
While UTF16 looks like:
0000000: fffe 4800 6500 6c00 6c00 6f00 2000 7700 ..H.e.l.l.o. .w.
0000010: 6f00 7200 6c00 6400 2e00 0a00 o.r.l.d.....
This is what your program sees. C/C++ expect ASCII encoding by default. If you want to handle other encodings, it's up to your program to accomodate it manually or by using a third-party library.
Also, you aren't testing to see if you've exceeded the length of the file. You could just be grabbing random garbage.
Using a simple text file just containing the string "Hello World", can your program do this:
Code Listing
// read a file into memory
#include <iostream> // std::cout
#include <fstream> // std::ifstream
#include <string.h>
int main () {
std::ifstream is ("test.txt", std::ifstream::binary);
if (is) {
// get length of file:
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
// allocate memory:
char * buffer = new char [length];
// read data as a block:
is.read (buffer,length);
// print content:
std::cout.write (buffer,length);
std::cout << std::endl;
// repeat at arbitrary locations:
for (int i = 0; i < length; i++ )
{
memset(buffer, 0x00, length);
is.seekg (i, is.beg);
is.read(buffer, length-i);
// print content:
std::cout.write (buffer,length);
std::cout << std::endl;
}
is.close();
delete[] buffer;
}
return 0;
}
Sample Output
Hello World
Hello World
ello World
llo World
lo World
o World
World
World
orld
rld
ld
d

Fixing syntax of number of line reading function

I tried making a program earlier that tells the user then number of char, words, and lines in a text file. I made functions to determine the numbers of each, yet I was passing them by value. This resulted in an error since after reading the number of char it would be at the end of the file and then output zero for the other two. Now I cant seem to rewrite my functions so that the file is open and closed each time its checked for char, words, and lines. Any one see where my errors are?? Thanks! (just copied and pasted one of my functions for now).
int num_of_lines(ifstream file)
{
string myfile;
myfile = argv[1];
ifstream l;
l.open(myfile);
int cnt3 = 0;
string str;
while(getline(file, str))cnt3++;
l.close();
return(cnt3);
}
int main(int argc, char **argv)
{
int num_of_char(ifstream file);
string file;
file = argv[1];
if(argc == 1)die("usage: mywc your_file");
ifstream ifs;
ifs.open(file);
if(ifs.is_open())
{
int a, b, c;
a = num_of_lines(ifs);
cout <<"Lines: " << a << endl;
}
else
{
cerr <<"Could not open: " << file << endl;
exit(1);
}
ifs.close();
return(0);
}
There is no way to "reopen" a file other than knowing the name and creating a new ifstream, but you can use the seekg member function to set your read position in the file, and setting it to 0 will have the next read operation start from the beginning of the file.
A stream is not possible to copy, so you can't pass it "by value", but must pass it by reference.
int num_of_lines(ifstream &file)
{
int count = 0;
string str;
while (getline(file, str)) {
count++;
}
file.seekg(0);
return count;
}
For the full problem, I agree with Mats Petersson, though. Counting both characters, lines and words in one pass will be much more efficient than reading through the file three times.