2017-01-31 1 views
1

Я уже давно пытаюсь понять это, и мне кажется, что я должен быть рядом. В принципе, у меня есть файл данных, содержащий различные записи в стране, разделенные новыми строками. Каждая запись содержит поля, разделенные запятыми, из которых я пытаюсь извлечь некоторые из них.Как читать строку с запятой в текстовом файле и вставлять ее поля в массив указателей на структуру?

Например (в виде одной строки):

60, AFG, Афганистан, Азии, Южной и Центральной Азии, 652090,1919,22720000,45.9,5976, Afganistan/Afqanestan, Исламский Эмират, Мохаммад Омар, 1, AF

Каждая из этих линий будет составлять структуру. По сути, я хочу прочитать каждую из этих строк и вставить ее в массив указателей на структуру (так динамически). Мне также нужны только определенные поля. Когда я «маркирую» строку, мне нужны поля для кода, имени, популяции и жизни expec. соответственно:

AFG, Афганистан, 22720000, 45.

Моя мысль была использовать fgets(), чтобы прочитать каждую строку в файле, а в таНос петли() немного памяти для указателей, разметить на поля, которые я хочу, затем вставьте. Однако то, что я делаю, должно быть ошибочным, поскольку различные тесты, похоже, ничего не показывают в моем выпуске.

Вот моя работа до сих пор. Я был бы признателен за любую помощь.

#include "allheaders.h" // contains all common headers for personal use 

#define BUF_SIZE 512 
#define NUM_RECS 238 

typedef struct { 
    char code[4]; 
    char name[40]; 
    int population; 
    float lifeExpectancy; 
} Country; 

typedef Country *countryPtr; 

int main(int argc, const char* argv[]) { 

/* Opening the file */ 
FILE *filePtr; // pointer to file 
if ((filePtr = fopen("AllCountries.dat", "r")) == NULL) { // if couldn't open file 
    printf("Error opening file\n"); // error message 
    exit(1); 
} 

/* Reading the file */ 
char buffer[BUF_SIZE]; // buffer to read 
int index = 0; 
char *token; 
countryPtr *myCountries = malloc(sizeof(*myCountries) * NUM_RECS); 
for(int i = 0; i < NUM_RECS; ++i) { 
    myCountries[i] = malloc(sizeof(*myCountries[i])); 
} 

while (fgets(buffer, BUF_SIZE, filePtr) != NULL) { 

    token = strtok(buffer,","); 
    token = strtok(NULL, ","); 
    strcpy(myCountries[index]->code, token); 
    token = strtok(NULL, ","); 
    strcpy(myCountries[index]->name, token); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    myCountries[index]->population = atoi(token); 
    token = strtok(NULL, ","); 
    myCountries[index]->lifeExpectancy = atof(token); 
    //printf("%s", buffer); 
    index++; 
} 

printf("%s", myCountries[1]->code); // test? 
free(myCountries); 

}

+1

Предлагает Вам сделать некоторые основные отладки. С отладчиком или даже базовыми отладочными операциями печати. Например, проверьте «буфер» и «токен» после каждой строки цикла. – kaylum

+0

Для начала 'index' не инициализирован. Что-то вы должны уметь легко обнаружить с помощью некоторой базовой отладки, как было предложено. – kaylum

+0

Теперь, когда вы исправили проблему «индекса», следующая проблема заключается в том, что ваши буферы «code» недостаточно велики; 'char code [3];'. Строки в C завершают NUL. Таким образом, чтобы сохранить 3-строчную строку, вам нужно 4 'char's. Таким образом, у вас есть переполнение буфера, что приводит к Undefined Behavior. – kaylum

ответ

0

я взял другой подход к решению его на основе коды и файл данных. Я протестировал его. Он работает с файлом типа записи, который вы показывали. Надеюсь, это объяснит некоторые вещи и облегчит вашу работу и даст вам хорошее место для работы.

Я не люблю писать программы таким образом, чтобы их можно было предварительно подсчитать (отнимать много времени) или предварительно узнать количество записей в файле по общим принципам, за исключением, может быть, в редких случаях. Поэтому при чтении файлов я предпочитаю выделять память, когда я иду. Теперь, если есть большой файл и много данных, тогда вам нужно разработать лучшую схему управления памятью, чем хранить все в памяти. В какой-то момент вам лучше пойти с каким-то консервированным db-решением. MySQL, API, библиотека, парсер и т. Д., Но это должно работать для небольших файлов.

Обычно в C на UNIX, exit(0) означает успех, exit(-1) означает отказ. Кроме того, поскольку ваши коды стран были 3-х символов, поле для хранения он должен быть не менее 4 символов для завершающего «\ 0»

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <strings.h> 

#define MAXRECL 512 
#define MAXFIELDS 100 
#define MAXFIELDL 80 

// Field indicies 

#define COUNTRY_CODE 1 
#define COUNTRY_NAME 2 
#define POPULATION  7 
#define LIFE_EXPECTANCY 8 

#define CCMAX   3 
#define CNMAX   40 

typedef struct Country { 
    struct Country *next; 
    char code[CCMAX + 1]; // (Need room for trailing '\0') 
    char name[CNMAX + 1]; // (Need room for trailing '\0') 
    int population; 
    float lifeExpectancy; 
} country_t; 

country_t *countryRecords; 

int main(int argc, const char* argv[]) { 

    FILE *fp; 
    if ((fp = fopen("AllCountries.dat", "r")) == NULL) { 
     printf("Error opening file\n"); 
     exit(-1); 
    } 
    int totalCountries = 0; 
    char buf[MAXRECL]; 
    char fields[MAXFIELDS][MAXFIELDL]; 
    country_t *prev_country = NULL; 
    while (fgets(buf, MAXRECL, fp) != NULL) { 
     ++totalCountries;  
     country_t *country = calloc(sizeof(struct Country), 1); 
     if (country == NULL) { 
      fprintf(stderr, "Out of memory\n"); 
      exit(-1); 
     } 
     char *field = strtok(buf, ","); 
     int i = 0; 
     while(field != NULL) { 
      strncpy(fields[i++], field, MAXFIELDL); 
      field = strtok(NULL, ","); 
     }   
     strcpy(country->code, fields[COUNTRY_CODE]); 
     strcpy(country->name, fields[COUNTRY_NAME]); 
     country->population = atoi(fields[POPULATION]); 
     country->lifeExpectancy = atof(fields[LIFE_EXPECTANCY]); 

     if (countryRecords == NULL) 
      countryRecords = country; 
     else 
      prev_country->next = country; 
     prev_country = country; 
    } 
    printf("Total countries: %d\n", totalCountries); 

    country_t *country = countryRecords; 
    while(country != NULL) { 
     printf("%3s %30s Population: %7d Life Expectancy: %5.2f\n", 
      country->code, country->name, country->population, country->lifeExpectancy); 
     country_t *prev_country = country; 
     country = country->next; 
     free(prev_country); 
    } 
    printf("Done\n"); 
    exit(0); 
} 
+0

Также следует упомянуть, что я использовал 'calloc()' вместо 'malloc()', потому что 'calloc()' обнуляет память до того, как она вернет ее вам, 'malloc()' does not. – clearlight

1

Посмотрите следующее. В первом случае вам нужно будет сделать некоторые работы по улучшению области отмечен NYI

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdarg.h> 

#define BUF_SIZE 512 
#define NUM_RECS 238 

typedef struct { 
    char code[4]; // NYI - magic numbers 
    char name[41]; // NYI - magic numbers 
    int population; // NYI - what if atoi fails? 
    float lifeExpectancy; // NYI - what if atof fails? 
} Country; 

typedef Country* countryPtr; 

int main(int argc, const char* argv[]) { 
    /* Opening the file */ 
    FILE *filePtr; // pointer to file 
    if ((filePtr = fopen("a.txt", "r")) == NULL) { // if couldn't open file 
    printf("Error opening file\n"); // error message 
    exit(1); 
    } 

    /* Reading the file */ 
    char buffer[BUF_SIZE]; // buffer to read 
    int index=0; 
    char *token; // NYI - initial value 
    countryPtr* myCountries = calloc(NUM_RECS, sizeof(countryPtr)); 
    for(int i = 0; i < NUM_RECS; ++i) { 
    myCountries[i] = calloc(1, sizeof(Country)); 
    } 

    while (fgets(buffer, BUF_SIZE, filePtr) != NULL) { 
    // NYI - magic lengths/overflow strcpy targets 

    token = strtok(buffer,","); // NYI - This is probably not the best way to do this. At least fold into a loop. 
    token = strtok(NULL, ","); 
    strcpy(myCountries[index]->code, token); 
    token = strtok(NULL, ","); 
    strcpy(myCountries[index]->name, token); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 
    token = strtok(NULL, ","); 

    myCountries[index]->population = atoi(token); // NYI - atoi failure 
    token = strtok(NULL, ","); 
    myCountries[index]->lifeExpectancy = atof(token); // NYI - atof failure 
    printf("%s", buffer); 
    index++; 
    } 

    printf("%s\n", myCountries[0]->code); // test? NYI - need more proof 
    free(myCountries); // NYI - this is a sequence - need to free each of the new elements 
} 
+0

Вы правы насчет «магических чисел». Должны быть константы. Вы правы в 'atoi() и atof()' failing.Я не мог решить все в своем ответе, но [это] (http://stackoverflow.com/q/22865622/2079103) охватывает проблему с ошибкой числового преобразования. Хорошо справиться с этим тоже. – clearlight