GPWiki.org
GPWiki.org
It is currently Sun May 26, 2013 6:27 am

All times are UTC




Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Sat Oct 20, 2012 6:56 pm 
Shake'n'Baker
User avatar

Joined: Fri Feb 05, 2010 1:11 pm
Posts: 52
Location: MiddleEurope
Hi dudes,

Can anyone help to solve a mystery of fscanf VS sscanf functions?
I really don't know how can this be, and why two C standard library functions, based on the same
principle - to be more correct, fscanf and sscanf based on scanf - are not working the same way they should.

To make this issue clearer, I'll post here three links from C++ Reference site describing these functions.

http://www.cplusplus.com/reference/clib ... dio/scanf/
http://www.cplusplus.com/reference/clib ... io/fscanf/
http://www.cplusplus.com/reference/clib ... io/sscanf/


The practical part of the problem:

I have my custom header file dataIOhandlers.h, composed myself.
There are 2 functions that deal with loading tile data into 2-Dimensional map array (represented as int TileMap_base[255][255])

both functions are in principle the same, with the difference that one loads the data from .txt file using fscanf and the other one is SUPPOSED to load it from string (or char[], if you want) rather. This way I wanted to tell that it doesn't work for me the way it probably should.

my two functions in the header file:

declaration of these two functions:
Code:
void loadMap(char * s_TileMapVALUE);
void loadMapFromFile(char* filename);


definition:

Code:
//function that loads map from string - or char[] which is most probably the same in principle
void loadMap(char * s_TileMapVALUE) {

    int x = 0;
    int y = 0;

    //outputs the value loaded as argument, to make sure everything's ok
    cout << s_TileMapVALUE << endl;

   // const char * tilemapPtr;
   // tilemapPtr = (s_TileMapVALUE.c_str());


/* Read the data from the file into the map */
   int cX;
   int cY;

   cX = 1;
   cY = 1;

   for (y=0;y<MapSize[1];y++)
   {
      for (x=0;x<MapSize[0];x++)
      {

         sscanf(s_TileMapVALUE, "%d", &TileMap_base[y][x]);

            cout << TileMap_base[y][x] << ", ";

         //if (map.tile[y][x] != BLANK_TILE)
         //{


            if (x > cX)
            {


               cX = x;
            }

            if (y > cY)
            {
                    cout << endl;
               cY = y;

            }

         //}
      }
   }


   // cout << tilemap_char << endl;


}

//function that loads map from file
void loadMapFromFile(char* filename) {

FILE * mapFile_ptr;
mapFile_ptr = fopen(filename, "r");

    // if failed to open
    if (mapFile_ptr == NULL)
   {
    cout << "Failed to open the map." << endl;
   }

/* Read the data from the file into the map */
   int cX;
   int cY;

   cX = cY = 0;

   for (int y=0;y<MapSize[1];y++)
   {
      for (int x=0;x<MapSize[0];x++)
      {
         fscanf(mapFile_ptr, "%d", &TileMap_base[y][x]);
            cout << TileMap_base[y][x] << ", ";

         //if (map.tile[y][x] != BLANK_TILE)
         //{
            if (x > cX)
            {
               cX = x;
            }

            if (y > cY)
            {
                cout << endl;
               cY = y;
            }
         //}
      }
   }
}


I'm currently using 10 x 10 tiles of that map array.
Odd enough maybe, but the second function, void loadMapFromFile(char* filename), works ok for me - it uses fscanf.
Not so for the first function void loadMap(char * s_TileMapVALUE), cos if I want to pass this from my .cpp file into the function:

Code:
char tilemapvalue[] = "0 0 0 1 0 0 0 0 0 0\n"
                        "0 1 0 0 0 1 0 0 0 1\n"
                        "0 1 1 0 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 0 1 1 1 0 1 1\n"
                        "0 1 0 1 0 1 0 0 0 1\n"
                        "0 0 0 0 0 0 0 0 0 0\n"
                        "1 1 1 1 1 1 1 1 1 1";

    loadMap(tilemapvalue);


, the cout << s_TileMapVALUE << endl; will display exactly what I've passed as argument - tilemapvalue, but it later fails on using sscanf...
when I load it and check the TileMap_base[y][x], it is 10x10 tiles of 0... everything is 0, without one single "1" so that I could see HOW does it work!!!

Does sscanf work differently than regular scanf and fscanf?

_________________
Even this world is "programmed" by a Creator, the most skilled programmer of us all. What do you think of all that exists and all the environmental phenomena? He that maketh all had programmed it all and whenever needed, He can call one of the functions with input specified by Him. :)


Top
 Profile  
 
PostPosted: Sat Oct 20, 2012 9:26 pm 
Shake'n'Baker

Joined: Sun May 27, 2012 6:01 pm
Posts: 62
Code:
sscanf(s_TileMapVALUE, "%d", &TileMap_base[y][x]);


Your input is not being consumed with a char *, while your file is. The line above says find the first int in s_TileMapVALUE and place it at TileMap_base[y][x]. The first value in s_TileMapVALUE is the same value through out the loop.

You can see this if you change the first "0" in the char to a "1", which will turn all entries into your TileMap_base into "1"s

The solution is most likely a little more complex, Why can you not pass the int data structure to start with?

Mikey


Top
 Profile  
 
PostPosted: Sat Oct 20, 2012 11:05 pm 
Shake'n'Baker
User avatar

Joined: Fri Feb 05, 2010 1:11 pm
Posts: 52
Location: MiddleEurope
cxzuk wrote:
Code:
sscanf(s_TileMapVALUE, "%d", &TileMap_base[y][x]);


Your input is not being consumed with a char *, while your file is. The line above says find the first int in s_TileMapVALUE and place it at TileMap_base[y][x]. The first value in s_TileMapVALUE is the same value through out the loop.

You can see this if you change the first "0" in the char to a "1", which will turn all entries into your TileMap_base into "1"s

The solution is most likely a little more complex, Why can you not pass the int data structure to start with?

Mikey


Thanx Mikey,

that helped me a lot, as now I realized that yeah... this will probably be the part of C++ travails that are inevitable to walk the road further.
char * points to 1st entry, as I've heard before... pointer initially points at 1st entry in array..

To start with int data structure? What do you mean exactly? To use rather class or struct for 2D Tile map storing of X, Y coordinates etc.?
I've already thought about the possibility I guess, BUT I must keep in mind that I need to have it available to just load string from external file - text file that carries more than just tile values of the map, one file for all map specs, including name of the map, its size, objects on it, and of course tiles.
That's why I have my own parsing function for it... map "tile values string" would be one field to extract from the file into string - and THAT would be later sscanf-ed.

Therefore, fscanf is not the exact thing that would be useful.

I already have had this before - in the init of my .cpp file

Code:
TileMap_base = {{1,1,1,1,1,1,1,1,1,1},
                    {1,1,1,1,1,1,1,1,1,1},
                    {1,1,1,1,1,1,1,1,1,1},
                    {1,1,1,0,0,1,1,0,1,1},
                    {1,1,1,1,0,1,0,1,0,1},
                    {1,1,1,1,0,1,0,1,0,1},
                    {1,1,1,1,0,1,0,1,0,1},
                    {1,1,1,0,1,1,1,0,1,1},
                    {1,1,1,1,1,1,1,1,1,1},
                    {1,1,1,1,1,1,1,1,1,1},};


That means if I don't call a function to load map after this, this will be as a default just for now.
Of course, for every map editor it is intended to load & save maps into some external file (and I guess everyone will manage the specs and order or parsing and storing the data inside the file on his own for some reason). One of the data fields should be that 2-Dim array as a value, but since C++ is compiled, I can't have it as an expression and just tell the .cpp file "treat this expression in txt file as a code" :/.. I guess what you can do with fscanf, can be done with sscanf as well, but maybe with some modifications.

If I look into function specs of fscanf
http://www.cplusplus.com/reference/clib ... io/fscanf/
it looks like it takes FILE * stream as first argument - as a file you mentioned is being consumed whole... but HOW? and why?

I can hardly believe such a thing would be a great problem... cannot C++ take somehow whole string as if it was a FILE * stream passed to fscanf? The content of a .txt file is pretty much the same as any string, consisting of either numbers or chars or words.. of chars in general (numbers, letters and punctuation are all chars) But in terms of C++ it seems to have differences? What are they?

_________________
Even this world is "programmed" by a Creator, the most skilled programmer of us all. What do you think of all that exists and all the environmental phenomena? He that maketh all had programmed it all and whenever needed, He can call one of the functions with input specified by Him. :)


Top
 Profile  
 
PostPosted: Sun Oct 21, 2012 7:13 am 
Shake'n'Baker

Joined: Sun May 27, 2012 6:01 pm
Posts: 62
yes, sorry an array data structure like your example.

If you use a build script, can you not make it convert your text file into a data structure? (your string is in the data segment anyway, why not have it in a usable form straight away)

alternatively give stringstream a go; http://www.cplusplus.com/reference/iost ... ingstream/


Mikey


Top
 Profile  
 
PostPosted: Sun Oct 21, 2012 1:27 pm 
Shake'n'Baker
User avatar

Joined: Fri Feb 05, 2010 1:11 pm
Posts: 52
Location: MiddleEurope
thanx for mentioning it.
now I've made my program to read actual tiles from struct TileMap that has one parameter inside, int tileIdx.
Its realization is TileMap BkgTileMap[255][255] -as a background tile map for this moment.

now, to proceed thru loop and set the tiles I use:

Code:
for (int y; y < MapSize[1]; y++) {
   for (int x; x < MapSize[0]; x++) {
      BkgTileMap[x][y].tileIdx = TheTileWeWant;
   }
}


The thing I ask is, does using data structures make it easier to use, manipulate and read from string that has the format of

Code:
 char tilemapvalue[] = "0 0 0 1 0 0 0 0 0 0\n"
                        "0 1 0 0 0 1 0 0 0 1\n"
                        "0 1 1 0 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 1 1 1 1 0 1 1\n"
                        "0 1 1 0 1 1 1 0 1 1\n"
                        "0 1 0 1 0 1 0 0 0 1\n"
                        "0 0 0 0 0 0 0 0 0 0\n"
                        "1 1 1 1 1 1 1 1 1 1";


with the whitespaces? Easier than for classical int array[][] ?

_________________
Even this world is "programmed" by a Creator, the most skilled programmer of us all. What do you think of all that exists and all the environmental phenomena? He that maketh all had programmed it all and whenever needed, He can call one of the functions with input specified by Him. :)


Top
 Profile  
 
PostPosted: Sun Oct 21, 2012 3:02 pm 
Bytewise

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 277
Location: Here (where else?)
One approach to your problem could be

Code:
char *current;

int GetNumber() {
    while (*current == ' ' || *current == '\t' || *current == '\n') current++; // Silently skip white space.
    if (*current == '\0') return -1; // Reached the end of the string.

    int res = 0;
    while (*current >= '0' && *current <= '9') {
        res = res * 10 + *current - '0';
        current++;
    }
    return res;
}

current = tilemapvalue;
for (int y; y < MapSize[1]; y++) {
   for (int x; x < MapSize[0]; x++) {
      BkgTileMap[x][y].tileIdx = GetNumber();
   }
}
Where current points to the location in the string to read next. Note that this implementation will fail miserably when it encounters something else than spaces or decimal digits.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Thu Nov 01, 2012 9:20 pm 
Shake'n'Baker
User avatar

Joined: Fri Feb 05, 2010 1:11 pm
Posts: 52
Location: MiddleEurope
hmm... nice try Alberth; however, worse is the limitation to 0-9 digits..
If one day the tile specifier turns into ANY integer value (e.g. 0-1000 tile types), or it'll be stored in string value as the name of a tile type (e.g. "grass01", "grass02", "concrete01", "cliff03_north", "cliff04_south" etc.) it will encounter a great deal of a problem, as you mentioned.

Maybe the style of storing tile data should be different. For example one tile per line; each tile separated by carriage return.
This method may be (by a friend of mine) quite unreadable in context of the whole map - as you can't see the overall look of the map having all the tiles a long list downwards, but when dealing with tile type names in string form it should be more comprehensible if we consider that some tile types may have longer names than other.

The thing here would be only to read it per line... but, can I FORCE the standard library to extract a specific line for me using getLine(...).. ?
I looked into and used getLine a bit, but now I don't know if you can specify let's say: 3rd line of the text here, and 25th line of the text here? Can you specify by passing the integer number - WHICH line will be extracted?

_________________
Even this world is "programmed" by a Creator, the most skilled programmer of us all. What do you think of all that exists and all the environmental phenomena? He that maketh all had programmed it all and whenever needed, He can call one of the functions with input specified by Him. :)


Top
 Profile  
 
PostPosted: Thu Nov 01, 2012 9:59 pm 
Bytewise

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 277
Location: Here (where else?)
Tbh, I would not do this manually, but instead add a scanner and parser to do the work for me. These tools are designed to allow processing text in almost any format you like.

Depending on how many tiles you get, you can also consider using a more compact format, for example one character per tile, or one pixel in an image.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Fri Nov 02, 2012 9:57 am 
Bytewise

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 277
Location: Here (where else?)
Adding a second reply, yesterday was a bit too fast, but it feels wrong to remove it.

ElectroPaladin wrote:
hmm... nice try Alberth; however, worse is the limitation to 0-9 digits..
If one day the tile specifier turns into ANY integer value (e.g. 0-1000 tile types)
Ah, you didn't actually try it, did you :)
The code parses arbitrary numbers up to the 'int' limit. (That's what the "while" loop below "int res = 0" does).

ElectroPaladin wrote:
it'll be stored in string value as the name of a tile type (e.g. "grass01", "grass02", "concrete01", "cliff03_north", "cliff04_south" etc.) it will encounter a great deal of a problem, as you mentioned.
You did not ask for this case, but fundamentally nothing changes, so it is easy to adapt the code. Replace the "int res = 0" ... "return res" code by

Code:
static char buf[256];
int i = 0;
while (*current != '\0' && *current != ' ' && *current != '\t' && *current != '\n') {
    if (i < 255) { buf[i] = *current; i++; }
    current++;
}
buf[i] = '\0';
return buf;
This returns a string, so the return type should also change, and the "return -1" would better be "return NULL".
The code that calls this routine also needs to change to deal with names instead of numbers.


ElectroPaladin wrote:
Maybe the style of storing tile data should be different. For example one tile per line; each tile separated by carriage return.
This method may be (by a friend of mine) quite unreadable in context of the whole map - as you can't see the overall look of the map having all the tiles a long list downwards, but when dealing with tile type names in string form it should be more comprehensible if we consider that some tile types may have longer names than other.
There is a trade-off between human-readability, and machine-readability.
Machine readable means a simple format, and it can be programmed with little effort. The problem is that understanding the big picture is more complicated.
Human readable means that you have to do more work in programming, since humans tend to be messy, they want things vertically aligned (with random sequences of TABs and spaces), add empty lines, use names instead of numbers, have comments, etc.
For a machine this means more work, and thus you need to do more programming.
On the other hand, entering data becomes a lot nicer (use names for everything), reducing the amount of errors that you make, and increasing the ease of doing experiments (comment out one line, add some other data, and retry).

In compiler construction, the latter (converting a big string of text or file to something that a machine understands) is called scanning and parsing. It is a well understood problem area, and there are standard tools available for solving this problem.
The usual approach is to use a scanner generator like 'lex' and a parser generator like 'yacc', and describe what format the input has. lex & yacc then generate a scanner and parser for you. Hook them up to your code, and you're set.
These tools can handle normal programming languages like C, so a map layout format is trivial for them.

It is an investment in understanding how these things work, but you leap from struggling with reading characters to: hmm, maybe we should have a description like
Code:
map Level1;
size 26 x 31;

tiles {
  { grass, grass, grass, desert },
  { sand, rocks, grass rocks }
}

enemy red;
(ie pretty much anything you want to express)

In my project, I take it one step further. I have 'human readable' descriptions[1], which I convert to a machine readable format. That output is written to a new file, and loaded by my game. Loading those new files is simple, but of course the cost is that I have to write that convertor.

[1]: It is XML, which imho does not really classify as "readable". Eventually I want to move to a plain text format like the description I gave above, but I don't want to spend time on that now.


ElectroPaladin wrote:
I looked into and used getLine a bit, but now I don't know if you can specify let's say: 3rd line of the text here, and 25th line of the text here? Can you specify by passing the integer number - WHICH line will be extracted?
Strings and files are sequential beasts; you know the start, and you know that's where line 1 starts. You can also get the total size (often), and that's it.
If you need line 25, start at line 1, and read the 24 lines before it first.
Obviously, you can store the start offsets in an array, so the second time you need it, it's much easier.

(The exception is when each line has a known length. Then you can just jump to the right position relative from the beginning, and read from there.)

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group