无法读取C中的文本文件 [英] Can not read text file in C

查看:63
本文介绍了无法读取C中的文本文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个任务来创建一个菜单程序,该程序使用链接列表来存储电话目录列表.这是我的源代码:

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)
{
    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):

Here is my text file (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).但是,当我运行程序时,它不会显示任何消息(我在Windows 10上使用MinGW的gcc作为编译器).此外,由于其不可读性,因此输出未显示预期的结果.

My problem: When I debug my program, I see that my program can't read the text file. It shows that the file is empty (function isempty() returns true). However, when I run my program, it does not show any messages (I use gcc of MinGW on Windows 10 as a compiler). Moreover, because of its unreadability, the output does not show the expected result.

当我使用 ftell()函数检查指针的位置时,它返回0.

When I use ftell() function to check the position of pointer, it returns 0.

注意:我已经检查了我的输入文件名,所以不存在错误.我认为它在其他地方.输出:

Note: I have checked my input file name so the error is not there. I think it is somewhere else. The output:

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

推荐答案

很多困惑都来自您要建立列表的尴尬方式.现在暂留菜单-这留给您,让我们集中精力构建链接列表.对于初学者来说,由于您无法 检查输入函数的返回结果 ,因此无法确定失败的原因.除非您检查返回值,否则您将无法正确使用任何输入函数(或对代码的连续操作至关重要的任何函数).您也不想使用硬编码文件名,这就是 int main(int argc,char ** argv)的参数所在的位置,或者也可以使用文件名读取作为用户输入.

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.

接下来,由于数据是按文件中的输入行排列的,因此您应该使用面向行的输入函数从文件中读取(例如 fgets()或POSIX getline())这样,您可以确保每次使用一整行输入,而未读内容则不取决于所使用的格式说明符.相反,声明一个足够大的字符数组以容纳文件中的每一行,然后使用 fgets()读入该数组,然后分离为 name mem size price (使用 sscanf()(不要忘记检查退货)

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)

虽然您可以将打开的 FILE * 指针发送到 builldbytext()来读取和构建列表,但下面我们仅采用一种简单的方法并从中读取文件在 main()中循环,并使用每行中的值填充临时结构.然后,我们可以将指向列表的指针的地址以及指向包含数据的临时结构的指针传递给 add()函数(您的 push())以进行构建列表.

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;

虽然可以使用正向链接添加节点,但最终节点的顺序与文件中的顺序相反.您可以使用 head tail 指针在同一O(1)时间中使用 tail 指针顺序添加,也可以迭代到最后,并增加O(n)时间.

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

(注意:,使用 pointer-to-pointer 无需添加第一个或后续节点的特殊处理)

(note: by using a pointer-to-pointer there is no special treatment required for adding the first or subsequent nodes)

简单遍历 prn()和完成后删除列表 del_list()中所有节点的功能可以是:

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

最后,将文件名读取为程序的第一个参数,或者如果没有提供参数,则从 stdin 读取,只需填写所有内容,然后遍历并释放所有分配的内存可能是:

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

(注意:在接收列表的每个函数中处理 is_empty()功能,只需验证列表中的第一个节点是否不是NULL )

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

使用/输出示例

将数据保存在文件 dat/phones.txt 中,您将收到:

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.

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时,可以释放.

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.

对于Linux, valgrind 是通常的选择.每个平台都有类似的内存检查器.它们都很容易使用,只需通过它运行程序即可.

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.

仔细研究一下,如果您有任何疑问,请告诉我.如果您想更改为前向链接或使用 tail 指针,请告诉我,我们很乐意提供进一步的帮助.

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.

这篇关于无法读取C中的文本文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆