Jump to content

  • Log In with Google      Sign In   
  • Create Account


How to write a undertemined size array to a txt file?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 22 October 2013 - 02:28 PM

Hey guys, I'm doing a Text-based RPG game and it will include some checkpoints, on those checkpoints the game will save the progress, so I need to write all my variables to a text file and then read them all when the player loads the game.

Some of my variables, like enemyNames, dont have any determined size, they're like this: "int enemyNames[ ];"

So how do I write an array to a text file without determining a size?
And how do I read them when the player loads the game?

Thank you so much for your help.

Sponsor:

#2 rip-off   Moderators   -  Reputation: 7963

Like
3Likes
Like

Posted 22 October 2013 - 02:45 PM

If you aren't storing the size of the array, you won't be able to use it for anything. You probably do have the number of effective entries, and/or the total number of entries, somewhere. But consider using std::vector<T> rather than raw arrays.

Once you know the size, you can proceed to write the data. There are many ways to do this, it really depends on what constraints there might be, if any, on the data. A simple approach might be to write the number of entries in the list on the current line, and then write each entry on a newline. This will work fine unless you wish to support newlines in the data you are saving.

You should consider using an existing format such as JSON, rather than hand-rolling your own parser and writer.

#3 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 22 October 2013 - 02:53 PM

it's just that, not defining the size spares me from the work of changing its size everytime I add a new enemy name for example but if i don't have any option i guess i'll change it..

#4 TheComet   Members   -  Reputation: 1448

Like
4Likes
Like

Posted 22 October 2013 - 02:59 PM

He's saying to use std::vector<T> instead of C arrays. Here, check this out:
#include <iostream>
#include <string>
#include <vector>

int main()
{

    // this is a dynamic array
    std::vector<std::string> enemyNames;
    
    // add as many names to the array as needed
    enemyNames.push_back( "Cubert Cumberdale" );
    enemyNames.push_back( "The Dark Lord" );
    enemyNames.push_back( "ponies" );

    // this will loop through all elements in the array
    for( std::size_t i = 0; i != enemyNames.size(); ++i )
    {

        // you could change this to write to a file
        std::cout << enemyNames[i] << std::endl;
    }

    return 0;
}


Edited by rip-off, 23 October 2013 - 05:23 AM.

YOUR_OPINION >/dev/null


#5 HappyCoder   Members   -  Reputation: 2365

Like
0Likes
Like

Posted 22 October 2013 - 03:01 PM

using the vector class will also handle the resizing for you.

#6 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 22 October 2013 - 03:13 PM

He's saying to use std::vector<T> instead of C arrays. Here, check this out:

#include <iostream>
#include <string>
#include <vector>

int main()
{

    // this is a dynamic array
    std::vector<std::string> enemyNames;
    
    // add as many names to the array as needed
    enemyNames.push_back( "Cubert Cumberdale" );
    enemyNames.push_back( "The Dark Lord" );
    enemyNames.push_back( "ponies" );

    // this will loop through all elements in the array
    for( std::size_t i = 0; i != enemyNames.size(); ++i )
    {

        // you could change this to write to a file
        std::cout << enemyNames[i] << std::endl;
    }

    return 0;
}

thank you very much, this helps a lot, and when i'm using it in the game itself, i just declare them as i would do with a regular array right?

Edited by rip-off, 23 October 2013 - 05:23 AM.


#7 Acid-Chris   Members   -  Reputation: 472

Like
-4Likes
Like

Posted 23 October 2013 - 05:21 AM

This is just bad, bad code design. You should seriously consider a rework of your game structure.

It's pretty pointless to store anything in a "i dont know exactly how many items i need to store" matter.

 

Sure, the example code above will work, even if you'd need a variable which has the number of elements to read, also stored in your file.

If you really want to continue with your approach, check out the previously mentioned JSON format, even XML will do. 

 

If you continue in that hackish way, you'll be very frustraed at some point and you'll end up throwing your project away.



#8 rip-off   Moderators   -  Reputation: 7963

Like
2Likes
Like

Posted 23 October 2013 - 05:22 AM

... not defining the size spares me from the work of changing its size everytime I add a new enemy name for example...

C++ arrays must have their dimensions set at compile time. You are confused if you believe you can do otherwise. For a fixed array with a dynamic number of active entries, you would need to maintain three variables: the array itself, the total number of elements, and the number of active elements.

... when i'm using it in the game itself, i just declare them as i would do with a regular array right?

More or less. Instead of writing T myContainer[N], you would use std::vector<T> myContainer.

When passing to functions, you may want to use (const) reference to avoid unnecessary copies, e.g:
// When this function is called, a vector is temporarily created, and the data is copied from the source
// When the function returns, the temporary data is destroyed
void frobnicateCopy(std::vector<Example> data) {
    // frobnication...
}

// No copies or significant work is done when this function is called or returns
// The data is fetched (indirectly) from the source vector
void frobnicateReference(const std::vector<Example> &data) {
    // frobnication...
}
Note that raw C++ array parameters in function signatures are actually pointers in disguise, so any functions you were writing with your raw arrays were effectively passed by reference like the latter style above.
// The following declarations are actually identical

void frobnicate(Example data[], int totalElements) {
    // frobnication...
}

void frobnicate(Example *data, int totalElements) {
    // frobnication...
}


#9 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 23 October 2013 - 06:59 AM

This is just bad, bad code design. You should seriously consider a rework of your game structure.

It's pretty pointless to store anything in a "i dont know exactly how many items i need to store" matter.

 

Sure, the example code above will work, even if you'd need a variable which has the number of elements to read, also stored in your file.

If you really want to continue with your approach, check out the previously mentioned JSON format, even XML will do. 

 

If you continue in that hackish way, you'll be very frustraed at some point and you'll end up throwing your project away.

i'll just change it then, it's not too late...



#10 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 23 October 2013 - 07:01 AM

i still don't know how to read the data from the text file and give those values to the differente variables, can anyone give me a hint?



#11 rip-off   Moderators   -  Reputation: 7963

Like
2Likes
Like

Posted 23 October 2013 - 07:18 AM

Which part specifically is giving you trouble? Can you print the contents of a text file to the screen? Can you read values from std::cin into variables? Is it a particular type of variable that is giving you trouble?

What have you tried?

#12 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 23 October 2013 - 10:28 AM

Which part specifically is giving you trouble? Can you print the contents of a text file to the screen? Can you read values from std::cin into variables? Is it a particular type of variable that is giving you trouble?

What have you tried?

 

well i'm using the "standard" way of I/O files, like this

 

string line;
    ifstream myfile("data\\checkpoint01.txt");

    if(myfile.is_open()){
        while(myfile.good()){

            getline(myfile,line);
            cout << line << endl;
                            }
        myfile.close();

but this clearly doesn't fit for me because it writes everything in the file, and i want it to grab the first line, assign it to a variable, second line same thing, grab the next 4 lines and assign it to an array and so on



#13 TheComet   Members   -  Reputation: 1448

Like
2Likes
Like

Posted 23 October 2013 - 10:48 AM

The first thing you should do is think about how you want to structure your save file. XML might be a bit of an overkill for a text RPG, especially if the programmer is new to the language. I suggest doing an "INI" approach, where you tag different sections of your file using square brackets. Example:

[playernames]
John McCarter
The Dark One
Margret Baxter

[weapons]
The Skull crusher
Frilly Underwear (Pink)
Baguette

The tags are there so you can detect them later in the code. Your approach is pretty solid so far, but you have to expand it to something like this:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>

int main()
{

    std::vector<std::string> playerNames;
    std::vector<std::string> weapons;

    std::string line;
    int state = 0; // state 1 is playerNames, state 2 is weapons, and so on

    std::ifstream myfile("data/checkpoint01.txt");
    if(myfile.is_open()){
        while(myfile.good()){

            std::getline(myfile,line);

            // switch states
            if( line[0] == '[' ){
                if( line.compare("[playernames]") == 0 ) state = 1;
                if( line.compare("[weapons]") == 0 ) state = 2;

                // skip to next line
                std::getline(myfile,line);
            }

            switch(state){

                // player names
                case 1 :
                    if( line.size() > 0 ) playerNames.push_back( line );
                    break;

                // weapons
                case 2 :
                    if( line.size() > 0 ) weapons.push_back( line );
                    break;

                default: break;
            }

        }
        myfile.close();
    }else
        std::cout << "error opening file" << std::endl;

    // now print data to the screen
    std::cout << "player names:" << std::endl;
    for( std::vector<std::string>::iterator it = playerNames.begin(); it != playerNames.end(); ++it )
        std::cout << *it << std::endl;

    std::cout << std::endl << "weapons:" << std::endl;
    for( std::vector<std::string>::iterator it = weapons.begin(); it != weapons.end(); ++it )
        std::cout << *it << std::endl;

    return 0;
}

Note to the more experienced: I know you could refactor this into a class with read/write methods, and should make separate classes for players, weapons etc. etc. and should probably define constants for states instead of numbers, but that's not the point.


Edited by TheComet, 23 October 2013 - 10:52 AM.

YOUR_OPINION >/dev/null


#14 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 23 October 2013 - 12:16 PM

well i seem to understand that code, but how do i assign those values into my variables? and how do i do it if it's, for example, an array?

because my method of writing to the file atm is 1 line per value, so if i have like

playerItem[1][0] = 0

playerItem[1][1] = 2

playerItem[1][2] = 3

 

it writes like

 

o

2

3



#15 TheComet   Members   -  Reputation: 1448

Like
2Likes
Like

Posted 23 October 2013 - 12:24 PM


but how do i assign those values into my variables? and how do i do it if it's, for example, an array?

That's basically what my code is demonstrating. It's reading an arbitrary amount of data from a text file and pushing them into an std::vector<T> (remember, that's an "array").

 

Maybe I'm not quite understanding what you're asking. Could you elaborate more? What arrays are you using now? Are you using std::vector<T>? Please post your declaration of playerItem[] again so we're on the same page.

 

Can you also provide the code you're using to write to the file?


YOUR_OPINION >/dev/null


#16 theseven   Members   -  Reputation: 143

Like
0Likes
Like

Posted 23 October 2013 - 12:41 PM

hm no i stil haven't used vectors for anything, up until now i can succesfully write all the variables and array to the file, and trying to see how to read them back.

(looking again to the vectors i understand what you're doing, although i wanna assign those values into my own variables, maybe your code can do that and i'm just too noob to realise)

 

 this is my writing code:

ofstream myfile("data\\checkpoint01.txt", ios::trunc);
         if(myfile.is_open()){
             myfile << playerInfo[0] << endl;
             myfile << playerLocation << endl;
             myfile << roleNr << endl;
             myfile << role << endl;
             myfile << playerName << endl;
             myfile << gold << endl;
             myfile << playerHealth << endl;
             myfile << playerStrength << endl;
             myfile << playerAgilty << endl;
             myfile << playerEndurance << endl;
             myfile << playerMaxHealth << endl;
             myfile << playerItemCount << endl;
             for(int i=0; i<playerItemCount; ++i){
                 for(int j=0; j<3; ++j){
                    myfile << playerItems[i][j] << endl;
                                        }
                                                }
             for(int i=0; i<3; ++i){
                for(int j=0; j<3; ++j){
                    if(playerEquippedItems[i][0]!=0){
                        myfile << playerEquippedItems[i][j] << endl;
                                                    }
                                        }
                                    }
             myfile << questUpdate1 << endl;
             myfile.close();

                        }

          else cout << "Unable to open quest file.\n";

i hope this answers to all that you asked


Edited by theseven, 23 October 2013 - 01:37 PM.


#17 wintertime   Members   -  Reputation: 1640

Like
2Likes
Like

Posted 23 October 2013 - 03:19 PM

A better way is you just check how big the file is(move file pointer to end, read it, move it to beginning), allocate memory(vector::resize or new unsigned char[]), read the whole file in a single call into that memory, close the file. Mapping the file directly into memory is even better for larger files but needs OS-specific API calls like CreateFile, GetFileSize, CreateFileMapping, MapViewOfFile, UnmapViewOfFile and CloseHandle on Windows, mmap on Unix.

Then you can use normal array indexing to check the data und pick what you need. Not only is that easier, it can also be faster to not do so many read calls.

When writing the file you can do it in similar way and put everything into a vector first and write it in one call.


Edited by wintertime, 23 October 2013 - 03:24 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS