Не могу прочитать текстовый файл в C - PullRequest
0 голосов
/ 12 апреля 2020

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

int isempty(FILE *in)
{
    return in == NULL;
}
node *makenewnode(item newitem)
{
    node *newnode = (node *) malloc(sizeof(node));
    newnode->info = newitem;
    return newnode;
}
int countlines(FILE *datain)
{
    int lines = 0;
    char string[MAX];
    while (fgets(string, MAX, datain))
        lines++;
    return lines;
}
node *push(node *listin, item newitem)
{
    node *newnode = makenewnode(newitem);
    if(listin == NULL)
    {
        newnode->next = NULL;
        listin = newnode;
    }
    else
    {
        newnode->next = listin;
        listin = newnode;
        head = listin;
    }
    cur = head;
    return head;
}
node *builldbytext(FILE *txt, int maxlines)
{
    int i = 0;
    node *result = (node *) malloc(maxlines*sizeof(node));
    item *temp = (item *) malloc(maxlines*sizeof(item));
    while(i < maxlines)
    {
        fscanf(txt, "%s %d %f %s\n", temp[i].model, &temp[i].memory, &temp[i].screen_size, temp[i].price);
        i++;
    }
    for(i = 0; i< maxlines; i++)
        result = push(result, temp[i]);
    return result;
}
void tranverse(node *listprint, int maxlines)
{
    int i = 0;
    printf("%-20s%-15s%-20s%-30s\n", "Model", "Memory (GB)", "Screen size (Inch)", "Price");
    for(i = 0; i < maxlines; i++)
    {
        item temp = listprint[i].info;
        printf("%-20s%-15d%-20.2f%-30s\n", temp.model, temp.memory, temp.screen_size, temp.price);
    }
}
int main()
{
    FILE *fp = fopen("phoneDB.txt", "r");
    int maxlines = countlines(fp);
    fclose(fp);
    node *phonelist = (node *) malloc(maxlines*5*sizeof(node));
    int choice = 0;
    do
    {
        FILE *fptxt = fopen("phoneDB.txt", "r");
        printf("1. Import from Text\n3. Display List\n8. Quit\n");
        printf("Your choice: ");
        scanf("%d", &choice);
        getchar();
        switch(choice)
        {
            case 1:
                if(isempty(fptxt))
                {
                    printf("This file is empty\n");
                    goto x;
                }
                phonelist = builldbytext(fptxt, maxlines);
                x: break;
            case 3:
                tranverse(head, maxlines);
                break;
        }
    fclose(fptxt);
    }while(choice != 8);
    free(phonelist);
    return 0;
}

Вот мой текстовый файл (phoneDB.txt):

Iphone6 12 9.6 2000000
IphoneX 32 12.3 40000000
SamsungA6 16 11.3 1000000
SamsungNote6 16 12.3 12000000
Iphone5 32 9.5 6000000
Iphone5s 32 9.5 7000000
Iphone6 32 9.3 8000000
Iphone6s 32 11.3 8500000
OppoF5 32 9.3 10000000
OppoE6 32 11.3 20000000
OppoReno 16 12.6 20000000
IphoneSXmax 128 11.3 45000000
Huawei4 64 11.3 20000000
NokiaE5 16 8.6 3000000
SamsungGalaxy 32 12.3 6000000
SamsungNote7 32 12.3 8000000
Iphone7s 32 12.3 10000000
Huawei6 16 9.5 15000000
SamsungNote5 16 8.5 12500000
IphoneX 16 12.3 25000000
Iphone7 24 11.5 25100000

Моя проблема: Когда я отлаживаю свою программу, я вижу, что моя программа может не читал текстовый файл Это показывает, что файл пуст (функция isempty () возвращает true). Однако, когда я запускаю свою программу, она не показывает никаких сообщений (я использую g cc из MinGW на Windows 10 в качестве компилятора). Более того, из-за его нечитаемости вывод не показывает ожидаемый результат.

Когда я использую функцию ftell () для проверки позиции указателя, он возвращает 0.

Примечание: я проверил имя моего входного файла, поэтому ошибки нет. Я думаю, что это где-то еще. Выход:

Model               Memory (GB)    Screen size (Inch)  Price
Iphone7             24             11.50               25100000
                    0              0.00
                    -2147479552    0.00
                    0              0.00
                    0              -0.42               ¨‰
                    917518         0.00                
                    0              0.00
\\Very long after that but I just show a sample of it

1 Ответ

5 голосов
/ 12 апреля 2020

Большая часть путаницы возникает из-за неловкого подхода к составлению списка. Пока отложите меню - это остается вам, давайте просто сконцентрируемся на создании вашего связанного списка. Для начала, невозможно сказать, где возникает ваша ошибка, потому что вы не можете проверить возврат вашей функции ввода. Вы не можете правильно использовать любую функцию ввода (или любую функцию, критичную для продолжения работы вашего кода), если вы не проверите возврат . Вы также не хотите Имена файлов с жестким кодом , то есть для чего нужны параметры int main (int argc, char **argv) - или вы можете взять имя файла для чтения в качестве пользовательского ввода.

Далее, так как ваши данные расположены в виде строк ввода в вашем файле, вы должны использовать строчную функцию ввода для чтения из файла (например, fgets() или POSIX getline()). Вы потребляете всю строку ввода каждый раз, и то, что остается непрочитанным, не зависит от используемого спецификатора формата. Вместо этого объявите массив символов достаточного размера для хранения каждой строки из вашего файла, а затем прочитайте этот массив с помощью fgets(), а затем разделите на name, mem, size и price, используя sscanf() (не забудьте проверить возврат)

Хотя вы можете отправить свой открытый FILE* указатель на builldbytext(), чтобы прочитать и построить свой список, ниже давайте просто воспользуемся простым подходом и прочитаем из файла в al oop в main() и заполните временную структуру значениями из каждой строки. Затем мы можем передать адрес указателя на ваш список вместе с указателем на временную структуру, содержащую данные, в функцию add() (ваш push()) для построения списка.

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

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

#define NAMSZ  32       /* if you need a constant, #define one (or more) */
#define MAXC 1024

typedef struct node_t {     /* list node */
    char name[NAMSZ];
    double size;
    int mem, price;
    struct node_t *next;
} node_t;

Хотя вы можете использовать forward-chaining для добавления узлов, вы в конечном итоге получите свои узлы в в обратном порядке по сравнению с тем, что у вас есть в файле. Вы можете использовать указатель head и tail для добавления по порядку, используя указатель tail в то же время O (1), или вы можете выполнить итерацию до конца и добавьте время O (n).

Простой пример добавления узла в конце итерации каждый раз:

 /** add node at end of list */
node_t *add (node_t **head, node_t *tmp)
{
    node_t **ppn = head,                    /* pointer to pointer to head */
            *pn = *head,                    /* pointer to head */
            *node = malloc (sizeof *node);  /* allocate new node */

    if (!node) {                            /* validate allocation */
        perror ("malloc-node");
        return NULL;
    }
    *node = *tmp;                           /* assign tmp struct values */
    node->next = NULL;                      /* set next pointer NULL */

    while (pn) {                            /* iterate to end of list */ 
        ppn = &pn->next;
        pn = pn->next;
    }

    return *ppn = node;                     /* assign & return new node */
}

( note: by с использованием указатель-на-указатель особой обработки для добавления первого или последующих узлов не требуется)

Простой обход prn() и функция удаления всех узлов в списке del_list() когда это может быть сделано:

/** print all nodes in list */
void prn (node_t *l)
{
    if (!l) {
        puts ("list-empty");
        return;
    }
    for (node_t *n = l; n; n = n->next)
        printf ("%-16s %3d %5g %d\n", n->name, n->mem, n->size, n->price);
}

/** delete all nodes in list */
void del_list (node_t *l)
{
    node_t *n = l;
    while (n) {
        node_t *victim = n;
        n = n->next;
        free (victim);
    }
}

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

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

    char buf[MAXC];         /* buffer to hold each line */
    node_t *list = NULL;    /* pointer to list (must initialize NULL) */
    /* use filename provided as 1st argument (stdin by default) */
    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 from file */
        node_t tmp;                     /* temporary struct to hold values */
        /* if name, mem, size, price successfully parsed from line */
        if (sscanf (buf, "%s %d %lf %d",
                    tmp.name, &tmp.mem, &tmp.size, &tmp.price) == 4)
            if (!(add (&list, &tmp)))   /* add node to list/validate */
                break;
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

    prn (list);
    del_list (list);
}

( note: * * * * is_empty() обрабатывается в каждой функции, которая получает список, просто проверяя, что первый узел в списке не NULL)

Пример U se / Output

С вашими данными в файле dat/phones.txt вы получите:

$ ./bin/lls_phones dat/phones.txt
Iphone6           12   9.6 2000000
IphoneX           32  12.3 40000000
SamsungA6         16  11.3 1000000
SamsungNote6      16  12.3 12000000
Iphone5           32   9.5 6000000
Iphone5s          32   9.5 7000000
Iphone6           32   9.3 8000000
Iphone6s          32  11.3 8500000
OppoF5            32   9.3 10000000
OppoE6            32  11.3 20000000
OppoReno          16  12.6 20000000
IphoneSXmax      128  11.3 45000000
Huawei4           64  11.3 20000000
NokiaE5           16   8.6 3000000
SamsungGalaxy     32  12.3 6000000
SamsungNote7      32  12.3 8000000
Iphone7s          32  12.3 10000000
Huawei6           16   9.5 15000000
SamsungNote5      16   8.5 12500000
IphoneX           16  12.3 25000000
Iphone7           24  11.5 25100000

Если бы вы использовали forward-chaining для добавления узлы, то они будут печатать в обратном порядке.

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

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

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

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

$ valgrind ./bin/lls_phones dat/phones.txt
==17133== Memcheck, a memory error detector
==17133== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17133== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==17133== Command: ./bin/lls_phones dat/phones.txt
==17133==
Iphone6           12   9.6 2000000
IphoneX           32  12.3 40000000
SamsungA6         16  11.3 1000000
SamsungNote6      16  12.3 12000000
Iphone5           32   9.5 6000000
Iphone5s          32   9.5 7000000
Iphone6           32   9.3 8000000
Iphone6s          32  11.3 8500000
OppoF5            32   9.3 10000000
OppoE6            32  11.3 20000000
OppoReno          16  12.6 20000000
IphoneSXmax      128  11.3 45000000
Huawei4           64  11.3 20000000
NokiaE5           16   8.6 3000000
SamsungGalaxy     32  12.3 6000000
SamsungNote7      32  12.3 8000000
Iphone7s          32  12.3 10000000
Huawei6           16   9.5 15000000
SamsungNote5      16   8.5 12500000
IphoneX           16  12.3 25000000
Iphone7           24  11.5 25100000
==17133==
==17133== HEAP SUMMARY:
==17133==     in use at exit: 0 bytes in 0 blocks
==17133==   total heap usage: 24 allocs, 24 frees, 6,848 bytes allocated
==17133==
==17133== All heap blocks were freed -- no leaks are possible
==17133==
==17133== For counts of detected and suppressed errors, rerun with: -v
==17133== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

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

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