I am making a file reading class. It should, when constructed open the file with the given string and depending on which constructor is called use the second string supplied to skip through the file to the line after the string given.
Here is my code as it stands:
SnakeFileReader::SnakeFileReader(string filePath)
{
fileToRead_.open(filePath.c_str(), ios::in);
}
SnakeFileReader::SnakeFileReader(string filePath, string startString)
{
fileToRead_.open(filePath.c_str(), ios::in);
string toFind;
while (toFind != startString && !fileToRead_.eof())
{
fileToRead_ >> toFind;
}
}
string SnakeFileReader::ReadLine()
{
string fileLine;
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> fileLine;
return fileLine;
}
int SnakeFileReader::ReadInt()
{
string fileLine = "";
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> fileLine;
int ret;
istringstream(fileLine) >> ret;
return ret;
}
SnakeFileReader::~SnakeFileReader()
{
fileToRead_.close();
}
The problem I have is that in the second constructor I get a segmentation fault. I get another segmentation fault in the read line function as soon as I declare a string.
[Edit] Here is the extra code requested. I am making a "Snake Game" as a part of the first year of my degree. I want the game to read and save files rather than hard code variable values. I will finally be using this class a lot to setup a level in the game. However here are a few lines that should demonstrate how i intend to use this class:
//Level.cpp
std::string fileToRead = "resources/files/level1.txt";
SnakeFileReader sfr(fileToRead);
std::string mapFilePath = sfr.ReadLine();
ImageFile(mapFilePath).load(map_layout);
mapWidth_ = sfr.ReadInt();
mapHeight_ = sfr.ReadInt();
level_cell_size_ = sfr.ReadInt();
map_ = new TileData*[mapWidth_];
for (int i = 0; i < mapWidth_; i++)
{
map_[i] = new TileData[mapHeight_];
}
Layout of the file:
resources/images/Map1_Layout.bmp
40
30
20
Class declaration:
#ifndef SNAKE_FILE_READER_HPP
#define SNAKE_FILE_READER_HPP
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
class SnakeFileReader
{
public:
SnakeFileReader(string filePath);
SnakeFileReader(string filePath, string startString);
~SnakeFileReader();
string ReadLine();
int ReadInt();
private:
ifstream fileToRead_;
};
#endif // SNAKE_FILE_READER_HPP
in the ReadLine function, you return a reference to a variable allocated on the functions stack. you are corrupting the stack, crazy things can happen. your compiler should have warned you about that.
I'm not sure about your constructor, but the problem with ReadLine() is that you're trying to return the memory address of an automatic variable, which is destroyed when you exit the function.
The simplest fix would be to remove the '&' on the return value, and just return a string. But if you're determined to return a memory address, try this instead:
string *SnakeFileReader::ReadLine()
{
string *fileLine = new string;
if (!fileToRead_.fail() && !fileToRead_.eof())
fileToRead_ >> *fileLine;
return fileLine;
}
This will dynamically allocate the string and pass back the pointer. The difference is that dynamic variables are not automatically destroyed when you leave their scope. The string will still exist on the heap until you delete it yourself (which you must remember to do when you're done with it).
Related
I've a task to copy elements from .txt file[direct access file] to .bin file[fixed length record file] (homework).
.txt file holds strings. Every line has one word.
I came up with code below, but I'm not sure if that's what is needed and even slighly correct. Any help will be useful! (I'm new to C++)
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int buffer_size = 30;
class Word{
char name[buffer_size];
public:
void setName () // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open ("text.txt");
while(!infile.eof()) // To get you all the lines.
{
getline(infile,STRING); // Saves the line in STRING.
}
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
Word obj;
obj.setName();
outFile.write((char*)&obj, sizeof(obj));
outFile.close();
}
int main()
{
write_record();
return 0;
}
NEW APPROACH:
class Word
{
char name[buffer_size];
public:
Word(string = "");
void setName ( string );
string getName() const;
};
void readWriteToFile(){
// Read .txt file content and write into .bin file
string temp;
Word object;
ofstream outFile("out.dat", ios::binary);
fstream fin ("text.txt", ios::in);
getline(fin, temp);
while(fin)
{
object.setName(temp);
outFile.write( reinterpret_cast< const char* >( &object ),sizeof(Word) );
getline(fin, temp);
}
fin.close();
outFile.close();
}
int main()
{
readWriteToFile();
return 0;
}
Word::Word(string nameValue)
{
setName(nameValue);
}
void Word::setName( string nameString )
{
// Max 30 char copy
const char *nameValue = nameString.data();
int len = strlen(nameValue);
len = ( len < 31 ? len : 30);
strncpy(name, nameValue, len);
name[len] = '\0';
}
string Word::getName() const
{
return name;
}
Quick commentary and walk through
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Avoid using namespace std; while you are learning. It can lead to some really nasty, hard to pin-down bugs as your functions may be silently replaced by functions with the same name in the standard library.
const int buffer_size = 30;
class Word
{
char name[buffer_size];
Since it looks like you are allowed to use std::string why not use it here?
public:
void setName() // Trying to get every word from a line
Really bad name for a function that apparently is supposed to // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open("text.txt");
while (!infile.eof()) // To get you all the lines.
{
getline(infile, STRING); // Saves the line in STRING.
}
Few things wrong here. One is the epic Why is iostream::eof inside a loop condition considered wrong?
Next is while the code reads each line, it doesn't do anything with the line. STRING is never stored anywhere.
Finally in a class that sounds as though it should contain and manage a single word, it reads all the words in the file. There may be a case for turning this function into a static factory that churns out a std::vector of Words.
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
ios::app will add onto an existing file. This doesn't sound like what was described in the assignment description.
Word obj;
obj.setName();
We've already coverred the failings of the Word class.
outFile.write((char*) &obj, sizeof(obj));
Squirting an object into a stream without defining a data protocol or using any serialization is dangerous. It makes the file non-portable. You will find that some classes, vector and string prominent among these, do not contain their data. Writing a string to a file may get you nothing more than a count and an address that is almost certainly not valid when the file is loaded.
In this case all the object contains is an array of characters and that should write to file cleanly, but it will always write exactly 30 bytes and that may not be what you want.
outFile.close();
}
int main()
{
write_record();
return 0;
}
Since this is homework I'm not writing this sucker for you, but here are a few suggestions:
Read file line by line will get you started on the file reader. Your case is simpler because there is only one word on each line. Your teacher may throw a curveball and add more stuff onto a line, so you may want to test for that.
Read the words from the file into a std::vector. vector will make your job so easy that you might have time for other homework.
A very simplistic implementation is:
std::vector<std::string> words;
while (getline(infile, STRING)) // To get you all the lines.
{
words.push_back(STRING);
}
For writing the file back out in binary, I suggest going Pascal style. First write the length of the string in binary. Use a known, fixed width unsigned integer (no such thing as a negative string) and watch out for endian. Once the length is written, write only the number of characters you need to write.
Ignoring endian, you should have something like this:
uint32_t length = word.length(); // length will always be 32 bits
out.write((char*)&length, sizeof(length));
out.write(word.c_str(), length);
When you are done writing the writer, write a reader function so that you can test that the writer works correctly. Always test your code, and I recommend not writing anything until you know how you'll test it. Very often coming at a program from the test side first will find problems before they even have a chance to start.
I am having a bit of an issue with a current task of mine. Basically, I am given an XML file and am trying to parse it for key information. For example, some lines will be like this:
<IPAddress>123.45.67</IPAddress>
And I am to get the value of 123.45.67, nothing too bad at all. I was told not to use a XML parser and just parse it manually, which was pretty easy. However, I am having issues with the second part of the task. Basically, I am to make a class with certain member variables and declare them based on the values I parse. So let's say the class is called Something and there is a member variable called IPAddress. I am to then update the value of IPAddress to 123.45.67 so when someone calls Something.IPAddress in the main method, it returns 123.45.67. This was my initial attempt at it:
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
using namespace std;
class Something
{
public:
string location;
string IPAddress;
string theName;
int aValue;
//loop through the array from the method below
void fillContent(string* array)
{
for(int i = 0; i < array->size(); i++)
{
string line = array[i];
if((line.find("<") != std::string::npos) && (line.find(">")!= std::string::npos))
{
unsigned first = line.find("<");
unsigned last = line.find(">");
string strNew = line.substr (first + 1, last - first - 1); //this line will get the key, in this case, "IPAddress"
unsigned newfirst = line.find(">");
unsigned newlast = line.find_last_of("<");
string strNew2 = line.substr(newfirst + 1, newlast - newfirst - 1); //this line will get the value, in this case, "123.45.67"
if(strNew == "IPAddress")
{
IPAddress = strNew2; //set the member variable to the IP Address
}
}
}
}
//this method will create an array where each element is a line from the xml
void fillVariables()
{
string line;
ifstream myfile ("content.xml");
long num = //function that gets size that I didn't add to make code shorter!;
string *myArray;
myArray = new string[num];
string str1 = "";
string strNew2 = "";
int counter = 0;
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
myArray[counter] = line;
counter++;
}
myfile.close();
}
fillContent(myArray);
}
};
int main(int argc, char* argv[])
{
Something local;
local.fillVariables();
cout << local.IPAddress<< endl; // should return "123.45.67"
return 0;
}
Now this does do what I want it to do, however, you can see I need the if statement. Assuming I have at least 20 of these member variables, having 20 if-statements would be annoying and just frowned upon. Is there any other way I could somehow access the member variables from the class? Sorry if my question was long, I just wanted to make sure everything that is needed to understand the question is provided! Please let me know if anything crucial that may not be there should be added.
Thanks a lot!
This may be considered bad style, but I usually just do:
// at the top of the 'fillContent' function
std::map<string, string*> varmap{
{"IPAddress", &IPAddress},
{"AnotherField", &AnotherField}
};
// If you're not using C++11, you can also try:
// std::map<string, string*> varmap;
// varmap["IPAddress"] = &IPAddress;
// varmap["AnotherField"] = &AnotherField;
// parsing code goes here
*varmap[strNew] = strNew2;
I'm making a virtual machine in C++. I have loaded in the contents of a file as a string. I pass this string to a function of type int*, but the problem is the string variable containing the contents of the file seems to be empty because when I try to use cout << file << endl; I get nothing.
Here is the file in question:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
class reedoovm {
private:
string filedata;
string instruction;
string file;
int instr;
int instructionCount;
int instructionPointer;
public:
int load_program(string filename) {
ifstream rdfile(filename);
while(rdfile >> instruction) { /* Get each instruction */
filedata += instruction; /* Append the instruction to filedata */
filedata += ","; /* Append a comma to separate each instruction */
instructionCount++;
}
rdfile.close(); /* Close the file */
return instructionCount; /* Return the filedata */
}
int *instrToArr(string file) {
//file = "02,0022,00E1,0022,00,04,73";
cout << file << endl;
stringstream hextoint;
unsigned int value;
string s = file; /* store fconv in a variable "s" */
string delimiter = ","; /* The delimiter */
size_t pos = 0;
string token;
int i = 0;
int inst;
static int* instarray;
instarray = (int*) calloc(instructionCount,sizeof(int));
while ((pos = s.find(delimiter)) != string::npos) { /* Convert hex instructions to decimal */
token = s.substr(0, pos);
stringstream hextoint(token);
hextoint >> hex >> value;
if (i < instructionCount) {
instarray[i] = value;
cout << instarray[i] << endl;
i++;
}
s.erase(0, pos + delimiter.length());
}
return instarray;
}
int getNextIntruction(string s) {
int *instruction = instrToArr(s);
cout << *instruction << endl;
return 0;
}
void run_program(string s) {
int loop = 1;
while (loop) {
instr = getNextIntruction(s);
loop = 0;
}
}
void execute_program(string s) {
file = load_program(s);
int * arr = instrToArr(file);
//cout << arr << endl;
//run_program(s);
}
};
int main(int argc, char* argv[]) {
reedoovm rd;
rd.execute_program(argv[1]);
return 0;
}
The function causing the problem is int *instrToArr(string file) {. I don't know why all of a sudden the file variable is empty.
Your code has many issues, but the one that is bugging you is probably
file = loadProgram(s);
because loadProgram has been defined as returning an integer (the number of instructions) and not a string, but you're assigning it to a string.
For what I'd call a design bug of C++ assigning an integer to a string is a perfectly legal instruction and means that the string will have one character with the value of the integer.
Officially the reason for accepting assignment from an integers is that it was thought that it could be useful to write
str += chr; // Adds the char at the end
where str is a string and chr a char. By extension if += was legal then it was thought that also assignment should be legal too (a logical jump I don't agree with in this specific case).
chars however in C++ are numbers and integers (or even doubles) can be converted implicitly to a char without any warning or any error. So it's for example also legal:
std::string s;
s = 3.141592654;
Other issues I can see in your code are:
1. instructionCount is not initialized
In C++ you must always initialize native type members (e.g. integers, doubles) in class instances in the constructor. The default constructor won't do it for you. The result is that when allocating the class instance those members will have random values and you don't want that. Official explanation for this rule is that initializing members that won't be access may penalize performance, if the programmer wants to pay for initialization then it has to write the initialization.
2. instrToArr returns a pointer to a local static variable
That variable that is however allocated each time the function is called thus leaking memory at each call if the caller doesn't take care of deallocation.
Note that in C++ writing:
static int * instarray = (int *)calloc(...);
is not the same as writing:
static int * instarray;
instarray = (int *)calloc(...);
because in the first case the allocation is done only once (the first time the code reaches that instruction) while in the second case the allocation is done every time.
3. You are using calloc
Your code is allocation a variable-sized array using calloc and this, while not a bad idea in absolute, requires very careful handling to avoid leaks or other errors (for example memory allocated with calloc must be freed with free and not with delete[] but the compiler cannot help the programmer remembering what was allocated with one or with the other method (new[]).
MUCH better unless there are very specific reasons to play with naked pointers and implicit sizes is to use std::vector for variable-sized arrays.
4. You seem to want hex -> int conversion
... but your code does nothing to do it. Unfortunately input parsing is a sad story in C++ and I, as one, prefer to use old c <stdio.h> functions for input and especially for output (where formatting in C++ is just too painful).
5. your getNextInstruction always returns 0
Nothing remains of the processing of instrToArr and also the array returned is just dropped on the floor after sending the address on output.
This means just leaking memory at every iteration.
6. your run_program just loops once
... thus at least the naming is confusing (there are no real loops).
7. your program doesn't do any kind of checking in main
If someone calls the program passing no arguments (a quite common case) then something bad is going to happen.
I think in load_program() instead of:
return instructionCount;
you meant:
return filedata;
And change the return type of load_program() to string
I suppose you have a typo
int * arr = instrToArr(file)
instead of
int * arr = instrToArr(filedata)
I'm practicing C++ and have made a class that stores sequences read in from fast format as well as their names. The code is below:
#include<fstream>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Sequence {
vector<string> fullSequence, sequenceNames;
public:
void fastaRead(string fileName);
string getSequence(int index);
};
string Sequence::getSequence(int index)
{
return fullSequence[index];
}
void Sequence::fastaRead(string fileName)
{
vector<string> fullSequence, sequenceNames;
ifstream inputFile;
inputFile.open(fileName);
if (inputFile.is_open()) {
string currentSeq;
string line;
bool newseq = false;
while (getline(inputFile, line))
{
if (line[0] == '>') {
sequenceNames.push_back(line.substr(1,line.size()));
newseq = true;
} else {
if (newseq == true) {
fullSequence.push_back(currentSeq);
currentSeq = line;
newseq = false;
} else {
currentSeq.append(line);
}
}
}
}
inputFile.close();
}
int main()
{
Sequence inseq;
cout << "Fasta Sequence Filepath" << endl;
string input;
getline(cin, input);
inseq.fastaRead(input);
inseq.getSequence(0);
return 0;
}
However when I run the program with the following dummy input file:
>FirstSeq
AAAAAAAAAAAAAA
BBBBBBBBBBBBBB
>SecondSeq
TTTTTTTTTTTTTT
>ThirdSequence
CCCCCCCCCCCCCC
>FourthSequence
GGGGGGGGGGGGGG
I get a segmentation fault when the line inset.getSequence(0) is called. What is it I've done that causes the seg fault and how do I make sure it doesn't happen? I know it can have something to do with errors in pointers, but I don't think I've used pointers which if I remember correctly requires the * character.
Thanks,
Ben.
You need to remove vector<string> fullSequence, sequenceNames; in the void Sequence::fastaRead function. When you define those variables inside that function and use them, you are not accessing the ones in the class that have the same name, you are accessing the local variables that you have defined in that function, unless you prepend them with this-> while accessing.
The variables in the class are actually empty and you get a segmentation fault.
I am trying to read the lines from a file called 'weapon.txt' and input them into a structure something a long the lines of this
struct weapon
{
char name[20]; //Edited
int strength;
}
The file to be read looks like this:
Excalibur
150
Throwing Stars
15
Rapier
200
Bow and Arrow
100
Axe
200
Crossbow
100
Scimitar
250
Rusted Sword
10
Soul Slayer
500
The code I have right now is
#include<fstream>
#include<iostream>
#include<cstring>
using namespace std;
struct WeaponInfo
{
char name[16];
int strength;
};
const int MaxWeap = 10;
void openfile(ifstream&); //Opening the file
void displayfile(ifstream&, WeaponInfo&);//Display file
int main ()
{
WeaponInfo weapon[MaxWeap];
ifstream fin;
openfile(fin);
displayfile(fin, weapon[MaxWeap]);
}
void openfile(ifstream& fin)
{
fin.open("weapon.txt");
}
void displayfile(ifstream& fin, WeaponInfo& weapon[MaxWeap])
{
char nm;
int str;
while (fin.eof() == 0)
{
for(int i = 0; i <= MaxWeap; i++);
{
fin.getline(nm);
fin.getline(str);
strcpy(weapon[i].name, nm);
strcpy(weapon[i].strength, str);
i++;
cout << weapon[i].name << "\n" << weapon[i].strength << endl;
}
}
fin.close();
}
EDIT: This is what I have right now after re-doing it, I am getting compile errors of : declaration of 'weapon' as array of references; In function 'void displayfile(...) 'fin' was not declared in this scope; 'weapon' is not declared in this scope; ma,e lookup of 'i' changed for ISO 'for' scoping [-fpermissive].
I'd firstly tend to use std::string rather than char arrays - they're just easier to work with. So the structure noww looks like this:
struct weapon
{
string name;
int strength;
};
Next you need something that will read the structure from an input stream:
bool getWeapon( ifstream& is, weapon& w )
{
getline(is, w.name) ;
string strengthStr;
getline(is, strengthStr) ;
w.strength = strtol( strengthStr.c_str(), NULL, 0 );
return !is.eof();
}
Two things here, I've used strtol as a conversion function from string to int. atoi is used but strtol gives you slightly more flexibility and crucially, better error cchecking, alkthough I've not bothered to implement it here. A stringstream might have been another alternative here.
Secondly, I return a boolean indicating whether the name was empty. The reason for this is that when, later in the code, I check for eof() on the ifstream, it isn't actually set until you read past the end of the file. So the last good read will not set it but the first attempt to reead past it will. Returning false here then will indicate to the caller that the 'get' failed due to the ifstream being at end of file.
Lastly, we need something to read all of the weappons in:
ifstream input;
input.open("weapons.txt");
vector<weapon> ws;
if ( input )
{
while (! (input.eof()))
{
weapon w;
if ( ! getWeapon( input, w ) )
break;
ws.push_back( w );
}
}
input.close();
This wwill place all the weapons into a vector. Note the call to getWeapon breaks if it failed to prrevent adding on an 'empty' weapon. Not the most glamorous solution but it should work.
Pseudo-code goes something like this, (and like Martol1ni has coded for you):
open the file
while (!end-of file)
{
create instance of struct weapon
read a line and strcpy into weapon.name
read a line and set weapon.strength = atoi(line)
do something with the instance, eg. add to list, call a member function, etc.
}
loop
close file.
Assuming you control the weapons.txt, don't bother checking for errors in the file, you can do this. Next time, do a little research... :)
#include <fstream>
#include <vector>
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;
struct weapon
{
string name;
int strength;
weapon(string n, int s) : name(n), strength(s) {}
};
void readFileToVec(vector<weapon> &myVec) {
ifstream in("weapon.txt");
while (!in.eof()) {
string name;
getline(in,name);
string strength;
getline(in,strength);
weapon myWep(name,atoi(strength.c_str()));
myVec.push_back(myWep);
}
in.close();
}