无法在C中读取文本文件
Can not read text file in C
我有一个任务要创建一个菜单程序,该程序使用链表来存储 phone 目录的列表。这是我的源代码:
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() returns true)。但是,当我 运行 我的程序时,它没有显示任何消息(我在 Windows 10 上使用 MinGW 的 gcc 作为编译器)。此外,由于其不可读性,输出未显示预期结果。
当我使用ftell()函数检查指针的位置时,它returns0.
注意:我已经检查了我输入的文件名,所以错误不存在。我认为它在别的地方。
输出:
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
很多困惑来自于您构建列表的笨拙方式。现在把菜单放在一边——留给你,让我们专注于构建你的链表。对于初学者来说,无法判断你的失败发生在哪里,因为你没有检查你的输入函数的return。除非您检查return,否则您无法正确使用任何输入函数(或任何对代码的持续运行至关重要的函数)。您也不想 硬编码文件名 ,这就是 int main (int argc, char **argv)
的参数的用途——或者您可以将文件名作为用户输入读取。
接下来,由于您的数据在文件中按输入行排列,因此您应该使用 面向行的 输入函数从文件中读取(例如 fgets()
或 POSIX getline()
) 这样您就可以确保每次都使用整行输入,并且未读的内容不取决于所使用的格式说明符。相反,声明一个足够大的字符数组来保存文件中的每一行,然后使用 fgets()
读入该数组,然后分成 name
、mem
、size
、和 price
使用 sscanf()
(不要忘记检查 return)
虽然您可以将打开的 FILE*
指针发送到 builldbytext()
以读取和构建您的列表,但下面我们将采用一种简单的方法并在 [=27= 中循环读取文件] 并用每一行的值填充一个临时结构。然后我们可以将指向您的列表的指针的地址以及指向包含数据的临时结构的指针传递给 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;
虽然您可以使用 前向链接 添加节点,但您最终会以与文件中的顺序相反的顺序获得节点。您可以使用 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 */
}
(注意: 通过使用 指针到指针 添加第一个或后续节点不需要特殊处理)
一个简单的遍历 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);
}
(注意: is_empty()
功能在每个接收列表的函数中处理,只需验证列表中的第一个节点不是 NULL
)
例子Use/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
如果您使用 前向链接 添加节点,那么它们将以相反的顺序打印。
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于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)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您有任何问题,请告诉我。如果您想更改为 forward-chaining 或使用 tail
指针,请告诉我,我很乐意进一步提供帮助。
我有一个任务要创建一个菜单程序,该程序使用链表来存储 phone 目录的列表。这是我的源代码:
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() returns true)。但是,当我 运行 我的程序时,它没有显示任何消息(我在 Windows 10 上使用 MinGW 的 gcc 作为编译器)。此外,由于其不可读性,输出未显示预期结果。
当我使用ftell()函数检查指针的位置时,它returns0.
注意:我已经检查了我输入的文件名,所以错误不存在。我认为它在别的地方。 输出:
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
很多困惑来自于您构建列表的笨拙方式。现在把菜单放在一边——留给你,让我们专注于构建你的链表。对于初学者来说,无法判断你的失败发生在哪里,因为你没有检查你的输入函数的return。除非您检查return,否则您无法正确使用任何输入函数(或任何对代码的持续运行至关重要的函数)。您也不想 硬编码文件名 ,这就是 int main (int argc, char **argv)
的参数的用途——或者您可以将文件名作为用户输入读取。
接下来,由于您的数据在文件中按输入行排列,因此您应该使用 面向行的 输入函数从文件中读取(例如 fgets()
或 POSIX getline()
) 这样您就可以确保每次都使用整行输入,并且未读的内容不取决于所使用的格式说明符。相反,声明一个足够大的字符数组来保存文件中的每一行,然后使用 fgets()
读入该数组,然后分成 name
、mem
、size
、和 price
使用 sscanf()
(不要忘记检查 return)
虽然您可以将打开的 FILE*
指针发送到 builldbytext()
以读取和构建您的列表,但下面我们将采用一种简单的方法并在 [=27= 中循环读取文件] 并用每一行的值填充一个临时结构。然后我们可以将指向您的列表的指针的地址以及指向包含数据的临时结构的指针传递给 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;
虽然您可以使用 前向链接 添加节点,但您最终会以与文件中的顺序相反的顺序获得节点。您可以使用 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 */
}
(注意: 通过使用 指针到指针 添加第一个或后续节点不需要特殊处理)
一个简单的遍历 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);
}
(注意: is_empty()
功能在每个接收列表的函数中处理,只需验证列表中的第一个节点不是 NULL
)
例子Use/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
如果您使用 前向链接 添加节点,那么它们将以相反的顺序打印。
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于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)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您有任何问题,请告诉我。如果您想更改为 forward-chaining 或使用 tail
指针,请告诉我,我很乐意进一步提供帮助。