c - C 编程:从文件中读取数据、动态分配内存、将内容放入结构数组
问题描述
这是我在 stackoverflow 上的第一篇文章。我是一名学习 C 的 CS 学生,我在处理的问题上遇到了一些问题。另外,我应该说我知道的很少,所以如果我放在这里的任何东西被认为是愚蠢或无知的,那绝对不是我的本意
我知道还有其他类似的帖子,但是到目前为止,我觉得我已经尝试进行了很多修改,所有这些修改都以相同的结果结束。
我得到一个文本文件,其中每一行都包含 studentName(tab)gpa。文件的总大小是未知的,这个我必须使用动态内存分配。
文本文件格式示例
Jordan 4.0
Bhupesh 2.51
程序的一般步骤
为了避免尴尬,许多细节将被省略,但是我将对我正在努力解决的过程进行高级概述:
1.) Create dynamic memory array to hold struct for each line
2.) Start looping through file
3.) check the current size of the array to see if reallocation is necessary
4.) Create dynamic array to hold name
5.) Place name and gpa into struct
6.) rinse & repeat
最后,最后一件事。当达到我的初始分配内存限制并且程序尝试从堆中重新分配更多内存时,就会发生错误。
我的代码如下所示:
#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2
struct Student {
float gpa;
char * name;
};
// read the file, pack contents into struct array
struct Student * readFileContents(char *filename, int *rowCounter) {
// setup for loop
int maxDataSize = ROW_COUNT;
float currentStudentGpa = 0;
char studentNameBuffer[BUFFER_SIZE];
// initial structArray pre-loop
struct Student * structArray = calloc(maxDataSize, sizeof(*structArray));
FILE *pFile = fopen(filename, "r");
validateOpenFile(pFile);
// loop through, get contents, of eaach line, place them in struct
while (fscanf(pFile, "%s\t%f", studentNameBuffer, ¤tStudentGpa) > 0) {
structArray = checkArraySizeIncrease(*rowCounter, &maxDataSize, &structArray);
structArray->name = trimStringFromBuffer(studentNameBuffer);
structArray->gpa = currentStudentGpa;
(*rowCounter)++, structArray++;
}
fclose(pFile);
return structArray;
}
// resize array if needed
struct Student * checkArraySizeIncrease(int rowCount, int * maxDataSize, struct Student ** structArray) {
if (rowCount == *maxDataSize) {
*maxDataSize += ROW_COUNT;
**// line below is where the error occurs**
struct Student * newStructArray = realloc(*structArray, *maxDataSize * sizeof(*newStructArray));
validateMalloc(newStructArray);
return newStructArray;
}
return *structArray;
}
// resize string from initial data buffer
char *trimStringFromBuffer(char *dataBuffer) {
char *string = (char *) calloc(strlen(dataBuffer), sizeof(char));
validateMalloc(string);
strcpy(string, dataBuffer);
return string;
}
再次,如果有人问过类似的问题,我深表歉意,但请知道我已经尝试了我在堆栈溢出中发现的大多数建议,但没有成功(我很清楚这是我在C)。
我现在将立即为我的强制性“stackoverflow 上的第一篇文章”烘焙做好准备。干杯!
解决方案
您正在重用structArray
作为数组的基础和指向当前元素的指针。这行不通。我们需要两个变量。
有许多与动态数组相关的“松散”变量。定义一个struct
(例如dynarr_t
下面)来包含它们并只传递struct
指针会更干净。
复制字符串时,必须分配strlen
+ 1 [而不仅仅是strlen
]。但是,整个函数做了strdup
已经做的事情。
我试图尽可能多地保存,但我不得不稍微重构代码以包含所有必要的更改。
通过传递sizeof(*structArray)
给arrnew
函数,这允许结构用于任意大小的数组元素。
无论如何,这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2
struct Student {
float gpa;
char *name;
};
// general dynamic array control
typedef struct {
void *base; // base address
size_t size; // bytes in array element
size_t count; // current number of used entries
size_t max; // maximum number of entries
size_t grow; // number of entries to grow
} dynarr_t;
// arrfind -- return pointer to array element
void *
arrfind(dynarr_t *arr,size_t idx)
{
void *ptr;
ptr = arr->base;
idx *= arr->size;
ptr += idx;
return ptr;
}
// arrnew -- create new array control
dynarr_t *
arrnew(size_t siz,size_t grow)
// siz -- sizeof of array element
// grow -- number of elements to grow
{
dynarr_t *arr;
arr = calloc(1,sizeof(*arr));
if (arr == NULL)
sysfault("arrnew: calloc fail -- %s\n",strerror(errno));
arr->size = siz;
arr->grow = grow;
return arr;
}
// arrgrow -- grow array [if necessary]
// RETURNS: pointer to element to fill
void *
arrgrow(dynarr_t *arr)
{
void *ptr;
// grow array if necessary
// NOTE: use of a separate "max" from "count" reduces the number of realloc
// calls
if (arr->count >= arr->max) {
arr->max += arr->grow;
arr->base = realloc(arr->base,arr->size * arr->max);
if (arr->base == NULL)
sysfault("arrgrow: realloc failure -- %s\n",strerror(errno));
}
// point to current element
ptr = arrfind(arr,arr->count);
// advance count of elements
++arr->count;
return ptr;
}
// arrtrim -- trim array to actual number of elements used
void
arrtrim(dynarr_t *arr)
{
arr->base = realloc(arr->base,arr->size * arr->count);
if (arr->base == NULL)
sysfault("arrtrim: realloc failure -- %s\n",strerror(errno));
arr->max = arr->count;
}
void
validateMalloc(void *ptr)
{
if (ptr == NULL) {
perror("validateMalloc");
exit(1);
}
}
void
validateOpenFile(FILE *ptr)
{
if (ptr == NULL) {
perror("validateOpenFile");
exit(1);
}
}
// resize string from initial data buffer
char *
trimStringFromBuffer(char *dataBuffer)
{
#if 0
#if 0
char *string = calloc(1,strlen(dataBuffer));
#else
char *string = calloc(1,strlen(dataBuffer) + 1);
#endif
validateMalloc(string);
strcpy(string, dataBuffer);
#else
char *string = strdup(dataBuffer);
validateMalloc(string);
#endif
return string;
}
// read the file, pack contents into struct array
dynarr_t *
readFileContents(char *filename)
{
dynarr_t *arr;
// setup for loop
float currentStudentGpa = 0;
char studentNameBuffer[BUFFER_SIZE];
struct Student *structArray;
arr = arrnew(sizeof(*structArray),10);
FILE *pFile = fopen(filename, "r");
validateOpenFile(pFile);
// loop through, get contents, of eaach line, place them in struct
while (fscanf(pFile, "%s\t%f", studentNameBuffer, ¤tStudentGpa) > 0) {
structArray = arrgrow(arr);
structArray->name = trimStringFromBuffer(studentNameBuffer);
structArray->gpa = currentStudentGpa;
}
fclose(pFile);
arrtrim(arr);
return arr;
}
推荐阅读
- r - 使用函数对多个数据框进行排序
- apache-kafka - 配置 debezium 以根据自定义标准将数据放在单独的主题上
- php - 随机选择分数总和小于 10 的记录
- html - 订阅 next() 调用
- angular - 如何在 Angular 应用程序中使用 Springy.js?
- regex - 用于位置、特定文件缓存的 nginx 正则表达式
- wpf - CefSharp WPF 中的下拉菜单是否存在已知问题
- angular - 如何使用 angular2 使用表单一次编辑多行
- javascript - 在 onclick 调用中传递字符串参数
- typescript - 打字稿错误 - getElementsByTagName() 返回 NodeListOf
而不是 HTMLCollectionOf