Sign in to follow this  
crazykid48x

c++ input from a file.

Recommended Posts

crazykid48x    100
Im making a textbased game that loads information about the different room objects from a file. Theres an area class and the constructor does all the loading. Heres what I got so far. I basicly loads the name, description and position of each room. class constructor:
area::area()
{
      ifstream room("rooms.txt");
      room.seekg(pos);
      room.getline(roomname, 20);
      room.getline(description, 1000);
      for(int i=0; i<2; i++)
          room>>position[i];
      pos=room.tellg();
      room.close();
}

Text file:
The Hallway
Everyones room is on this hallway. The numbers on the doors go from 200 to 300.
1 1
The lobby
The lobby has gold tiles and a ceilling atleast 30 feet high. There are beutiful gold paterns along the walls and the ceilling. There is a desk with a receptionist north of you and an elevator to the west. To the east theres a dining area and to the south, the exit.
1 2
the Dining hall
The Dining hall has gold pattern walls just like the lobby but instead of tiles it has red carpet. Theres rows of round tables at witch to eat, All properly set. The bar is to the east, and the pool is to the south. To the west is the lobby. 
1 3
The Bar
The Bar follows the look of the other rooms. There are lots of colorful lights above the bar. Behind the bar is a large array of bottles. To the north is the bartender, To the west is the Dining hall.
1 4
The Outside
Outside of the hotel theres two valley parkers in red uniforms. If you look up you can see all the windows to the rooms on your floor. To the north is the Lobby.
2 2
The Pool
The Pool is a great place to relax. The shimmering water is soothing. To the north is the Dining Hall.
2 3

Pos is a global variable, atfirst set to 0. Its used to keep track of where to load from. I think somehow its the problem. the problem is the beginnig of the room names get cut off. (sory if this was a simple problem and i didnt have to give so much info).

Share this post


Link to post
Share on other sites
Enigma    1410
seekg and tellg can be unreliable with text mode files under Operating Systems which use multiple characters to represent a newline (like Windows). This is because the number of characters read can be different to the number of bytes read. Since it seems likely that all the area will be constructed one after the other you could just delegate the file opening to a higher level function and pass the ifstream to the constructor:
void loadAreas()
{
ifstream roomsData("rooms.txt");
something = area(roomsData);
// ...
}

area::area(ifstream & data)
{
data.getline(roomname, 20);
// ...
}

Actually, I'd make a further suggestion than that, since it seems you are using the dreaded char * to represent text in C++. Replace your evil char *'s with std::strings and change data.getline(x, y); to getline(data, x);.

Enigma

Share this post


Link to post
Share on other sites
crazykid48x    100
Thats exactly what I was going to do, but I had trouble making an array of objects that had constructor arguments. Could you please show me how your function would work to make alot of room objects?

Share this post


Link to post
Share on other sites
Enigma    1410
Probably something like:
std::vector< area > rooms;

void loadAreas()
{
ifstream roomsData("rooms.txt");
while (!roomsData.eof())
{
rooms.push_back(area(roomsData));
}
}

Enigma

Share this post


Link to post
Share on other sites
Zahlman    1682
Hrm... where exactly are you getting 'pos' from now? It looks as though you're keeping it stored in a global, so that you can re-open and re-close the file for each object construction.

Constructing from a stream object would indeed be cleaner (no reopening or reclosing needed; just re-pass the same stream each time, as it has already 'advanced' to the right position). But yes, you don't really get to declare arrays of non-default-constructed objects in C++ - a rather annoying language limitation.

In your case, the solution I would propose is not to load into an array anyway, but instead into a std::vector. That will also free you from having to know (or somehow indicate) how many rooms are indicated in the file.

Something like:


std::vector<area> rooms;
std::ifstream data("rooms.txt");
while (!data.eof()) {
rooms.push_back(area(data));
}

Share this post


Link to post
Share on other sites
crazykid48x    100
ah crap, I was trieng to aviod using vectors. Ive always thought they were gonna be complicated because they werent covered in my 4 dummies book but it looks simple enough. thanks for the help everyone.

Share this post


Link to post
Share on other sites
crazykid48x    100
I left and read through a vector tutorial and then got back to work. But now im getting the oddest error Ive ever seen! here I'll post the whole source (dont wory it isnt much!)


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <vector>
using namespace std;
class area
{
private:
bool firstvisit;
char roomname[20];
char description[1000];
public:
int position[2];
area(ifstream& room);
int DisplayArea();
};

area::area(ifstream& room)
{
//cout<<"error";
room.getline(roomname, 20);
room.getline(description, 1000);
for(int i=0; i<2; i++)
room>>position[i];
//cout<<roomname;
}

int area::DisplayArea()
{
cout<<roomname<<endl<<endl<<description<<endl;
for(int i=0; i<2; i++)
cout<<position[i];
cout<<endl<<endl;
}
int main()
{
vector<area> map;
ifstream room("rooms.txt");
while(!room.eof())
map.push_back(area(room));
room.close();
for(int i=0; i<map.size(); i++)
map[i].DisplayArea();
getchar();
return 0;
}






At first I wasnt getting any output. Then I placed cout statements in the constructer and thats where it gets odd. If you un comment the first cout (cout<<"error") you will se the effects of an infinate loop. but If you recomment that and un comment the 2nd cout statement (cout<<roomname) It will only apear once. And also when i tried getline(room, roomname); it gave me an error. (no matching function for call to `getline(std::basic_ifstream<char, std::char_traits<char> >&, char[20])')

I didnt think making a text based game would be so hard. And im just now getting started.

[Edited by - crazykid48x on November 11, 2005 9:37:00 PM]

Share this post


Link to post
Share on other sites
Oluseyi    2103
Quote:
Original post by crazykid48x
I didnt think making a text based game would be so hard. And im just now getting started.

The problem is that text processing is not one of C++'s greatest strengths. It may sound unintuitive, but you'd probably obtain faster and more stable results if you either build a chunk-based binary format (each chunk stores its own length in bytes and you drop newlines entirely) or use a paired key-value format a la .ini files.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by crazykid48x
I left and read through a vector tutorial and then got back to work. But now im getting the oddest error Ive ever seen! here I'll post the whole source (dont wory it isnt much!)

*** Source Snippet Removed ***

At first I wasnt getting any output. Then I placed cout statements in the constructer and thats where it gets odd. If you un comment the first cout (cout<<"error") you will se the effects of an infinate loop. but If you recomment that and un comment the 2nd cout statement (cout<<roomname) It will only apear once.


The line where you read the numbers does not end up reading the newline for that line. As a result, the next getline call (for the next object creation) will read an empty line for a name, then a name line for a description, then try to get numbers from the description line. That will cause the stream to 'fail' because there are no numbers there, and because that state is not recovered from and you read in a loop, the process gets stuck there.

Quote:
And also when i tried getline(room, roomname); it gave me an error. (no matching function for call to `getline(std::basic_ifstream<char, std::char_traits<char> >&, char[20])')


The free function std::getline expects a std::string for the second argument - which is what you should be using anyway. Just say no to storing text in char arrays.

Also, you do not initialize 'firstvisit', and you probably won't need the 'position' to be public. Also, as a style issue, output functions normally take and return a reference to an ostream, rather than being hard-coded to work with cout - that gives you extra flexibility, and also allows you to easily set up your objects to be output with a << operator. (As a more serious style issue, don't declare functions to return int if they don't return anything; 'void' exists as a return type for a reason.)


#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <limits>
using namespace std;

class area {
private:
bool firstvisit;
string roomname;
string description;
int position[2];
area(ifstream& room);
ostream& display(ostream& os);
};

area::area(ifstream& room) : firstvisit(false) { // or true, if that's more appropriate? :s
getline(room, roomname);
getline(room, description);
room >> position[0] >> position[1];
// Now skip the rest of that line. We could read into another 'garbage'
// string, or else explicitly 'ignore' the rest:
room.ignore(numeric_limits<streamsize>::max(), '\n');
}

ostream& area::display(ostream& os) {
return os<<roomname<<"\n\n"<<description<<"\n"<<
"Location: ("<<position[0]<<", "<<position[1]<<")";
// Such functions as this normally do not emit 'endl' or 'flush'
// so that the caller retains control over when flushing occurs.
}

// And now, a little bit of magic.
// This allows us to do
// area foo(mystream);
// cout << foo << endl; <-- invoking foo.display as a result.

ostream& operator<<(ostream& os, const area& a) {
return a.display(os);
}



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this