Find the answer to your Linux question:
Results 1 to 9 of 9
hello all, im working on a wiimote device which reads from a configuration file to set what the buttons do (the config file has button=unsigned int format for each button. ...
  1. #1
    Just Joined!
    Join Date
    Dec 2006
    Posts
    85

    fscanf to read configuration files in c.

    hello all, im working on a wiimote device which reads from a configuration file to set what the buttons do (the config file has button=unsigned int format for each button. the int is a code for a button press event (such as KEY_PAGEUP))
    for the most part it works but button_plus and button_up do not work.
    below is the code for the function that reads from this file

    Code:
    void getwiiconf(wiimote *wm){
    FILE *myfile;
    //unsigned int* hello[2];
    //unsigned int hiya;
    //char *buffer;
    myfile = fopen("wiimote.conf","r");
    if(fscanf(myfile,"button_a=%u\nbutton_b=%u\nbutton_home=%u\nbutton_plus=%u\nbutton_minus=%u\nbutton_left=%u\nbutton_right=%u\nbutton_up=%u\nbutton_down=%u\nbutton_one=%u\nbutton_two=%u\n ",(unsigned int*)&wm->data.buttons.button_a,(unsigned int*)&wm->data.buttons.button_b,(unsigned int*)&wm->data.buttons.button_home,(unsigned int*)&wm->data.buttons.button_plus,(unsigned int*)&wm->data.buttons.button_minus,(unsigned int*)&wm->data.buttons.button_left,(unsigned int*)&wm->data.buttons.button_right,(unsigned int*)&wm->data.buttons.button_up,(unsigned int*)&wm->data.buttons.button_down,(unsigned int*)&wm->data.buttons.button_one,(unsigned int*)&wm->data.buttons.button_two)==0){
     printf("failed read\n");
     exit(1);
    }
    }
    thats a realy long version i used to save space, however i have broken it down with an fscanf for each button, and put printf's to show the value of the non-working buttons and its even wierder than i expected...i tracked button plus and it read it and assigned it...until it set button_up...then it changes to 0, button_up also changes to 0 after button_down. however all other buttons work...

    this is the wiimotedata (which is the main part of the wiimote struct..some things are there to be used later or to be removed later)

    Code:
    #ifndef WIIMOTEDATA_H
    #define WIIMOTEDATA_H
    
    #define MOTION_DEFAULT_SENSITIVITY		10.0f
    
    #define LED_ONE         0x10
    #define LED_TWO         0x20
    #define LED_THREE       0x40
    #define LED_FOUR        0x80
    
    #define BUTTON_TWO      0x0001
    #define BUTTON_ONE      0x0002
    #define BUTTON_B        0x0004
    #define BUTTON_A        0x0008
    #define BUTTON_MINUS    0x0010
    #define BUTTON_HOME     0x0080
    #define BUTTON_LEFT     0x0100
    #define BUTTON_RIGHT    0x0200
    #define BUTTON_DOWN     0x0400
    #define BUTTON_UP       0x0800
    #define BUTTON_PLUS     0x1000
    
    struct button{
    uint16_t button_a;
    uint16_t button_b;
    uint16_t button_home;
    uint16_t button_down;
    uint16_t button_up;
    uint16_t button_plus;
    uint16_t button_minus;
    uint16_t button_left;
    uint16_t button_right;
    uint16_t button_one;
    uint16_t button_two;	
    } button;
    
    typedef struct WiimoteData
    {
        /* The following members are related to updates. */
        int x, y, z;
        float ax, ay, az;
        uint16_t ButtonField;
        float pitch, roll, yaw;
        struct button buttons;
        float motion_sensitivity;
        
        unsigned char calg_x, calg_y, calg_z;  //Acceleration calibration G
        unsigned char calz_x, calz_y, calz_z;  //Acceleration calibration Zero
        
        /* The following member are related to commands. */
        unsigned char LEDField; //use LED masks above, 0 all off
        unsigned char isRumble; //1 or 0 for rumble on and off
        int connected;
    } WiimoteData;
    
    #endif /*WIIMOTEDATA_H*/
    so can you help me here? should i use something other than fscanf? this is my first time making a program to read a configuration file.

  2. #2
    Linux Engineer wje_lf's Avatar
    Join Date
    Sep 2007
    Location
    Mariposa
    Posts
    1,192
    Whenever reading text from a file which I expect to be in a given format, I never use fscanf(). I use fgets() to read the line in, making sure that I have allocated enough space for as long as the line might be. Then, if fgets() did not return NULL as its value, I look at the data in the buffer. If the final character before the NUL byte is not LF (line feed, \n), I know that the buffer was not large enough to hold the line.

    If the buffer was large enough, I then use sscanf() (two s's at the beginning of that function name) to parse the line I have just input.

    I split it up, calling two functions, for two reasons. First, if it's not working as expected, I can take a look at the string that was input, and it might give me a clue. Second, if you ever do this for interactive input, error recovery from invalid input is smoother, because you're going line by line, and you won't be hanging waiting for input on a following line if there wasn't enough input on the previous line. That would be confusing to the user.

    Hope this helps.
    --
    Bill

    Old age and treachery will overcome youth and skill.

  3. #3
    Just Joined!
    Join Date
    Dec 2006
    Posts
    85
    hmm i havent tried it like that...though i dont know how long the line will actually be (since "button_a=2" would be shorter than "button_minus=250" without knowing the length, that would be troublesome.)

    however i did find the source of the error, it was the parsing from uint16_t into unsigned int...i dont know WHY it was the problem...but i made the buttons field into ints and used %d and it seems to have resolved the problem...

    although i would like to be able to read them in any order, but since i will be making the conf file with another program i suppose it doesnt matter.

    well thanks anyways ^_^ always good to get a reply.

  4. #4
    Linux Engineer wje_lf's Avatar
    Join Date
    Sep 2007
    Location
    Mariposa
    Posts
    1,192
    i dont know how long the line will actually be (since "button_a=2" would be shorter than "button_minus=250" without knowing the length, that would be troublesome.)
    You don't have to know the precise length of the line. Just make the buffer large enough to include any likely input line. I'm old enough to remember punch cards, so I make many such lines 80 characters, and use 80 in the fgets() call. If you want to be closer to bulletproof, use 1024 instead. If you always check that the final character of the input line is a line feed, you'll soon know whether the buffer is large enough! :)

    If you want absolutely bulletproof code (which you probably don't need at this point), you react to a lack of line feed by saving that partial line and reading more of the same line with another fgets(). And so forth.

    On a practical level, 80 is usually good enough, 1024 is extremely unlikely to be too short, and splitting it up into fgets() and sscanf() will pay big dividends in input error recovery handling.
    --
    Bill

    Old age and treachery will overcome youth and skill.

  5. #5
    Just Joined!
    Join Date
    Dec 2006
    Posts
    85
    hmm...sscanf works on the same format as fscanf right. i was wondering if its possible to read for instance:
    Code:
    sscanf(string,"%s=%d",mystring,myint)
    if(mystring=="button_a"){
    wm->data.buttons.button_a=myint;
    }
    i tried that using fscanf:
    Code:
    void getwiiconf(wiimote *wm){
    FILE *myfile;
    myfile = fopen("wiimote.conf","r");
    int status;
    while(status != -1){
    char *buffer;
    int myint;
    char* myvalue;
    myvalue="button_a";
    printf("scanning\n");
    status=fscanf(myfile,"%s=%d\n",buffer,&myint);
    printf("scanned, read: %d\n",status);
    if (buffer==("button_a")){
    wm->data.buttons.button_a=myint;
    printf("buffer is: %s",buffer);
    }
    /*
    else if (buffer=="button_b"){
    wm->data.buttons.button_b=myint;
    }
    else if (buffer=="button_home"){
    wm->data.buttons.button_home=myint;
    }
    else if (buffer=="button_up"){
    wm->data.buttons.button_up=myint;
    }
    else if (buffer=="button_down"){
    wm->data.buttons.button_down=myint;
    }
    else if (buffer=="button_left"){
    wm->data.buttons.button_left=myint;
    }
    else if (buffer=="button_right"){
    wm->data.buttons.button_right=myint;
    }
    else if (buffer=="button_one"){
    wm->data.buttons.button_one=myint;
    }
    else if (buffer=="button_two"){
    wm->data.buttons.button_two=myint;
    }
    else if (buffer=="button_minus"){
    wm->data.buttons.button_minus=myint;
    }
    else if (buffer=="button_plus"){
    wm->data.buttons.button_plus=myint;
    }
    but for some reason it crashes (like that there though it doesnt even compile, says it wont allow a comparison to a string litteral...its just a warning though i CAN make it compile...but...)

    what do you think would be the best way to impliment this (the key here is to make the config file be in ANY order, so it reads string = int\n and compares string to see what to stick int into...)

  6. #6
    Linux Engineer wje_lf's Avatar
    Join Date
    Sep 2007
    Location
    Mariposa
    Posts
    1,192
    I'm not making a thorough analysis of your code, but the compiler tells you for good reason that this doesn't go:
    Code:
    if(mystring=="button_a")
    If you do that, you won't be comparing the content of each string; you'll be comparing the pointers to the first byte of each string. Comparing the pointers, you'll get "not equal" pretty much every time. Instead, use strcmp().

    Do this at the command line:
    Code:
    man strcmp
    If that man page is not installed on your system, google for this:
    Code:
    linux man strcmp
    Be aware that each string has two sizes: the number of bytes which have been allocated for the string, and the number of bytes in the actual content of the string. The final byte in a string is marked, how? It's marked by the byte after that byte being a NUL byte, a byte with no bits on. So if you have, say, 80 bytes allocated for a string, you can fit a string as long as 79 bytes in there, because the byte after the final significant byte must be that NUL byte.

    One advantage of sscanf() over fscanf() is that you have tighter control in debugging situations; you can clearly see what's on each input line.

    Another thing you need to be aware of is that just because you put that "=" in your format doesn't mean that the string you read in will end just before "=". That data string will consist of the whole line, and you will have only one item read by fgets(), not two. So if you execute the following script:
    Code:
    #!/bin/bash
    
    cat > 1.c <<EOD
    #include <stdio.h>
    
    int main(void)
    {
      int  int_result;
      int  myint;
    
       char label [80];
       myint=999;
     
      int_result=fscanf(stdin,"%s=%d",label,&myint);
    
      printf("fscanf() has read %d items\n",int_result);
      printf("label is \"%s\"\n",label);
      printf("myint is %d\n",myint);
    
      return 0;
    
    } /* main() */
    EOD
    cc -Wall 1.c -o 1
    ./1 <<EOD
    fred=5
    EOD
    ... you'll get this as output:
    Code:
    fscanf() has read 1 items
    label is "fred=5"
    myint is 999
    See how the value of myint has not changed from what the program set it to before the fscanf()?
    --
    Bill

    Old age and treachery will overcome youth and skill.

  7. #7
    Just Joined!
    Join Date
    Dec 2006
    Posts
    85
    hmm...i suspected that might be the case. i just kinda hoped it would take the "=" as being a sign to stop reading.
    but then...how do i make it so i can read them in any order...i do find the current way too ridgid, its fine for now but in the future i will have sections that need to be optionaly read (since my last post i added one, the modifier field. if it fails to match the string it sets a variable saying not to use the accelerometers and sets the modifier flags to 0, otherwise it reads into the modifiers fields.) however i intend to also add a field for the nunchaku which is also optional, and i dont want to have one to have the other. also i want a bit more flexibility in how users can modify the config file, so it can be button_a=xxx or button_b=xxx and not cause any problems.
    maybe i should use a string stream...

  8. #8
    Linux Engineer wje_lf's Avatar
    Join Date
    Sep 2007
    Location
    Mariposa
    Posts
    1,192
    If I were writing something like this in C, I would do the extra work of writing a loop to scan for the = and then processing what was to the left and the right of that character. I don't see a magic bullet.

    I'm hoping that someone will jump in and say that there's a library to do all this for you easily and quickly. But once again, my approach would be just to code it by hand so you can see everything right there in the source file.
    --
    Bill

    Old age and treachery will overcome youth and skill.

  9. #9
    Just Joined!
    Join Date
    Dec 2006
    Posts
    85
    i switched it over to c++ (i wasnt going to til i read about constructors and i knew i just HAD to have one of those. i also prettied my code up a lot) and just used a string stream.
    looks a bit like this:
    Code:
    while(feof(myfile)==0){//while the file has not ended.
    fgets(mybuf,80,myfile);//read...
    buffer=mybuf;//sets string buffer to be the same as char* mybuf. strings are better anyways..though you cant printf them...i dont think >.>...
    istringstream mystream(buffer);//makes a stringstream for this
    mystream >> mybuf >> myint;//sepperates them out into a string of chars and an int
    buffer=mybuf;//puts those chars back into buffer
    if(buffer=="[wiimote]"){//if its the wiimote section...
    while(buffer!="[/wiimote]"){//read until the end block, if its not there though this will read indefinately...probably something to fix...
    fgets(mybuf,80,myfile);
    buffer=mybuf;
    istringstream mystream(buffer);//i dont know why i had to speficy this again...but it complained if i didnt...i guess it copied buffer into the stringstream rather than links to it, so a change in buffer is not a change in the stringstream...wonder if there is a pointer equivalent
    mystream >> mybuf >> myint;
    buffer=mybuf;
    //cout << buffer;
    if (buffer=="button_b"){ //comparing the string to see which of these the string is.
    data.buttons.button_b=myint;
    //printf("button_b\n");
    }
    else if (buffer=="button_a"){
    data.buttons.button_a=myint;
    }
    ...
    }
    }
    if(buffer=="[acc]"){//repeat for the acc block...and the nunchaku block and the classic control block, and the ir block, etc as they become added. config options like  can be added, so you can set up head tracking options in there, you can set one time config options like "invert true" which could invert the mouse, all these would need a block like this but its mostly copy paste work.
    data.usemodifiers=1;
    while(buffer!="[/acc]"){
    fgets(mybuf,80,myfile);
    buffer=mybuf;
    int myint;
    istringstream mystream(buffer);
    mystream >> mybuf >> myint;
    buffer=mybuf;
    if (buffer=="modifier1"){
    data.buttons.modifier1=myint;
    }
    ...
    else if(buffer =="[/acc]"){//this was just here so the else would work since it still reads this before breaking.
    }
    else{
    data.usemodifiers=0; // this is here so if someone puts in an acc block with nothing in it, there is no reason to use modifiers, so this will disable them.
    data.modifiers=0;
    }
    }
    }
    }
    fclose(myfile);
    }
    this worked pretty well. i just had to remove the "=" and just use a space, so it was:
    "button_a number\n"
    by doing that i could read in any order and i could break it into segments according to block (so if there wasnt an acc block but after there was a nunchaku block there would be no problem ^_^)

    as of current its working pretty well, i have done all i can without ir an extension so i put what i had on sourceforge (though i am using a public terminal as i have no internet at home so i couldnt use the cvs..i had to upload to megauploads...that might hurt my project...)

    anyhow you have been a great help. i will probably use this for reading all manner of config files.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
...