Can not read text file in C

前端 未结 1 471
终归单人心
终归单人心 2021-01-29 00:03

I have an assignment to create a menu program that uses a linked list to store a list of phone catalog. Here is my source code:

int isempty(FILE *in)
{
    retur         


        
相关标签:
1条回答
  • 2021-01-29 00:22

    Much of the confusion comes from the awkward way you are approaching building your list. Set aside the menu for now -- that is left to you, let's just concentrate on building your linked list. For starters, it's impossible to tell where your failure arises because you fail to check the return of your input function. You cannot use any input function correctly (or any function critical to the continued operation of your code) unless you check the return. You also do not want to Hardcode Filenames, that is what the parameters to int main (int argc, char **argv) are there for — or you can take the filename to read as user-input.

    Next, since your data is arranged in lines-of-input in your file, you should use a line-oriented input function to read from the file (such as fgets() or POSIX getline()) That way you ensure you consume an entire line of input each time and what remains unread does not depend on the format specifier used. Instead, declare a character array of sufficient size to hold each line from your file and then read into that array with fgets() and then separate into name, mem, size, and price using sscanf() (don't forget to check the return)

    While you can send your open FILE* pointer to builldbytext() to read and build your list, below let's just take a simple approach and read from the file in a loop in main() and fill a temporary struct with the values from each line. Then we can pass the address of the pointer to your list along with a pointer to the temporary struct containing data to an add() function (your push()) to build the list.

    You didn't provide your node definition, so for purposes of the example we will use:

    #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;
    

    While you can use forward-chaining to add the nodes, you will end up with your nodes in the reverse order from what you have in the file. You can either use a head and tail pointer to add in order using the tail pointer in the same O(1) time or you can iterate to the end and add in O(n) time.

    A simple example of adding the node at the end iterating each time would be:

     /** 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 using a pointer-to-pointer there is no special treatment required for adding the first or subsequent nodes)

    A simple traversal prn() and a function to delete all nodes in the list del_list() when done could be:

    /** 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);
        }
    }
    

    Finally, all that is needed to take your filename to read as the first argument to the program, or read from stdin if no argument is provided, to fill the list and then traverse and free all allocated memory could be:

    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: the is_empty() functionality is handled in each function that receives the list simply by validating that the first node in the list isn't NULL)

    Example Use/Output

    With your data in the file dat/phones.txt, you would receive:

    $ ./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
    

    If you had used forward-chaining to add the nodes, then they would print in the reverse order.

    Memory Use/Error Check

    In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

    It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

    For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

    $ 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)
    

    Always confirm that you have freed all memory you have allocated and that there are no memory errors.

    Look things over and let me know if you have questions. If you would like to change to forward-chaining or using a tail pointer, let me know and I'm happy to help further.

    0 讨论(0)
提交回复
热议问题