How can I read a form file of undefined data and store it in a structure

I have a problem reading data from a file.

File:

0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
23 liu zhengzhi 90
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0
0   0.0

      

in the file you are not sure what is one line 0 0.0

or int char* char* double

, and I need to read it in structure.

Structure:

typedef struct Credit
{
    char first_name[20]="";
    char last_name[20]="";
    int account_number=0;
    double balance=0.0;
}account;

      

How can i do this.

+3


source to share


4 answers


Given that there are only two cases, the parsing is simplified:

1) Open / read the file with fopen()


2) read line by line using fgets()

in a while loop 3) tokenize and store the elements of each new line with strtok()

(or strtok_r()

if any)
4) Use the number of tokens per line and test each line token for the content
5) For counting 2 tokens, skip line
6) For counting 4 tokens, convert 1st and 4th lines. ( atoi()

i atof()

)
7) Assign the analyzed values ​​to the structure elements.
8) close the file -fclose()

If there is the possibility of more than one row of data, then you might want the structure array to contain the data:



For the data file you showed, here is an example of a simple parsing procedure using the following steps:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

typedef struct Credit
{
    char first_name[20];
    char last_name[20];
    int account_number;
    double balance;
}ACCOUNT;

ACCOUNT account[10];

int main()
{
    char file[]={"C:\\Play\\account.txt"};
    char line[1024];
    char *token = {0};
    char delim[]={" \r\n\t"};
    int count, i, j;
    char array[4][80];//should be dynamically allocated, this for illustration

    FILE *fp = fopen(file, "r");
    if(fp)
    {
        j = 0;
        while (fgets(line, 1024, fp) != NULL)
        {
            token = strtok(line, delim);
            i = -1;
            while(token)
            {
                i++;
                strcpy(array[i], token);
                token = strtok(NULL, delim);
            }
            if(i==3)
            {
                account[j].account_number = atoi(array[0]);
                strcpy(account[j].first_name, array[1]);
                strcpy(account[j].last_name, array[2]);
                account[j].balance = atof(array[3]);
            }
            j++;//for next in array of account
            memset(array, 0, 320);
        }
        fclose(fp);
    }

    return 0;

}

      

+2


source


Parse the string multiple times as needed.

Use " %n"

to measure success. "%n"

indicates that the number of scans is being saved char

. Since it is the last one, it will only be changed after the scan is complete. It is also useful to look for extra junk on the line.

// Return number of fields successfully scanned 4, 2, 0
int ParseLine(account *dest, const char *src) {
  int n = 0;
  sscanf(src, "%d%19s%19s%lf %n", &dest->account_number, dest->first_name, 
     dest->last_name, &dest->balance, &n);
  // If all fields scanned and no extra garbage ...
  if (n && src[n] == 0) {
    return 4;
  }

  n = 0;
  sscanf(src, "%d%lf %n", &dest->account_number, &dest->balance, &n);
  if (n && src[n] == 0) {
    dest->first_name[0] = dest->last_name[0] = 0;
    return 2;
  }

  return 0; // nothing scanned
}

      




A more reliable test will ensure that it first/last

does not exceed 19 char

by testing if the longer name was readable.

  char first[20+1];
  first[19] = 0;
  char last[20+1];
  last[19] = 0;
  sscanf(src, "%d%20s%20s%lf %n", &dest->account_number, first, 
     last, &dest->balance, &n);
  // If all fields scanned, no extra garbage, no long name ...
  if (n && src[n] == 0 && first[19] == 0 && last[19] == 0) {
    strcpy(dest->first_name, first); 
    strcpy(dest->last_name, last); 
    return 4;
  }

      




Code can use "%d %19s %19s %lf %n"

instead "%d%19s%19s%lf %n"

. It may be more readable, but because "%d"

, "%s"

, "%f"

themselves consume a leading white space - it has no functional differences.

+2


source


1.) One way to do this. Use getline to read the line one by one. Reading returns the number of characters in the string. If the number of characters is 4 then skip it and if its 15 then store it in the structure as you wish

while ((read = getline(&line, &len, fp)) != -1) {
       if(read==15)
       {
        //store it in struct
       }
     }

      

2.) Another way is to use a string tokenizer, and if the token is 4, a struct.

There will be many ways to do this. The common baseline is that one line has more characters and the other lines have the same number of characters or words. Therefore, I would not name this undefined data. He installed a template for it.

0


source


the following code, compiles cleanly, works, and is the recommended way to read said file.

Not everyone will agree with the use of "continue"

#include <stdio.h>   // fopen, fclose, fgets
#include <stdlib.h>  // exit, EXIT_FAILURE
#include <string.h>  // memset

struct account
{
    char first_name[20];
    char last_name[20];
    int account_number;
    double balance;
};

#define MAX_LINE_LEN (1024)

int main( int argc, char* argv[])
{   
    if( 2 != argc )
    { // then missing file name argument
        printf( "usage: %s <accountFileName>\n", argv[0] );
        exit( EXIT_FAILURE );
    }

    // implied else, correct number of command line arguments

    printf( "processing file: %s\n", arg[1] );

    FILE *fp = NULL;

    if( NULL == (fp = fopen( argv[1], "r" ) ) )
    { // then fopen failed
        perror( "fopen for input file failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fopen successful

    struct account* accountList = NULL;
    char buffer[MAX_LINE_LEN] = { '\0' };
    int numAccounts = 0;

    while( fgets( buffer, sizeof buffer, fp ) )
    {
        if( '0' == buffer[0] ) // assumes no 'id' starts with 0
        { // then, 'this' record not of interest

            // clear for next loop iteration
            memset( buffer, 0x00, sizeof buffer );
            continue; // go to top of loop and get another record
        }

        // when get here, then record of interest

        // used so realloc failure does not lose pointer to allocated memory
        struct account* temp = NULL;

        if( NULL == (temp = realloc( accountList, sizeof( struct account )*numAccounts+1 ) ) )
        { // then malloc failed
            perror( "realloc failed" );

            printf( "realloc failed for record number: %d\n", numAccounts+1);
            free( accountList );
            fclose( fp );
            exit( EXIT_FAILURE );
        }

        // implied else, malloc successful

        accountList = temp;  // update pointer to allocated memory

        // clear the new account struct area
        memset( &(accountList[numAccounts]), 0x00, sizeof( struct account ) );

        // following assumes all records are properly formatted
        if( 4 != sscanf( buffer, "%d %19s %19s %lf", 
                        &accountList[numAccounts].account_number,
                         accountList[numAccounts].first_name,
                         accountList[numAccounts].last_name,
                        &accountList[numAccounts].balance ) )
        { // then sscanf failed
            perror( "sscanf for record parsing failed" );
            // add error handling here, following is suggested error handling
            printf( "sscanf/record parsing failed for record number: %d\n", numAccounts+1 );
            free( accountList );
            fclose (fp );
            exit( EXIT_FAILURE );
        }

        // implied else, sscanf successful

        // clear for next loop iteration
        memset( buffer, 0x00, sizeof buffer );
    } // end while loop

    // done with file, so close it
    fclose( fp );

    // --add processing of account list here--

    free( accountList );

    return(0);
} // end function: main

      

0


source







All Articles