Почему пятые данные не могут работать? Что-то не так с fscanf ()? - PullRequest
0 голосов
/ 20 декабря 2018
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
int CurrentCnt = 0;

#define MAX_ID_LEN 30
#define MAX_NAME_LEN 30
#define MAX_PRICE_LEN 30
#define MAX_DISCOUNT_LEN 30
typedef struct {
    char    goods_id[MAX_ID_LEN];
    char    goods_name[MAX_NAME_LEN];
    int     goods_price;
    char    goods_discount[MAX_DISCOUNT_LEN];
    int     goods_amount;
    int     goods_remain;
} GoodsInfo;

//--------------------------------------------------------------------
//define node
//--------------------------------------------------------------------
typedef struct node
{
    GoodsInfo data;
    struct node *next;
} GoodsList;

bool check_nullfile(void)
{
    FILE *fp = fopen("goodsinfo.txt", "r");
    //file not exist
    if (!fp) {
        printf("no files found.\n");
        FILE *fp = fopen("goodsinfo.txt", "w");
        fclose(fp);

        return false;
    }
        //file already exist
    else {
        int temp;
        //res for try to read file if file null feof() can't determine             
whether file is null or not
        int res = fscanf(fp, "%d", &temp);
        fclose(fp);
        if (res <= 0)
            return false;
        else
            return true;
    }
}

void info_init(GoodsList **L) {

    if(check_nullfile())
    {
        FILE * fp;
        fp=fopen("goodsinfo.txt", "r");
        GoodsList *listptr;
        while (1)
        {
            if (feof(fp)) break;
            listptr=(GoodsList*)malloc(sizeof(GoodsList));
            listptr->next=(*L)->next;
            (*L)->next=listptr;
            fscanf(fp,"%4s\t",listptr->data.goods_id);
            fscanf(fp,"%4s\t",listptr->data.goods_name);
            fscanf(fp,"%d\t",&(listptr->data.goods_price));
            fscanf(fp,"%s\t",listptr->data.goods_discount);
            fscanf(fp,"%d\t",&(listptr->data.goods_amount));
            fscanf(fp,"%d",&(listptr->data.goods_remain));
/*          printf("%c%c%c%c\n",listptr->data.goods_id[0],listptr-                
>data.goods_id[1],listptr->data.goods_id[2],listptr->data.goods_id[3]);
            printf("%c%c%c%c\n",listptr->data.goods_name[0],listptr-        
>data.goods_name[1],listptr->data.goods_name[2],listptr- 
>data.goods_name[3]);
            printf("%d\n",listptr->data.goods_price);
            printf("%c%c%c%c\n",listptr->data.goods_discount[0],listptr- 
>data.goods_discount[1],listptr->data.goods_discount[2],listptr- 
>data.goods_discount[3]);
            printf("%d\n",listptr->data.goods_amount);
            printf("%d\n",listptr->data.goods_remain); these are my 
testing*/
            CurrentCnt++;
            if (feof(fp)) break;
        }
        fclose(fp);
    }

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

int main (void)
{
    GoodsList **L;
    L=(GoodsList**)malloc(sizeof(GoodsList*));
    info_init(L);
    return 0;
}

У меня есть тестовый файл, который включает пять групп файлов.Когда я запускаю эту программу, пятая группа данных не может быть выведена правильно.Мои данные тестирования:

1000    new1    90  0.9 90  80
1001    new2    80  0.9 80  80
1002    new3    70  0.8 10  10
1003    new4    88  0.8 70  80
1004    new5    100 0.8 70  80

Почему position4 работает, а другие не могут? Position1 2 3 сделает CurrentCnt равным 6, но 5. В последнем цикле программа ничего не получает, но почему она не выскочитцикл?Моя новая бедная программа:

void info_init(GoodsList **L) {

if(check_nullfile())
{
    FILE * fp;
    fp=fopen("goodsinfo.txt", "r");
    GoodsList *listptr;
    while (1/*feof(fp) position1*/)
    {
        //if (feof(fp)) break; //position2
        listptr=malloc(sizeof(*listptr));
        listptr->next=*L;
        *L=listptr;
        //if (feof(fp)) break;//position3
        fscanf(fp,"%s\t",listptr->data.goods_id);
        fscanf(fp,"%s\t",listptr->data.goods_name);
        fscanf(fp,"%d\t",&(listptr->data.goods_price));
        fscanf(fp,"%s\t",listptr->data.goods_discount);
        fscanf(fp,"%d\t",&(listptr->data.goods_amount));
        fscanf(fp,"%d",&(listptr->data.goods_remain));
        //if (feof(fp)) break;//position4
        CurrentCnt++;
    }
    fclose(fp);
}

printf("%d\n", CurrentCnt);

}

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

То, как вы учли свой код (1) для обработки списка;и (2) добавить данные в список, он настолько запутан и настолько не проверен, что неудивительно, что у вас возникают трудности с его сортировкой.

Считывание данных некорректно с самого начала.См. Почему while (! Feof (file)) всегда неверно? .Кроме того, вы не можете проверить один возврат fscanf.Если одно чтение не удалось, вы вызываете неопределенное поведение , слепо используя неопределенное значение (и, вероятно, каждое значение с этого момента будет неопределенным).На этом этапе все ставки закончились.

Тем не менее, вам следует рекомендовать использовать #define для определения необходимых вам констант, но тогда вы не сможете защитить границы своего массива, включив field-width модификаторы со всеми char* спецификаторами преобразования.Пока вы #define константы, вы затем поворачиваетесь и жестко кодируете свое имя файла.Не делай этого.Передайте ваше имя файла в качестве аргумента вашей программе или запросите его ввод.

Всякий раз, когда обрабатываете «строку данных», вы должны использовать строчно-ориентированную функцию вводанапример, fgets или POSIX getline, а затем проанализируйте нужные значения из строки данных.Это обеспечивает возможность отдельной проверки (1) чтения данных из файла;и (2) анализ значений из результирующего буфера.Если по какой-либо причине возникает ошибка в формате, ваш анализ не удастся, и вы можете просто continue цикл чтения и прочитать следующую строку - без риска Неопределенное поведение .

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

Не смешивайте данные чтения ссписок операций.Вместо этого прочитайте и передайте данные, необходимые для вашей функции append().Хотя это в значительной степени зависит от вас, неспособность разделить чтение / анализ и добавление приведет к тому, что функции списка не будут использоваться повторно.

Например, вместо того, чтобы пытаться читать и анализировать данные в вашей функции списка, откройтеваш файл в main() и проанализируйте данные во временную goodsinfo 1 структуру и передайте адрес списка и указатель на ваши временные данные в функцию добавления.Вы можете сделать что-то похожее на следующее для чтения и анализа данных и передачи необходимых значений в вашу функцию:

int main (int argc, char **argv)
{
    char buf[MAXC];             /* read buffer */
    size_t linecnt = 0;         /* line counter */
    goodslist *list = NULL;     /* linked list pointer */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

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

    while (fgets (buf, MAXC, fp)) {         /* read each line of data */
        goodsinfo tmp = { .goods_id = "" }; /* temp data struct */
        /* parse and validate data in buf (can be separate function) */
        if (sscanf (buf, "%29s %29s %d %29s %d %d", tmp.goods_id, 
                    tmp.goods_name, &tmp.goods_price, tmp.goods_discount, 
                    &tmp.goods_amount, &tmp.goods_remain) != 6) {
            fprintf (stderr, "error: invalid format line %zu.\n", linecnt+1);
            continue;
        }
        if (!append (&list, &tmp))      /* append to list/validate */
            break;
    }
    if (fp != stdin)    /* close file if not stding */
        fclose (fp);

    prn_list (list);    /* print list */
    free_list (list);   /* free list data */

    return 0;
}

( note: программа берет имя файла для чтения данныхиз первого аргумента или читает по умолчанию stdin, если имя файла не указано. Также обратите внимание, что вы объявляете список как указатель на goodslist, а не указатель на указатель на goodslist)

Когда ваши данные прочитаны и проанализированы, ваша функция append() просто должна выделить хранилище для data и выделить хранилище для нового узла списка.Он имеет только два случая для обработки (1) список пуст?- оставить node->next = NULL;в противном случае (2) установите node->next равным текущему адресу списка, прежде чем назначать адрес для вашего нового узла в качестве нового адреса списка, чтобы связать вместе ваши узлы, например,

/* function to allocate goodslist node and append allocated goodsinfo 
 * data to list. Takes address of list pointer and pointer to goodsinfo data 
 * to append to list. Returns pointer new node on success, NULL otherwise.
 */
goodsinfo *append (goodslist **l, goodsinfo *tmp)
{
    goodsinfo *data = malloc (sizeof *data);    /* allocate/validate data */
    if (!data) {
        perror ("malloc-data");
        return NULL;
    }
    *data = *tmp;   /* fill allocated data block with tmp values */

    /* allocate/validate list node */
    goodslist *node = malloc (sizeof *node);
    if (!node) {
        perror ("malloc-node");
        free (data);
        return NULL;
    }
    node->data = data;  /* initialize data and set next NULL */
    node->next = NULL;

    if (*l) /* if list exists, chain next to list */
        node->next = *l;

    return ((*l = node)->data); /* assign new node as list, return data */
}

Сложив все вместе, вы могли бы что-то сделатьнапример:

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

#define MAX_ID_LEN          30
#define MAX_NAME_LEN        MAX_ID_LEN
#define MAX_PRICE_LEN       MAX_NAME_LEN
#define MAX_DISCOUNT_LEN    MAX_PRICE_LEN
#define MAXC                1024    /* read buffer size (don't skimp) */

typedef struct {
    char    goods_id[MAX_ID_LEN];
    char    goods_name[MAX_NAME_LEN];
    int     goods_price;
    char    goods_discount[MAX_DISCOUNT_LEN];
    int     goods_amount;
    int     goods_remain;
} goodsinfo;

typedef struct goodslist {
    goodsinfo *data;        /* make data a pointer and allocate */
    struct goodslist *next;
} goodslist;

/* bool check_nullfile(void)
 * (poor test, if first char not 0-9, test fails)
 */

/* function to allocate goodslist node and append allocated goodsinfo 
 * data to list. Takes address of list pointer and pointer to goodsinfo data 
 * to append to list. Returns pointer new node on success, NULL otherwise.
 */
goodsinfo *append (goodslist **l, goodsinfo *tmp)
{
    goodsinfo *data = malloc (sizeof *data);    /* allocate/validate data */
    if (!data) {
        perror ("malloc-data");
        return NULL;
    }
    *data = *tmp;   /* fill allocated data block with tmp values */

    /* allocate/validate list node */
    goodslist *node = malloc (sizeof *node);
    if (!node) {
        perror ("malloc-node");
        free (data);
        return NULL;
    }
    node->data = data;  /* initialize data and set next NULL */
    node->next = NULL;

    if (*l) /* if list exists, chain next to list */
        node->next = *l;

    return ((*l = node)->data); /* assign new node as list, return data */
}

/* simple print list function */
void prn_list (goodslist *l)
{
    if (!l)
        return;

    while (l) {
        printf (" %-8s %-8s %8d %-8s %8d %9d\n", l->data->goods_id, 
                l->data->goods_name, l->data->goods_price, 
                l->data->goods_discount, l->data->goods_amount, 
                l->data->goods_remain);
        l = l->next;
    }
}

/* simple free list function */
void free_list (goodslist *l)
{
    if (!l)
        return;

    goodslist *iter = l;

    while (iter) {
        goodslist *victim = iter;
        free (iter->data);
        iter = iter->next;
        free (victim);
    }
}

int main (int argc, char **argv)
{
    char buf[MAXC];             /* read buffer */
    size_t linecnt = 0;         /* line counter */
    goodslist *list = NULL;     /* linked list pointer */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

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

    while (fgets (buf, MAXC, fp)) {         /* read each line of data */
        goodsinfo tmp = { .goods_id = "" }; /* temp data struct */
        /* parse and validate data in buf (can be separate function) */
        if (sscanf (buf, "%29s %29s %d %29s %d %d", tmp.goods_id, 
                    tmp.goods_name, &tmp.goods_price, tmp.goods_discount, 
                    &tmp.goods_amount, &tmp.goods_remain) != 6) {
            fprintf (stderr, "error: invalid format line %zu.\n", linecnt+1);
            continue;
        }
        if (!append (&list, &tmp))      /* append to list/validate */
            break;
    }
    if (fp != stdin)    /* close file if not stding */
        fclose (fp);

    prn_list (list);    /* print list */
    free_list (list);   /* free list data */

    return 0;
}

( примечание: ваш bool check_nullfile(void) приносит больше вреда, чем пользы, и потерпит неудачу, если первый непробельный символ не является цифрой)

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

( примечание: при использовании сцепления без сохранения указателя "last" приводит к тому, что узлы списка сохраняются в обратном порядке)

$ ./bin/ll_goodslist dat/goodsinfo.txt
 1004     new5          100 0.8            70        80
 1003     new4           88 0.8            70        80
 1002     new3           70 0.8            10        10
 1001     new2           80 0.9            80        80
 1000     new1           90 0.9            90        80

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

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

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

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

$ valgrind ./bin/ll_goodslist dat/goodsinfo.txt
==3493== Memcheck, a memory error detector
==3493== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3493== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3493== Command: ./bin/ll_goodslist dat/goodsinfo.txt
==3493==
 1004     new5          100 0.8            70        80
 1003     new4           88 0.8            70        80
 1002     new3           70 0.8            10        10
 1001     new2           80 0.9            80        80
 1000     new1           90 0.9            90        80
==3493==
==3493== HEAP SUMMARY:
==3493==     in use at exit: 0 bytes in 0 blocks
==3493==   total heap usage: 11 allocs, 11 frees, 1,152 bytes allocated
==3493==
==3493== All heap blocks were freed -- no leaks are possible
==3493==
==3493== For counts of detected and suppressed errors, rerun with: -v
==3493== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

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

сноски

  1. Хотя это не ошибка, C обычно избегает использования camelCase или MixedCase имена переменных в пользу всех строчных букв при резервировании прописных имен для использования с макросами и константами.Это вопрос стиля - так что это полностью зависит от вас, но если вы не будете следовать ему, то в некоторых кругах может произойти неправильное первое впечатление.
0 голосов
/ 20 декабря 2018

Есть две проблемы.Основным является то, что вы создаете связанный список некорректно.

Для первой прочитанной записи значение *L не определено - вы только создали L.Таким образом, доступ к (*L)->next приведет к сбою вашей программы.

listptr->next=(*L)->next;

Но это из-за того, что вы неправильно понимаете, что вообще такое L.Вашей функции info_init передается GoodList **, поскольку она использует этот аргумент для передачи вновь созданного списка.Вы не должны передавать переменную GoodList **, вы должны передавать указатель на GoodList *.Эта переменная должна быть инициализирована как NULL для начала.

int main (void)
{
    GoodsList *L=NULL;
    info_init(&L);
    return 0;
}

, а затем вместо

listptr->next=(*L)->next;
(*L)->next=listptr;

у вас есть

listptr->next=*L;
*L=listptr;

Это означает, чтоВновь созданный узел списка будет указывать на предыдущий узел, который хранится в *L.Как и в main, изначально это NULL, это означает, что первый узел будет указывать рядом с NULL.И тогда *L будет обновлено, чтобы указывать на первый узел.И затем в следующий раз второй узел будет указывать на первый и т. Д.

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

Более надежный метод чтения в каждой записи:читать в каждой строке, используя fgets и используя sscanf для чтения каждого поля.Вы также можете устранить необходимость в функции check_nullfile, проверив возвращаемое значение fopen.Если файл пуст, у вас будет пустой список, так как ничего не будет прочитано или выделено.

Вам также не нужно приводить возвращаемое значение malloc, и безопаснее использовать sizeof(*listptr)рассчитать объем памяти, необходимый listptr, поскольку он будет работать, даже если вы измените тип listptr.

void info_init(GoodsList **L) {

    FILE * fp;
    fp=fopen("goodsinfo.txt", "r");
    if(fp)
    {
        char line[512];
        GoodsList *listptr;
        while (fgets(line,512,fp))
        {
            listptr=malloc(sizeof(*listptr));
            listptr->next=(*L);
            (*L)=listptr;
            sscanf(line,"%4s\t%4s\t%d\t%s\t%d\t%d",listptr->data.goods_id,
                listptr->data.goods_name,
                &(listptr->data.goods_price),
                listptr->data.goods_discount,
                &(listptr->data.goods_amount),
                &(listptr->data.goods_remain));
            CurrentCnt++;
        }
        fclose(fp);
    }

    printf("%d\n", CurrentCnt);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...