Как получить альтернативную строку из файла и сохранить ее как строку в структуре? - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть файл, который нужно прочитать по коду.Файл показан ниже.Самая первая строка файла содержит одно целое число, указывающее количество записей журнала в файле.Мне нужно написать программу на C, чтобы прочитать файл и сохранить содержимое в динамически распределенном массиве структур.

4
12/04/2010 
Interview went well I think, though was told to wear shoes. 
18/04/2010 
Doc advised me to concentrate on something... I forget. 
03/05/2010
Was asked today if I was an art exhibit. 
19/05/2010 
Apparently mudcakes not made of mud, or angry wasps.

Мне удалось strtok () день, месяц и год, чтобы сохранить в моей структуре, однако я застрял в сохранении строк в моих структурах.Вот мой код для strtok (),

FILE* file=fopen("struct.txt","r");
        if (file==NULL){
            perror("Error opening file\n.");}
            else {
                fscanf(file,"%d",&size);
                 res=(Diary*)malloc(size*sizeof(Diary));
                 fscanf(file,"%*[^\n]");
while(fgets(day,1024,file)!= NULL){
    oken=strtok(day,"/");
    h[i]=atoi(oken);          */h[i] is my day
    oken=strtok(NULL,"/");
    fre[i]=atoi(oken);        */fre[i] is the month
    oken=strtok(NULL,"/");
    re[i]=atoi(oken);          */ re[i] is the year
    okena=strtok(day,"\n");
    strcpy(rese[i],okena);    */i had declared rese as char rese[1024]
    printf("%s",okena);
    i++;
   }

, программа не работает с этим strcpy (), когда я его запускаю, он продолжает падать.Однако, если я удалю strcpy (), он напечатает следующим образом:

12
Interview went well I think, though was told to wear shoes. 
18
Doc advised me to concentrate on something... I forget. 
03
Was asked today if I was an art exhibit. 
19
Apparently mudcakes not made of mud, or angry wasps.

, это не те строки, которые я хочу сохранить в своей структуре.Я застрял в том, как хранить строки в структуре.Моя структура

typedef struct journal{
int day;
int month;
int year;
char entry[1024];
} Diary;

Любые добрые души могут сказать мне, что не так?

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Ваша проблема представляет классическую проблему: «Как мне прочитать и выделить для Х числа что-то, когда я не знаю, сколько заранее?»На самом деле это более простой вариант вопроса, потому что вы можете прочитать число X в первой строке вашего файла данных.

(что упрощает задачу до одного размещения структур X после прочтения первой строки - в противном случае вам нужно будет отслеживать текущее количество выделенных структур и realloc при необходимости)

Для начала я бы рекомендовал не создавать char entry[1024]; в вашей структуре по двум причинам: во-первых, автоматическое хранилище для entry создается в стеке, а большой дневник может легко StackOverflow ... Во-вторых, это просто расточительно.Если целью является динамическое распределение, выделите только хранилище, необходимое для каждого entry.Вы можете объявить один буфер из 1024 символов для использования в качестве буфера чтения, но затем выделить только strlen (buf) + 1 символ для хранения записи (после обрезки включенного '\n' из записи).

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

Соединяя эти фрагменты и предоставляя дополнительную информацию в комментариях ниже, вы можете сделать что-то вроде следующего:

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

typedef struct journal {
    int day,
        month,
        year;
    char *entry;    /* declare a pointer, allocate only need No. of chars */
} diary_t;

#define MAXLENGTH 1024  /* max read buf for diary entry */

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

    size_t entries = 0, i, n = 0;
    char buf[MAXLENGTH] = "";
    diary_t *diary = NULL;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* read first line, parse number of entries */
    if (!(fgets (buf, MAXLENGTH, fp)) ||        /* validate read */
        sscanf (buf, "%zu", &entries) != 1) {   /* validate conversion */
        fputs ("error: failed to read 1st line.\n", stderr);
        return 1;
    }

    /* allocate/validate entries number of diary_t */
    if (!(diary = calloc (entries, sizeof *diary))) {
        perror ("calloc-diary_pointers");
        return 1;
    }

    for (i = 0; i < entries; i++) { /* loop No. entries times */

        size_t len = 0;
        if (!fgets (buf, MAXLENGTH, fp)) {  /* read/validate date */
            fprintf (stderr, "error: failed to read date %zu.\n", i);
            return 1;
        }
        if (sscanf (buf, "%d/%d/%d",    /* parse into day, month, year */
                    &diary[i].day, &diary[i].month, &diary[i].year) != 3) {
            fprintf (stderr, "error failed to parse date %zu.\n", i);
            return 1;
        }

        if (!fgets (buf, MAXLENGTH, fp)) {  /* read entry */
            fprintf (stderr, "error: failed to read entry %zu.\n", i);
            return 1;
        }

        len = strlen (buf);                 /* get length */
        if (len && buf[len - 1] == '\n')    /* check last char is '\n' */
            buf[--len] = 0;                 /* overwrite with nul-character */
        else if (len == MAXLENGTH - 1) {    /* check entry too long */
            fprintf (stderr, "error: entry %zu exceeds MAXLENGTH.\n", i);
            return 1;
        }

        /* allocate/validate memory for entry */
        if (!(diary[i].entry = malloc ((len + 1)))) {
            perror ("malloc-diary_entry");
            fprintf (stderr, "error: memory exausted, entry[%zu].\n", i);
            break;  /* out of memory error, don't exit, just break */
        }
        strcpy (diary[i].entry, buf);   /* copy buf to entry */

        n++;    /* increment successful entry read */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (i = 0; i < n; i++) {   /* output diary entries */
        printf ("entry[%2zu]:  %2d/%2d/%4d - %s\n", i, diary[i].day,
                diary[i].month, diary[i].year, diary[i].entry);
        free (diary[i].entry);  /* don't forget to free entries */
    }
    free (diary);   /* don't forget to free diary */

    return 0;
}

( примечание: вы можете еще больше упростить код, используя POSIX getline() для чтения вместо фиксированного buf, и вы можете упростить выделение и копирование каждой записи в вашей структуре, используя strdup(), но ни один из них не гарантированно доступен для всех компиляторов - используйте их, если ваш компилятор поддерживает их, и переносимость везде не имеет значения. Также обратите внимание, что GNU gcc использует %zu в качестве спецификатора формата для size_t. Если выдля windoze измените значение на %lu)

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

$ cat dat/diary.txt
4
12/04/2010
Interview went well I think, though was told to wear shoes.
18/04/2010
Doc advised me to concentrate on something... I forget.
03/05/2010
Was asked today if I was an art exhibit.
19/05/2010
Apparently mudcakes not made of mud, or angry wasps.

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

$ ./bin/diary <dat/diary.txt
entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.

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

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

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

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

$ valgrind ./bin/diary <dat/diary.txt
==6403== Memcheck, a memory error detector
==6403== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6403== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6403== Command: ./bin/diary
==6403==
entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.
==6403==
==6403== HEAP SUMMARY:
==6403==     in use at exit: 0 bytes in 0 blocks
==6403==   total heap usage: 5 allocs, 5 frees, 309 bytes allocated
==6403==
==6403== All heap blocks were freed -- no leaks are possible
==6403==
==6403== For counts of detected and suppressed errors, rerun with: -v
==6403== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

( примечание: хранилище, необходимое для всех записей дневника (всего дневника), составляет всего 309-bytes, чтоменьше 1/10th требуемый объем памяти: char entry[1024];)

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


MS Windows

Поскольку у вас, похоже, возникают проблемы с окнами, приведенный выше код не содержит ничего, кроме %lu, заменяющего %zu (так как windows воспринимает %zu как литерал), скомпилированный на Win7 с более старой версией компилятора VS:

> cl /?
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

Компиляция

> cl /nologo /Wall /wd4706 /wd4996 /Ox /Foobj/diary /Febin/diary /Tc diary.c

( note: Я поставил свой.obj файлы в подкаталоге ./obj и мои двоичные исполняемые файлы в ./bin для поддержания моего исходного каталога в чистоте. Это цель /Foobj/diary и /Febin/diary выше)

Пример Use /Выход

> bin\diary.exe dat\diary.txt
entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.

Вы должны убедиться, что вы меняете каждый %zu на %lu, иначе вы не можете ожидать правильного вывода.Вы говорите, что изменили все на int, но фрагмент, который вы разместили в комментариях ниже, содержит %zu - это не будет работать в Windows.

Просмотрите все и дайте мне знать, если у вас есть дальнейшиевопросы.

0 голосов
/ 25 апреля 2018

следующий предложенный код:

  1. выполняет желаемую функциональность
  2. дает значимые имена "магическим" числам
  3. отделяет определение структуры от typedef для этой структуры
  4. обновлено / отредактировано с последними подробностями вопроса

и теперь предложенный код:

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

#define MAX_LINE_LEN 1024

struct journal
{
    int day;
    int month;
    int year;
    char entry[ MAX_LINE_LEN ];
};
typedef struct journal Diary;


int main( void )
{

    FILE* file=fopen("struct.txt","r");
    if ( !file )
    {
        perror("fopen failed");}
        exit( EXIT_FAILURE );
    }

    // implied else, fopen successful

    char line[ MAX_LINE_LEN ];
    int size;

    if( fgets( line, sizeof line, file ) )
    {
        if ( sscanf( line, "%d", size ) != 1 )
        {
            fprintf( stderr, "scanf for data count failed\m" );
            exit( EXIT_FAILURE );
        }

        // implied else, input of data count successful
    }

    else
    {
        perror( "fgets for data count failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets successful


    Diary myDiary[ size ];  // uses VLA (variable length array feature of C


    size_t i = 0;
    char *token = NULL;

    while( i < size && fgets( line, sizeof( line ), file) )
    {
        token = strtok( line, "/" );
        if( token )
        {
            myDiary[i].day = atoi( token );

            token = strtok( NULL, "/" );
            if( token )
            {
                myDiary[i].month = atoi( token );

                token = strtok( NULL, "/" );
                if( token )
                {
                    myDiary[i].year = atoi( token );

                    // input data directly into struct instance
                    fgets( myDiary[i].entry, MAX_LINE_LEN, file );
                }
            }
        }
        i++;
    }
}
...