Reputation: 117
Hey is it possible to have a text file which its contents are:
Weapon Name: Katana
Damage: 20
Weight: 6
Is it possible to assign these bits of information into member variables of a weapons class?. So that when i call getWeaponName in my main i will get Katana?
I was looking around google and i can get the whole text file input but its not assigned to any variable.
The code i have so far is:
Weapons :: Weapons()
{
this->weaponName = "";
this->damage = 0;
this->weight = 0;
}
Weapons :: Weapons(string weaponName,int damage,int weight)
{
this->weaponName = weaponName;
this->damage = damage;
this->weight = weight;
}
void Weapons :: getWeapon()
{
ifstream myfile ("Weapons\\Katana.txt");
string line;
if (myfile.is_open())
{
while (myfile.good())
{
getline (myfile,weaponName,'\t');//This line gets the entire text file.
//getline (myfile,damage,'\t');
//getline (myfile,weight,'\t');
//myfile >> weaponName;
//myfile >> damage;
//myfile >> weight;
cout << weaponName<< "\n";
}
myfile.close();
}
else
{
cout << "Unable to open file";
}
}
Thanks in advance.
Upvotes: 0
Views: 139
Reputation: 15397
Change
getline (myfile, weaponName, '\t');
to
getline (myfile, weaponName);
What your version is doing is telling getline
to grab everything in the file, up to a tab character, and I'm guessing you don't have any tab characters. The version I'm recommending - with no delimiter specified - will get characters up to a newline. So it should read in Weapon Name: Katana
.
Then you still need to extract "Katana". Assuming your input file has a very fixed format, you can simply do something like
weaponName = weaponName.substr(weaponName.find_first_of(':') + 2);
This will take the substring starting at the position 2 after the ':'.
Using weaponName
is not exactly proper for your getline statement. weaponName
is a string, but at that point, you're just looking for a line. You already have the proper variables in place in getWeapon()
. We just need to use them:
void Weapons :: getWeapon()
{
ifstream myfile ("Weapons\\Katana.txt");
string line;
string number;
if (myfile.is_open())
{
while (myfile.good())
{
getline (myfile,line);
weaponName = line.substr(line.find_first_of(':') + 2);
getline (myfile,line);
number = line.substr(line.find_first_of(':') + 2);
damage = atoi(number.c_str());
getline (myfile,line);
number = line.substr(line.find_first_of(':') + 2);
weight = atoi(number.c_str());
cout << weaponName<< "\n";
}
myfile.close();
}
else
{
cout << "Unable to open file";
}
}
Note: you'll need to #include <stdlib.h>
for atoi
to work.
Honestly, this still isn't very robust. Others have offered you better solutions for looking at the input to see what the data is, and reading and storing all your data, but this should show you the very basics.
Upvotes: 2
Reputation: 490663
First, you need/want to distinguish between "weapons" (plural) and a single weapon. To make much sense, each individual weapon has the characteristics you're reading (name, weight, damage). So, weapons will be a collection of individual weapon objects, each of which has the characteristics.
Based on that, we can attempt to write some meaningful code:
class weapon {
std::string name;
int damage;
int weight;
public:
std::string get_name() { return name; }
Now, we want a weapon
to be able to "reconstitute" itself from data stored in a file. Note, however, that right now we're writing a weapon
class, so we're only going to deal with one weapon, not a whole collection of them:
friend std::istream &operator>>(std::istream &is, weapon &w) {
std::ignore(is, 1024, ':'); // ignore the "Weapon Name:" header
std::getline(is, w.name);
std::ignore(is, 1024, ':'); // ignore the "Damage:" header
is >> w.damage;
std::ignore(is, 1024, ':'); // ignore the "Weight:" header
is >> w.weight;
return is;
}
Though we don't need it just yet, let's create a matching function to write out a weapon in the correct format as well:
std::ostream &operator<<(std::ostream &os, weapon const &w) {
return os << "Weapon Name: " << w.name << "\n"
<< "Damage: " << w.damage << "\n"
<< "Weight: " << w.weight << "\n";
}
With that, we can read the data for a single weapon. Then we need some way to store multiple weapons. Lacking a reason to do otherwise, our first choice for that is normally an std::vector
. If we want to fill that with the data from a file, we can do it something like this:
// open a file of all the weapons data:
std::ifstream in("weapons.txt");
// initialize the vector from the data in the file:
std::vector<weapon> weapons((std::istream_iterator<weapon>(in)),
std::istream_iterator<weapon>());
With this in place we can (for example) list all the weapons (here we're going to use the "operator<<" we defined above):
std::copy(weapons.begin(), weapons.end(),
std::ostream_iterator<weapon>(std::cout, "\n"));
If we want an abbreviated list with just the name of each weapon, we can do something like this:
for (auto const &w : weapons)
std::cout << w.get_name() << "\n";
Upvotes: 1
Reputation: 154047
Your format looks like a variant of a typical .ini file. There
are lots of parsers around for that, if you can modify the
format to make it conform. That would be by far the easiest
solution. Otherwise: how are the various weapons separated in
the file? Is it by an empty line, or is it because the first
entry is always "Weapon Name"
? In the first case, I would use
something like the following to read the file (in a free
function, not as a member):
std::auto_ptr<Weapon> currentWeapon;
Line line;
int lineNumber = 0;
while ( source >> line ) {
++ lineNumber;
if ( line.empty() ) {
if ( currentWeapon.get() != NULL ) {
weaponCollection.insert( currentWeapon );
}
currentWeapon.release();
} else {
Line::const_iterator pivot
= std::find( line.begin(), line.end(), ':' );
if ( pivot == line.end() ) {
// Output error message, using lineNumber...
} else {
if ( currentWeapon.get() == NULL ) {
currentWeapon.reset( new Weapon );
}
std::string key( strip( std::string( line.begin(), pivot ) ) );
std::string value( strip( std::string( pivot + 1, line.end() ) ) );
if ( key == "WeaponName" ) {
currentWeapon->weaponName = value;
} else if ( key == "Damage" ) {
currentWeapon->damage = asInt( value );
} else if ( key == "Weight" ) {
currentWeapon->weight = asInt( value );
} // ...
else {
// output error message...
}
}
}
}
Line
is a class in my toolbox, that I use a lot for this sort
of thing. It's basically:
class Line : private std::string
{
public:
typedef std::string::iterator iterator;
// ...
using std::string::empty;
using std::string::begin;
// ...
};
The only difference between it and std::string
is that its
operator>>
calls std::getline
, then "cleans up" the results,
by removing any trailing white space (including a possible
'\r'
, because the file was written under Windows, but I'm
reading it under Unix); in this case, it might be useful to make
it also remove any leading white space. (Mine also has
a manipulator which sets a comment character; if this is set, it
removes any text from this character to the end of the line,
before trimming trailing whitespace.
(From experience: do provide some sort of facility for commenting
in the file. You'll regret it if you don't.)
And asInt
is, of course:
int
asInt( std::string const& original )
{
std::istringstream s( original );
int results;
s >> results >> std::ws;
if ( !s || s.get() != EOF ) {
// Error...
}
return results;
}
Again, something that you should have in your toolbox already.
If the key to a new weapon is the "Weapon Name"
attribute,
skip the empty line business (or treat empty lines as comments),
and store any existing Weapon and create the new one in the
handling for "Weapon Name"
.
If you throw on error, above, you'll need to use a try...catch block to output the error and continue. You might also want to mark that there was an error somewhere, and abort the game if so.
Upvotes: 0
Reputation: 10685
You will need to parse each line of your file. So, change the function
getline(myfile, weaponName, '\t');
to
getline(myfile, weaponName);
and parse result.
Do something like that:
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string line = "Weapon Name: Katana";
int pos = line.find(':');
string weaponName;
if ( line.substr(0, pos) == "Weapon Name")
weaponName = line.substr(pos+1, line.npos);
printf("%s\n", weaponName.c_str());
}
Upvotes: 2