倒回文件,创建动态结构
Rewind File, Create Dynamic Struct
此函数将倒回文件,创建动态数组(大小),并读入数据,填充 _data 结构动态数组。请注意,流
这次是按值传递的。然后函数 returns 填充
结构数组
struct _data
{
char* name;
long number;
};
struct _data *load(FILE *stream, int size)
{
struct _data BlackBox = calloc(size, sizeof(_data));
char tempName[3];
stream = fopen("names.txt", "r");
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", tempName, &data.number);
BlackBox[i].name = calloc(strlen(tempName), sizeof(char));
strcpy(BlackBox[i].name, tempName);
}
fclose(stream);
return &BlackBox;
}
File Content
ron 7774013
jon 7774014
我是初学者,代码设计有困难。有人可以解释一下吗?谢谢
我想你有一些来自 gcc 的警告可以帮助你。
使用 calloc 修复内存管理,不要 return 堆栈指针
typedef struct _data
{
char* name;
long number;
} _data;
_data *load(FILE *stream, int size)
{
_data *BlackBox = calloc(size, sizeof(_data));
char tempName[3];
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", tempName, &BlackBox[i].number);
BlackBox[i].name = strdup(tempName);
}
fclose(stream);
return BlackBox;
}
int main (void)
{
FILE *f = fopen("test.data", "r");
_data *data = load(f, 2);
printf("%s %ld\n", data[0].name, data[0].number);
printf("%s %ld\n", data[1].name, data[1].number);
return 0;
}
输出
aurel@vm-pontarlier:~$ ./a.out
ron 7774013
jon 7774014
考虑改_data
typedef struct _data{
char name[256];
long number;
} _data;
扫描将是:
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", BlackBox[i].name, &BlackBox[i].number);
}
您做出与之前 post 相同的错误。您也不为 _data
中的 name
成员分配内存。至于编译错误:
- 使用
malloc
或 calloc
动态分配的 T
类型的数组由句柄控制,指向 T*
类型的 T
的指针.
- 您定义的结构是
struct _data
类型,该类型包含关键字struct
。如果你想为你的类型使用单字标识符,请使用 Ôrel 向你展示的“typedef”。
- 您没有名为 ´data
. The handle to the newly allocated memory is ´BlackBox
的变量。
如果您修复这些错误,您将 运行 变成编译器无法知道的逻辑错误:
- 您的
BlackBox[i].name
通过 calloc
初始化为 NULL
,因此它没有指向有效内存。你不能 strcpy
任何东西。你可以做的是使用非标准但广泛使用的strdup
,它首先根据需要分配内存,然后复制。
- 您的临时字符串长度为 3;它最多可以包含两个字符加上空终止符。文件中的名称很短,这是理所当然的,但您的程序必须为任何输入做好准备,甚至是非法输入。
FILE *
未在您的函数之外使用;它应该是 load
. 的本地
- 用户必须指定要阅读的项目数。这违背了动态分配的目的。用户也不知道阅读了多少项目;根据要求,文件中的项目可能较少。您的功能设计应该反映这一点。 (好吧,这不是真的:你将内存初始化为零,这意味着
NULL
的 char 指针表示数组的结尾,但是:)
- 您没有测试
fscanf
的输出,您应该这样做。如果您的文件少于 size
个项目,最后的项目将包含垃圾,因为您将垃圾值复制到零初始化内存中。
- 本身不是错误,但如果您的文件是按行组织的,请考虑先读取带有
fgets
的行,然后使用 sscanf
. 解析该行
下面的示例代码试图结合这些准则。注意事项:
- 内存没有分配成一个巨大的块,但它会随着对
realloc
的后续调用而根据需要增长。调用 realloc(NULL, s)
等同于 malloc(s)
.
- 函数"fills in"通过指向整数的指针读取元素的数量。
- 字符串被复制到新分配的内存中。这意味着理论上,字符串可以随心所欲。 (实际上,最大缓冲区长度 20 会缩短此类字符串。)函数
duplicate
模拟函数 strdup
.
- 这些字符串在清理时应该
free
d。
load
函数使用文件名而不是文件句柄。
- 文件读取分两个阶段进行:首先读取一行,然后扫描该行。如果格式不是 "string without spaces" + "number",则会写入错误消息。
无论如何,这里是:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct _data
{
char *name;
long number;
};
/*
* Duplicate a string on the heap (aka strdup)
*/
char *duplicate(const char *str)
{
char *p = malloc(strlen(str) + 1);
strcpy(p, str);
return p;
}
/*
* Read a list of names from file fn
*/
struct _data *load(const char *fn, int *psize)
{
struct _data *data = NULL;
FILE *stream;
char line[80]; // buffer for line
int lnr = 0; // line number for error message
int n = 0; // number of read items
stream = fopen("names.txt", "r");
if (stream == NULL) return NULL; // Can't open file
while (fgets(line, sizeof(line), stream)) {
long int number;
char buf[20];
lnr++;
if (sscanf(line, "%19s %ld", buf, &number) == 2) {
data = realloc(data, (n + 1) * sizeof(*data));
data[n].number = number;
data[n].name = duplicate(buf);
n++;
} else {
fprintf(stderr, "[%s, line %d] Illegal format\n", fn, lnr);
}
}
fclose(stream);
*psize = n; // Assign number of read items
return data;
}
/*
* Free memory allocated by load
*/
void cleanup(struct _data *data, int n)
{
while (n--) free(data[n].name);
free(data);
}
int main()
{
struct _data *data;
int i, n;
data = load("names.txt", &n);
if (data == NULL) return -1;
for (i = 0; i < n; i++) {
printf("%-20s%12ld\n", data[i].name, data[i].number);
}
cleanup(data, n);
return 0;
}
...最后 tempName[3] 太小:考虑到您的 3 个字母输入,它必须至少为 4。此外,您忘记为 malloc 调用中的终止空字符分配空间:
char tempName[4];
...
BlackBox[i].name = malloc(strlen(tempName)+1);
(tempName[3] 没有导致错误是因为编译器可能将其四舍五入为偶数字节 - 但这是典型的初学者错误。)
此函数将倒回文件,创建动态数组(大小),并读入数据,填充 _data 结构动态数组。请注意,流 这次是按值传递的。然后函数 returns 填充 结构数组
struct _data
{
char* name;
long number;
};
struct _data *load(FILE *stream, int size)
{
struct _data BlackBox = calloc(size, sizeof(_data));
char tempName[3];
stream = fopen("names.txt", "r");
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", tempName, &data.number);
BlackBox[i].name = calloc(strlen(tempName), sizeof(char));
strcpy(BlackBox[i].name, tempName);
}
fclose(stream);
return &BlackBox;
}
File Content
ron 7774013
jon 7774014
我是初学者,代码设计有困难。有人可以解释一下吗?谢谢
我想你有一些来自 gcc 的警告可以帮助你。
使用 calloc 修复内存管理,不要 return 堆栈指针
typedef struct _data
{
char* name;
long number;
} _data;
_data *load(FILE *stream, int size)
{
_data *BlackBox = calloc(size, sizeof(_data));
char tempName[3];
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", tempName, &BlackBox[i].number);
BlackBox[i].name = strdup(tempName);
}
fclose(stream);
return BlackBox;
}
int main (void)
{
FILE *f = fopen("test.data", "r");
_data *data = load(f, 2);
printf("%s %ld\n", data[0].name, data[0].number);
printf("%s %ld\n", data[1].name, data[1].number);
return 0;
}
输出
aurel@vm-pontarlier:~$ ./a.out
ron 7774013
jon 7774014
考虑改_data
typedef struct _data{
char name[256];
long number;
} _data;
扫描将是:
for (int i=0; i<size; i++)
{
fscanf(stream, "%s %ld", BlackBox[i].name, &BlackBox[i].number);
}
您做出与之前 post 相同的错误。您也不为 _data
中的 name
成员分配内存。至于编译错误:
- 使用
malloc
或calloc
动态分配的T
类型的数组由句柄控制,指向T*
类型的T
的指针. - 您定义的结构是
struct _data
类型,该类型包含关键字struct
。如果你想为你的类型使用单字标识符,请使用 Ôrel 向你展示的“typedef”。 - 您没有名为 ´data
. The handle to the newly allocated memory is ´BlackBox
的变量。
如果您修复这些错误,您将 运行 变成编译器无法知道的逻辑错误:
- 您的
BlackBox[i].name
通过calloc
初始化为NULL
,因此它没有指向有效内存。你不能strcpy
任何东西。你可以做的是使用非标准但广泛使用的strdup
,它首先根据需要分配内存,然后复制。 - 您的临时字符串长度为 3;它最多可以包含两个字符加上空终止符。文件中的名称很短,这是理所当然的,但您的程序必须为任何输入做好准备,甚至是非法输入。
FILE *
未在您的函数之外使用;它应该是load
. 的本地
- 用户必须指定要阅读的项目数。这违背了动态分配的目的。用户也不知道阅读了多少项目;根据要求,文件中的项目可能较少。您的功能设计应该反映这一点。 (好吧,这不是真的:你将内存初始化为零,这意味着
NULL
的 char 指针表示数组的结尾,但是:) - 您没有测试
fscanf
的输出,您应该这样做。如果您的文件少于size
个项目,最后的项目将包含垃圾,因为您将垃圾值复制到零初始化内存中。 - 本身不是错误,但如果您的文件是按行组织的,请考虑先读取带有
fgets
的行,然后使用sscanf
. 解析该行
下面的示例代码试图结合这些准则。注意事项:
- 内存没有分配成一个巨大的块,但它会随着对
realloc
的后续调用而根据需要增长。调用realloc(NULL, s)
等同于malloc(s)
. - 函数"fills in"通过指向整数的指针读取元素的数量。
- 字符串被复制到新分配的内存中。这意味着理论上,字符串可以随心所欲。 (实际上,最大缓冲区长度 20 会缩短此类字符串。)函数
duplicate
模拟函数strdup
. - 这些字符串在清理时应该
free
d。 load
函数使用文件名而不是文件句柄。- 文件读取分两个阶段进行:首先读取一行,然后扫描该行。如果格式不是 "string without spaces" + "number",则会写入错误消息。
无论如何,这里是:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct _data
{
char *name;
long number;
};
/*
* Duplicate a string on the heap (aka strdup)
*/
char *duplicate(const char *str)
{
char *p = malloc(strlen(str) + 1);
strcpy(p, str);
return p;
}
/*
* Read a list of names from file fn
*/
struct _data *load(const char *fn, int *psize)
{
struct _data *data = NULL;
FILE *stream;
char line[80]; // buffer for line
int lnr = 0; // line number for error message
int n = 0; // number of read items
stream = fopen("names.txt", "r");
if (stream == NULL) return NULL; // Can't open file
while (fgets(line, sizeof(line), stream)) {
long int number;
char buf[20];
lnr++;
if (sscanf(line, "%19s %ld", buf, &number) == 2) {
data = realloc(data, (n + 1) * sizeof(*data));
data[n].number = number;
data[n].name = duplicate(buf);
n++;
} else {
fprintf(stderr, "[%s, line %d] Illegal format\n", fn, lnr);
}
}
fclose(stream);
*psize = n; // Assign number of read items
return data;
}
/*
* Free memory allocated by load
*/
void cleanup(struct _data *data, int n)
{
while (n--) free(data[n].name);
free(data);
}
int main()
{
struct _data *data;
int i, n;
data = load("names.txt", &n);
if (data == NULL) return -1;
for (i = 0; i < n; i++) {
printf("%-20s%12ld\n", data[i].name, data[i].number);
}
cleanup(data, n);
return 0;
}
...最后 tempName[3] 太小:考虑到您的 3 个字母输入,它必须至少为 4。此外,您忘记为 malloc 调用中的终止空字符分配空间:
char tempName[4];
...
BlackBox[i].name = malloc(strlen(tempName)+1);
(tempName[3] 没有导致错误是因为编译器可能将其四舍五入为偶数字节 - 但这是典型的初学者错误。)