简体   繁体   English

如何读取不确定的数据表单文件并将其存储在struct中

[英]How can I read uncertain data form file and store it in struct

I have a trouble to read data from a file. 我无法从文件中读取数据。

The 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 not sure one line is 0 0.0 or int char* char* double and I need to read it to a struct. 在文件中你不确定一行是0 0.0还是int char* char* double我需要将它读取到结构中。

The struct: 结构:

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

How can I do it. 我该怎么做。

Given that there are only the two cases, parsing is simplified: 鉴于只有两种情况,解析简化了:

1) Open/read file using fopen() 1)使用fopen()打开/读取文件
2) read line by line using fgets() in while loop 2)使用fgets()循环中的fgets()逐行读取
3) tokenize and store elements of each new line using strtok() (or strtok_r() if threaded) 3)使用strtok() 标记化并存储每个新行的元素strtok_r()如果是线程的话, strtok_r()
4) Use count of tokens per line, and test each string token for contents 4)每行使用令牌数,并测试每个字符串令牌的内容
5) For counts of 2 tokens, skip line 5)对于2个令牌的计数,跳过线
6) For counts of 4 tokens, convert 1st & 4th strings. 6)对于4个令牌的计数,转换第1和第4个字符串。 ( atoi() & atof() ) atoi()atof()
7) Assign parsed values to struct members. 7)将解析后的值分配给struct成员。
8) close file - fclose() 8)关闭文件 - fclose()

If there is a possibility of more than 1 data line, then you might want an array of struct to contain data: 如果有可能存在多于1个数据行,那么您可能需要一个包含数据的struct数组:

For the data file you have shown, here is a very simple parsing routine example using these 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;

}

Parse the string, multiple times if needed. 如果需要,可以多次解析字符串。

Use " %n" to determine success. 使用" %n"确定成功。 "%n" specifies to save the number of char scanned. "%n"指定保存扫描的char数。 Since it is last, it will only be changed if the scan was complete. 由于它是最后一个,因此只有在扫描完成后才会更改。 Useful for looking for extra garbage on the line too. 也适用于在线上寻找额外的垃圾。

// 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 robust test would insure first/last do not exceed 19 char by testing if a longer name was readable. 更强大的测试可以通过测试更长的名称是否可读来确保first/last不超过19个char

  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 could use "%d %19s %19s %lf %n" instead of "%d%19s%19s%lf %n" . 代码可以使用"%d %19s %19s %lf %n"而不是"%d%19s%19s%lf %n" It may be more readable, but since "%d" , "%s" , "%f" consume leading white-space by themselves anyways - it makes no functional difference. 它可能更具可读性,但由于"%d""%s""%f"本身消耗领先的空白区域 - 它没有任何功能差异。

1.) One way to do it.Use getline to read the line one by one. 1.)一种方法。使用getline逐行读取行。 Read returns the number of character in a line. Read返回一行中的字符数。 If the number of characters is 4 then skip it and if its 15 then store it in struct as you wish 如果字符数为4,则跳过它,如果它的15,则根据需要将其存储在struct中

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

2.) Another way is use string tokenizer and if the token is 4 store it in struct. 2.)另一种方法是使用字符串标记化器,如果标记是4,则将其存储在struct中。

There will be many ways to do this. 有很多方法可以做到这一点。 Common baseline is that one line has more characters and other lines have same number of characters or words. 共同基线是一行具有更多字符,而其他行具有相同数量的字符或单词。 So I wouldn't call this uncertain data. 所以我不会称这种不确定的数据。 It has set pattern to it . 它为它设定了模式。

the following code, cleanly compiles, works and is a suggested method of reading the indicated 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM