Маркировка внешнего файла - PullRequest
0 голосов
/ 24 ноября 2018

Так что я застрял в том, как маркировать токен FIRST и поместить это значение в структуру.В моем случае я пытаюсь прочитать строки из файла, который выглядит следующим образом:

Формат TDV:

 TN     1424325600000   dn20t1kz0xrz    67.0    0.0  0.0     0.0    101872.0    262.5665
 TN     1422770400000   dn2dcstxsf5b    23.0    0.0  100.0   0.0    100576.0    277.8087
 TN     1422792000000   dn2sdp6pbb5b    96.0    0.0  100.0   0.0    100117.0    278.49207
 TN     1422748800000   dn2fjteh8e80    6.0     0.0  100.0   0.0    100661.0    278.28485
 TN     1423396800000   dn2k0y7ffcup    14.0    0.0  100.0   0.0    100176.0    282.02142 

Как вы можете видеть, есть TN, который указал код состояния.В моей функции ниже я должен быть в состоянии распознать, что строка предназначена для определенного состояния, и отправить ее в структуру.

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

void analyze_file(FILE *file, struct climate_info **states, int num_states)
{
    const int line_sz = 100;
    char line[line_sz];
    int counter = 0;
    char *token;

    while (fgets(line, line_sz, file) != NULL)
    {
        /* TODO: We need to do a few things here:
         *
         *       * Tokenize the line.
         *       * Determine what state the line is for. This will be the state
         *         code, stored as our first token.
         *       * If our states array doesn't have a climate_info entry for
         *         this state, then we need to allocate memory for it and put it
         *         in the next open place in the array. Otherwise, we reuse the
         *         existing entry.
         *       * Update the climate_info structure as necessary.
         */
        struct climate_info *states = malloc(sizeof(struct climate_info)*num_states);
        token = strtok(line," \n");
        strcpy(states->code, token);
        //printf("token: %s\n", token);

        while(token)
        {

            printf("token: %s\n", token);
            token = strtok(NULL, " \t");

        }
    }
    printf("%d\n",counter);

}

Вот моя структура, которую я определил:

struct climate_info
{
    char code[3];
    unsigned long num_records;
    long long millitime;
    char location[13];
     double humidity;
    double snow;
    double cloud;
    double lightning;
    long double pressure;
     double temperature;
};

Вот где я печатаю свои выходные данные, и именно здесь моя программа, похоже, не распознает, что делается в функции analy_file:

void print_report(struct climate_info *states[], int num_states)
{
    printf("States found: ");
    int i;
    for (i = 0; i < num_states; ++i)
    {
        if (states[i] != NULL)
        {
            struct climate_info *info = states[i];
            printf("%s", info->code);
        }
    }
    printf("\n");

Вывод должен выглядеть следующим образом: Найдено состояний: TN Яв состоянии маркировать мою строку и выводить каждый токен каждой строки, однако проблема заключается в том, когда я пытаюсь дать значения структуры.В моей строке в analysis_file: strcpy (states-> code, token);Я пытаюсь взять первый токен, который мне известен, это код состояния и передать его выделенному пространству, которое я создал из своей структуры.Как вы можете видеть из моей функции print_report, он, похоже, не распознает, что я отправляю значения в информацию о климате.У меня вопрос, как мне исправить мою функцию analy_file, не меняя функцию print_report?

1 Ответ

0 голосов
/ 24 ноября 2018

Кажется, что большая часть трудностей, с которыми вы сталкиваетесь, пытаясь понять, как использовать "TN", связана с вашей попыткой сохранить все данные, считанные в каждой строке, в отдельной структуре.Как упомянуто в комментариях, это может быть хорошо для чтения данных в базу данных, где база данных предоставляет возможность запрашивать все записи с помощью аббревиатуры состояний, но делает обработку данных немного более неудобной.Почему?

Когда вы сохраняете все записи как отдельную структуру, нет никакой связи между состоянием, к которому принадлежат данные, и информацией, хранящейся, кроме члена code структуры.Это означает, что если вы хотите найти или распечатать информацию, например, "TN", вы должны пройти через каждую отдельную структуру, проверяя, соответствует ли элемент code "TN".Подумайте о печати.Вы должны зацикливаться для каждого состояния, а затем зацикливаться на каждой структуре каждый раз, выбирая информацию для отдельного состояния для печати. ​​

Вместо сохранения каждой записи информации в качестве элемента в массиве записей, почему бы и нетиметь массив состояний, где каждое состояние содержит указатель на данные для этого состояния.Это сделало бы ваш num_records член более понятнымПосле этого вам просто придется перебрать свой массив состояний, проверить, является ли (num_records > 0), а затем распечатать информацию num_records для этого состояния, пропуская все состояния, в которых данные не были сохранены.Это обеспечивает гораздо более эффективный подход.

Например, требуется немного усилий, чтобы немного изменить ваши структуры, чтобы обеспечить связь между состоянием и данными, связанными с этим состоянием, например:

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

/* if you need constants, either #define them or use an enum */
enum { ABRV = 2, NDATA = 8, LOC = 13, NAME = 15, MAXC = 1024 };
...
typedef struct {            /* struct holding only climate data */
    long long millitime;
    char location[LOC];
    double humidity;
    double snow;
    double cloud;
    double lightning;
    long double pressure;
    double temperature;
} climate_t;

typedef struct {
    size_t  num_allocated,  /* track of how many data are allocated */
            num_records;
    climate_t *data;        /* a pointer to allocated block for data */
} statedata_t;

Но как соотнести чтение "TN" из файла, чтобы получить данные, хранящиеся в правильном состоянии?Вот где приходит таблица поиска. Если бы у вас была другая простая структура, содержащая имя и аббревиатуру состояния, вы могли бы создать простой массив структуры, содержащий информацию об аббревиатуре, и когда вы читаете, например, "TN" из файла, вы можетепросто "посмотрите" индекс , где "TN" живет в вашем массиве с сокращениями, а затем используйте этот index , чтобы сохранить информацию из этой строки в соответствующем индексе в вашем statedata_t массиве.

Поскольку ваш "lookup-array" будет постоянным, он может быть просто глобальным, который объявлен как const.Если вы используете несколько исходных файлов, вы можете просто определить массив в одном файле и объявить его extern в остальных файлах, которые в нем нуждаются.Так как бы вы это определили?Сначала объявите строку с нужной вам информацией в поиске (имя и аббревиатуру состояния), а затем объявите их постоянный массив, инициализирующий имя и аббревиатуру для каждого, например,

typedef struct {
    char name[NAME+1],
        abrv[ABRV+1];
} stateabrv_t;
...
const stateabrv_t state[]  =  { { "Alabama",        "AL" },
                                { "Alaska",         "AK" },
                                { "Arizona",        "AZ" },
                                { "Arkansas",       "AR" },
                                { "California",     "CA" },
                                { "Colorado",       "CO" },
                                { "Connecticut",    "CT" },
                                { "Delaware",       "DE" },
                                { "Florida",        "FL" },
                                { "Georgia",        "GA" },
                                { "Hawaii",         "HI" },
                                { "Idaho",          "ID" },
                                { "Illinois",       "IL" },
                                { "Indiana",        "IN" },
                                { "Iowa",           "IA" },
                                { "Kansas",         "KS" },
                                { "Kentucky",       "KY" },
                                { "Louisiana",      "LA" },
                                { "Maine",          "ME" },
                                { "Maryland",       "MD" },
                                { "Massachusetts",  "MA" },
                                { "Michigan",       "MI" },
                                { "Minnesota",      "MN" },
                                { "Mississippi",    "MS" },
                                { "Missouri",       "MO" },
                                { "Montana",        "MT" },
                                { "Nebraska",       "NE" },
                                { "Nevada",         "NV" },
                                { "New Hampshire",  "NH" },
                                { "New Jersey",     "NJ" },
                                { "New Mexico",     "NM" },
                                { "New York",       "NY" },
                                { "North Carolina", "NC" },
                                { "North Dakota",   "ND" },
                                { "Ohio",           "OH" },
                                { "Oklahoma",       "OK" },
                                { "Oregon",         "OR" },
                                { "Pennsylvania",   "PA" },
                                { "Rhode Island",   "RI" },
                                { "South Carolina", "SC" },
                                { "South Dakota",   "SD" },
                                { "Tennessee",      "TN" },
                                { "Texas",          "TX" },
                                { "Utah",           "UT" },
                                { "Vermont",        "VT" },
                                { "Virginia",       "VA" },
                                { "Washington",     "WA" },
                                { "West Virginia",  "WV" },
                                { "Wisconsin",      "WI" },
                                { "Wyoming",        "WY" } };

const int nstates = sizeof state / sizeof *state;

Теперь у вас есть простойДвухсторонний поиск.Учитывая имя или сокращение состояния, вы можете вернуть index , где оно находится в массиве.Кроме того, по имени вы можете искать аббревиатуру или по аббревиатуре, вы можете искать имя.

Простая функция поиска, возвращающая индекс, может выглядеть так:

/* simple lookup function, given a code s, return index for state
 * in array of statedata_t on success, -1 otherwise.
 */
int lookupabrv (const char *s)
{
    int i = 0;

    for (; i < nstates; i++)
        if (state[i].abrv[0] == s[0] && state[i].abrv[1] == s[1])
            return i;

    return -1;
}

Теперь, когда выВы можете найти index с учетом сокращения, используя вашу глобальную таблицу поиска, вы можете собрать остаток вашей обработки данных в main(), объявив массив 50 statedata_t, например,

int main (int argc, char **argv) {

    char buf[MAXC]; /* line buffer */
    /* array of 50 statedata_t (one for each state) */
    statedata_t stdata[sizeof state / sizeof *state] = {{.num_records = 0}};

Теперь вы готовы начать чтение из вашего файла, и insert_data для правильного состояния, основанного на сокращении, считанном из файла.Простой способ приблизиться к чтению - это прочитать "TN" в отдельный массив, а затем прочитать данные о климате во временную структуру типа climate_t, которую можно передать в функцию insert_data,В вашей функции insert_data вы просто просматриваете индекс (выделяете или перераспределяете для data при необходимости), а затем назначаете временную структуру данных для блока памяти для вашего state.data.Например, ваша функция insert_data может выглядеть примерно так:

/* insert data for state given code and climate_t containing data */
int insert_data (statedata_t *st, const char *code, climate_t *data)
{
    int index = lookupabrv (code);  /* lookup array index */

    if (index == -1)    /* handle error */
        return 0;

    if (!st[index].num_allocated) { /* allocate data if not allocated */
        st[index].data = malloc (NDATA * sizeof *st[index].data);
        if (!st[index].data) {
            perror ("malloc-st[index].data");
            return 0;
        }
        st[index].num_allocated = NDATA;
    }

    /* check if realloc needed */
    if (st[index].num_records == st[index].num_allocated) {
        /* realloc here, update num_allocated */
    }

    /* add data for proper state index */
    st[index].data[st[index].num_records++] = *data;

    return 1;   /* return success */
}

Вот и все.То, как вы анализируете информацию из каждой строки, зависит от вас, но для целей моего примера, учитывая ваши данные для примера, я просто использовал sscanf для простоты.В целом, вы можете сделать что-то вроде следующего:

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

/* if you need constants, either #define them or use an enum */
enum { ABRV = 2, NDATA = 8, LOC = 13, NAME = 15, MAXC = 1024 };

typedef struct {
    char name[NAME+1],
        abrv[ABRV+1];
} stateabrv_t;

typedef struct {            /* struct holding only climate data */
    long long millitime;
    char location[LOC];
    double humidity;
    double snow;
    double cloud;
    double lightning;
    long double pressure;
    double temperature;
} climate_t;

typedef struct {
    size_t  num_allocated,  /* track of how many data are allocated */
            num_records;
    climate_t *data;        /* a pointer to allocated block for data */
} statedata_t;

const stateabrv_t state[]  =  { { "Alabama",        "AL" },
                                { "Alaska",         "AK" },
                                { "Arizona",        "AZ" },
                                { "Arkansas",       "AR" },
                                { "California",     "CA" },
                                { "Colorado",       "CO" },
                                { "Connecticut",    "CT" },
                                { "Delaware",       "DE" },
                                { "Florida",        "FL" },
                                { "Georgia",        "GA" },
                                { "Hawaii",         "HI" },
                                { "Idaho",          "ID" },
                                { "Illinois",       "IL" },
                                { "Indiana",        "IN" },
                                { "Iowa",           "IA" },
                                { "Kansas",         "KS" },
                                { "Kentucky",       "KY" },
                                { "Louisiana",      "LA" },
                                { "Maine",          "ME" },
                                { "Maryland",       "MD" },
                                { "Massachusetts",  "MA" },
                                { "Michigan",       "MI" },
                                { "Minnesota",      "MN" },
                                { "Mississippi",    "MS" },
                                { "Missouri",       "MO" },
                                { "Montana",        "MT" },
                                { "Nebraska",       "NE" },
                                { "Nevada",         "NV" },
                                { "New Hampshire",  "NH" },
                                { "New Jersey",     "NJ" },
                                { "New Mexico",     "NM" },
                                { "New York",       "NY" },
                                { "North Carolina", "NC" },
                                { "North Dakota",   "ND" },
                                { "Ohio",           "OH" },
                                { "Oklahoma",       "OK" },
                                { "Oregon",         "OR" },
                                { "Pennsylvania",   "PA" },
                                { "Rhode Island",   "RI" },
                                { "South Carolina", "SC" },
                                { "South Dakota",   "SD" },
                                { "Tennessee",      "TN" },
                                { "Texas",          "TX" },
                                { "Utah",           "UT" },
                                { "Vermont",        "VT" },
                                { "Virginia",       "VA" },
                                { "Washington",     "WA" },
                                { "West Virginia",  "WV" },
                                { "Wisconsin",      "WI" },
                                { "Wyoming",        "WY" } };

const int nstates = sizeof state / sizeof *state;

/* simple lookup function, given a code s, return index for state
 * in array of statedata_t on success, -1 otherwise.
 */
int lookupabrv (const char *s)
{
    int i = 0;

    for (; i < nstates; i++)
        if (state[i].abrv[0] == s[0] && state[i].abrv[1] == s[1])
            return i;

    return -1;
}

/* insert data for state given code and climate_t containing data */
int insert_data (statedata_t *st, const char *code, climate_t *data)
{
    int index = lookupabrv (code);  /* lookup array index */

    if (index == -1)    /* handle error */
        return 0;

    if (!st[index].num_allocated) { /* allocate data if not allocated */
        st[index].data = malloc (NDATA * sizeof *st[index].data);
        if (!st[index].data) {
            perror ("malloc-st[index].data");
            return 0;
        }
        st[index].num_allocated = NDATA;
    }

    /* check if realloc needed */
    if (st[index].num_records == st[index].num_allocated) {
        /* realloc here, update num_allocated */
    }

    /* add data for proper state index */
    st[index].data[st[index].num_records++] = *data;

    return 1;   /* return success */
}

/* print states with data collected */
void print_data (statedata_t *st)
{
    int i = 0;

    for (; i < nstates; i++) {
        if (st[i].num_records) {
            size_t j = 0;
            printf ("\n%s\n", state[i].name);
            for (; j < st[i].num_records; j++)
                printf ("  %13lld  %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
                        "%8.4f\n",
                        st[i].data[j].millitime, st[i].data[j].location,
                        st[i].data[j].humidity, st[i].data[j].snow,
                        st[i].data[j].cloud, st[i].data[j].lightning,
                        st[i].data[j].pressure, st[i].data[j].temperature);
        }
    }
}

/* free allocated memory */
void free_data (statedata_t *st)
{
    int i = 0;

    for (; i < nstates; i++)
        if (st[i].num_records)
            free (st[i].data);
}

int main (int argc, char **argv) {

    char buf[MAXC]; /* line buffer */
    /* array of 50 statedata_t (one for each state) */
    statedata_t stdata[sizeof state / sizeof *state] = {{.num_records = 0}};
    /* read from file given as argument (or stdin if none given) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line of data */
        char code[ABRV+1] = "";         /* declare storage for abriviation */
        climate_t tmp = { .millitime = 0 }; /* declare temp stuct for data */

        /* simple parse of data with sscanf */
        if (sscanf (buf, "%2s %lld %12s %lf %lf %lf %lf %Lf %lf", code,
            &tmp.millitime, tmp.location, &tmp.humidity, &tmp.snow,
            &tmp.cloud, &tmp.lightning, &tmp.pressure, &tmp.temperature)
            == 9) {
            if (!insert_data (stdata, code, &tmp))  /* insert data/validate */
                fprintf (stderr, "error: insert_data failed (%s).\n", code);
        }
        else    /* handle error */
            fprintf (stderr, "error: invalid format:\n%s\n", buf);
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    print_data (stdata);    /* print data */
    free_data (stdata);     /* free allocated memory */

    return 0;
}

Пример входного файла

$ cat dat/state_climate.txt
 TN     1424325600000   dn20t1kz0xrz    67.0    0.0  0.0     0.0    101872.0    262.5665
 TN     1422770400000   dn2dcstxsf5b    23.0    0.0  100.0   0.0    100576.0    277.8087
 TN     1422792000000   dn2sdp6pbb5b    96.0    0.0  100.0   0.0    100117.0    278.49207
 TN     1422748800000   dn2fjteh8e80    6.0     0.0  100.0   0.0    100661.0    278.28485
 TN     1423396800000   dn2k0y7ffcup    14.0    0.0  100.0   0.0    100176.0    282.02142

Пример использования / Вывод

$ ./bin/state_climate <dat/state_climate.txt

Tennessee
  1424325600000  dn20t1kz0xrz  67.0   0.0   0.0   0.0 101872.0 262.5665
  1422770400000  dn2dcstxsf5b  23.0   0.0 100.0   0.0 100576.0 277.8087
  1422792000000  dn2sdp6pbb5b  96.0   0.0 100.0   0.0 100117.0 278.4921
  1422748800000  dn2fjteh8e80   6.0   0.0 100.0   0.0 100661.0 278.2849
  1423396800000  dn2k0y7ffcup  14.0   0.0 100.0   0.0 100176.0 282.0214

Использование памяти / проверка ошибок

В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей относительно любого блока памятираспределены: (1) всегда сохранять указатель на начальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда он больше не нужен.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец,чтобы подтвердить, что вы освобождаете всю выделенную память.

Для Linux valgrind - нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через него.

$ valgrind ./bin/state_climate <dat/state_climate.txt
==6157== Memcheck, a memory error detector
==6157== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6157== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6157== Command: ./bin/state_climate
==6157==

Tennessee
  1424325600000  dn20t1kz0xrz  67.0   0.0   0.0   0.0 101872.0 262.5665
  1422770400000  dn2dcstxsf5b  23.0   0.0 100.0   0.0 100576.0 277.8087
  1422792000000  dn2sdp6pbb5b  96.0   0.0 100.0   0.0 100117.0 278.4921
  1422748800000  dn2fjteh8e80   6.0   0.0 100.0   0.0 100661.0 278.2849
  1423396800000  dn2k0y7ffcup  14.0   0.0 100.0   0.0 100176.0 282.0214
==6157==
==6157== HEAP SUMMARY:
==6157==     in use at exit: 0 bytes in 0 blocks
==6157==   total heap usage: 1 allocs, 1 frees, 768 bytes allocated
==6157==
==6157== All heap blocks were freed -- no leaks are possible
==6157==
==6157== For counts of detected and suppressed errors, rerun with: -v
==6157== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

Посмотрите вещии подумайте, почему изменение в структурах имеет смысл.Дайте мне знать, если у вас есть какие-либо вопросы.

...