Inputting a file into a structure - c++

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();
}

Related

How to sort struct members under the same array index?

I am working on a C++ program, but I discovered that a function for sorting through arrayed struct members transferred from a text file didn't execute and ended up displaying the unsorted struct members.
This program is intended for my university course's semester project, where I made a basic ride-sharing program based on C++. The program must read the text file containing driver information and transfer it to arrayed structs, where it will then begin sorting from the lowest to highest price and display the sorted struct members. I did some research on a C++ textbook and even went on the a few forums to find similar problems, but I kept getting the same results as the text file was originally.
Here is the content of the text file for reference.
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
Riley Winston
0174965739
Ford Everest
70
2.50
Here is my coding
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string.h>
using namespace std;
struct ProSort
{
char nameProvider[10][40]; //40 character limit for nameProvider
char numPhoneProvider[10][11]; //11 character limit for numPhoneProvider
char nameVehicle[10][40]; //40 character limit for nameVehicle
double KMh[10];
double price[10];
};
ProSort sortingS[7]; //7 set of structs, but I'll put one of the said struct in the sorting function as an example below.
void sortS(ProSort, int);
void share_ride_sort_input(ProSort sortingS[], fstream& File)
{
File.open("sList/s4-Wheels.txt", ios::in);
{
if (File.is_open())
{
int a = 0;
while (!File.eof())
{
File >> ws;
File.getline(sortingS[0].nameProvider[a], 40);
File >> ws;
File.getline(sortingS[0].numPhoneProvider[a], 11);
File >> ws;
File.getline(sortingS[0].nameVehicle[a], 40);
File >> sortingS[0].KMh[a];
File >> sortingS[0].price[a];
//Contents of the text file will be assigned to the struct members above
a++; //Array index number will increase until the end of the text file
}
}
}
File.close();
}
void sortS(ProSort sortingS, int SIZE) //The sorting function for said issue above
{
int index;
int smallestIndex;
int location;
char temp[100];
double temp2;
for (index = 0; index < SIZE - 1; index++)
{
smallestIndex = index;
for (location = index + 1; location < SIZE; location++)
{
if (sortingS.price[index] > sortingS.price[smallestIndex])
{
smallestIndex = location;
strcpy(temp, sortingS.nameProvider[smallestIndex]);
strcpy(sortingS.nameProvider[smallestIndex], sortingS.nameProvider[index]);
strcpy(sortingS.nameProvider[index], temp);
strcpy(temp, sortingS.numPhoneProvider[smallestIndex]);
strcpy(sortingS.numPhoneProvider[smallestIndex], sortingS.numPhoneProvider[index]);
strcpy(sortingS.numPhoneProvider[index], temp);
strcpy(temp, sortingS.nameVehicle[smallestIndex]);
strcpy(sortingS.nameVehicle[smallestIndex], sortingS.nameVehicle[index]);
strcpy(sortingS.nameVehicle[index], temp);
temp2=sortingS.KMh[smallestIndex];
sortingS.KMh[smallestIndex]=sortingS.KMh[index];
sortingS.KMh[index]=temp2;
temp2=sortingS.price[smallestIndex];
sortingS.price[smallestIndex]=sortingS.price[index];
sortingS.price[index]=temp2;
// Basically all of the arrayed struct members with the same array index will move together as one whole set of driver info until every set of struct members is sorted
}
}
}
}
void share_ride_output(ProSort sortingS[], fstream& File) //Function for displaying the sorted struct members by writing to a text file.
{
File.open("sList/s4-Wheels-sorted.txt", ios::out);
{
if (File.is_open())
{
for(int i=0; i<2; i++)
{
File<<sortingS[0].nameProvider[i]<<endl;
File<<sortingS[0].numPhoneProvider[i]<<endl;
File<<sortingS[0].nameVehicle[i]<<endl;
File<<sortingS[0].KMh[i]<<" km/h"<<endl;
File<<"£"<<sortingS[0].charge[i]<<endl;
File<<"\n";
} //This is for writing 2 sets of struct members that was assigned in the share_ride_sort_input function to another text file.
}
}
File.close();
}
int main()
{
fstream File;
const int SIZE = 7;
share_ride_sort_input(sortingS, File);
for(int i=0; i<7; i++) //Originally this was meant for 7 car classes, but only the struct members from the s4-wheels.txt file will be put as an example
{
sortS(sortingS[i], SIZE);
}
share_ride_output(sortingS, File); //Sorted struct members will be written to a text file.
return 0;
}
I expect the output to the text file to be:
Riley Winston
0174965739
Ford Everest
70
2.50
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
But instead, I got the output to be unsorted like this:
Annie Aliston
0174987723
Range Rover Evoque
60
6.00
Riley Winston
0174965739
Ford Everest
70
2.50
No error message is displayed, as the program runs without any warnings from the compiler. I would assume that I did something wrong in the sorting formula, but I couldn't seem to get other solutions to work either.
Main problem with your code is that actually it is not a C++. It is mostly C which is much harder to handle.
Second problem as someone has point out in comment, you reversed task hint. Instead doing array of structs you created an arrays inside a struct, which in this case made things even harder.
When you write C++ code, don't use C features like: char[] for strings (use std::string), C arrays SomeType variable[number] (use std::vector or std::array).
Start with something like that and the use std::sort and it will turn out quite easy:
struct Ride {
std::string dirver;
std::string phone;
std::string vehicle;
double distance;
double price;
};
std::istream& loadRide(std::istream& input, Ride& ride)
{
input >> std::ws; // consume white spaces in front
std::getline(input, ride.dirver);
std::getline(input, ride.phone);
std::getline(input, ride.vehicle);
return input >> ride.distance >> price;
}
std::istream& loadRides(std::istream& input, std::vector<Ride>& rides)
{
rides.clear();
Ride ride;
while(loadRide(input, ride)) {
rides.push_back(ride);
}
}
std::vector<Ride> loadRidesFromFile(const std::string& fileName)
{
std::ifstream f{ fileName };
std::vector<Ride> rides;
loadRides(f, rides);
return rides;
}

C++ From text file to Binary file

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.

File Reading Class C++

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).

Error when closing file stream whose existence depends on the same bool as the .close() statement in C++

I'm having trouble wording this question (as you might see from the title), but I'll try:
I'm opening a file stream (ofstream) if a bool variable is true, and then trying to close the file stream again if the same bool is true gives the error
'fout' was not declared in this scope"
Here is a minimal example:
using namespace std;
#include <fstream>
int main(int argc, char * argv[]) {
bool output = true;
if (output) {
ofstream fout("test.txt");
}
if (output) {
fout.close();
}
} // end main
What I'm trying to do is to open a file for storing some results from a program, only if the boolean "output" is true. I could just let it open the file every time (no if tests), but that gets very messy after a while, because the real file name depends on variables set in the program, and I don't want to overwrite any old files, so I'd rather do it this way.
EDIT: Today I learned what a "scope" is... Thanks guys!
Working code goes like this:
using namespace std;
#include <fstream>
int main(int argc, char * argv[]) {
bool output = true;
ofstream fout;
if (output) {
fout.open("test.txt");
}
if (output) {
fout.close();
}
} // end main
You declare fout inside the scope of the if block, the else block has separate scope with it's own automatic variable of which fout is not one.
Yet you don't actually need to worry about it here, the ofstream will close itself when it falls out of scope because it abides by RAII.
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
I fact letting it close it's self is preferable to doing yourself.
http://en.cppreference.com/w/cpp/io/basic_ofstream
(Note the comment next to the destructor).
int main(int argc, char * argv[]) {
bool output = true;
if (output) {
ofstream fout("test.txt");
//do with fout, let it close itself
}
} // end main
The above code is fine.
The error is because you are defining "fout" in the "if-statement" and it is not defined out of that area(out of the if-statement)
you can instead do this :
#include <fstream>
using namespace std ;
int main()
{
ofstream fout ;
bool output = true ;
if(output)
{
fout.open("test.txt");
}
if(output)
{
fout.close();
}
}
Food for Thought :)
The simple solution to the minimal example is to either declare the ofstream in a scope where it exists for the duration of the time you need to use it. Then you just have your conditional statements where you want to actually use the stream:
{
bool output = someArgOrTestResult;
ofstream myOutStream;
if( output ) mOutStream << "Whatever";
}
However the repeated conditional statements can get (a) ugly, and (b) hard to maintain if you decide at sometime to change behaviour. So it might be beneficial to look at what you are doing and create an MyOutput class.
class MyOutput
{
private:
std::string filename;
bool output;
ofstream fout;
public:
MyOutput( const std::string& fn , bool out ) : filename(fn) , output(out)
{
if( output )
fout.open(filename); //Check syntax might need .c_str()
}
~MyOutput()
{
if( output )
fout.close();
}
//There are many ways to expose the output function but simplest
void write( const std::string& data )
{
if( output )
{
output << data;
}
}
};
NOTE - there is a lot missing in terms of error checking that you would need to put in - for example what happens if filename can't be opened.
Then in main you can have something like this:
#include <fstream>
int main(int argc, char * argv[]) {
bool output = true;
MyOutput fout("test.txt",output);
std::string myLocalData = "Some stuff....";
fout.write(myLocalData);
} // end main
The advantage now is that you can just have the statement to write data without the surrounding test. There are a million variations on this, and it might be that the stuff you want to write can even be accessed if output is not set, but if that's the case then maybe the processing needs to be in MyOutput as well although it would need a rename and different methods...

Using the fstream getline() function inside a class

I'm trying to load lines of a text file containing dictionary words into an array object. I want an array to hold all the words that start with "a", another one for "b" ... for all the letters in the alphabet.
Here's the class I wrote for the array object.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class ArrayObj
{
private:
string *list;
int size;
public:
~ArrayObj(){ delete list;}
void loadArray(string fileName, string letter)
{
ifstream myFile;
string str = "";
myFile.open(fileName);
size = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if (str.at(0) == letter.at(0))
size++;
}
size -= 1;
list = new string[size];
int i = 0;
while(!myFile.eof())
{
myFile.getline(str, 100);
if(str.at(0) == letter.at(0))
{
list[i] = str;
i++;
}
}
myFile.close();
}
};
I'm getting an error saying:
2 IntelliSense: no instance of overloaded function "std::basic_ifstream<_Elem, _Traits>::getline [with _Elem=char, _Traits=std::char_traits<char>]" matches the argument list d:\champlain\spring 2012\algorithms and data structures\weeks 8-10\map2\arrayobj.h 39
I guess it's requiring me to overload the getline function, but I'm not quite certain how to go about or why it's necessary.
Any advice?
the function for streams that deals with std::string is not a member function of istream but rather a free function it is used like so. (the member function version deals with char*).
std::string str;
std::ifstream file("file.dat");
std::getline(file, str);
It is worth noting there are better safer ways to do what you are trying to do like so:
#include <fstream>
#include <string>
#include <vector>
//typedeffing is optional, I would give it a better name
//like vector_str or something more descriptive than ArrayObj
typedef std::vector<std::string> > ArrayObj
ArrayObj load_array(const std::string file_name, char letter)
{
std::ifstream file(file_name);
ArrayObj lines;
std::string str;
while(std::getline(file, str)){
if(str.at(0)==letter){
lines.push_back(str);
}
}
return lines;
}
int main(){
//loads lines from a file
ArrayObj awords=load_array("file.dat", 'a');
ArrayObj bwords=load_array("file.dat", 'b');
//ao.at(0); //access elements
}
don't reinvent the wheel; checkout vectors they are standard and will save you a lot of time and pain.
Final try not to put in using namespace std that is bad for a whole host of reasons I wont go into; instead prefix std objects with std:: so like std::cout or std::string.
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/string/basic_string/getline
http://en.cppreference.com/w/cpp/string