我正在尝试从文件创建结构数组。
文件中的每一行的格式为:old_word new_word
并且我设法将文件的内容存储到数组中,但是每一行都作为元素存储在数组中,但是我想将每一行作为结构加载,以便获得结构数组。
我的结构如下:
typedef struct ChangedWord
{
char *old;
char *new;
} word_change_t;
我的计划是遍历此结果数组并能够访问
for example the old word in the struct
我还设法将
old_word
和new_word
存储为不同的变量。因此,我只能创建一个仅包含old
或new_words
的数组,但是我一生都看不到如何将两个单词都存储为一个结构并获得一个结构数组。我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ChangedWord
{
char *old;
char *new;
} word_change_t;
char **readFile(char *filename, int *len);
int main(int argc, char *argv[])
{
if (argc == 1)
{
printf("Usage add file\n");
return 1;
}
int length = 0;
char **words = loadfile(argv[1],&length);
printf("%d\n", length);
for (int i = 0; i < length; i++) {
printf("%s\n",words[i]);
}
free(words);
return 0;
}
char **readfile(char *filename, int *len)
{
const int REALLOCSIZE = 100;
FILE *f = fopen(filename,"r");
if (!f) {
fprintf(stderr, "Can't open file\n");
return NULL;
}
int arrlen = REALLOCSIZE;
char **lines = malloc(sizeof(word_change_t) * REALLOCSIZE);
char buf[1000];
int i = 0;
int counter = 2;
while (fgets(buf,1000,f))
{
char old_str[500], new_str[500];
strcpy(old_str, strtok(buf , " "));
strcpy(new_str, strtok(NULL, " "));
if (i == arrlen)
{
counter++;
arrlen += REALLOCSIZE;
char **newlines = realloc(lines, sizeof(word_change_t) * counter * REALLOCSIZE);
if(!newlines)
{
printf("Out of memory\n");
//return 2;
}
lines = newlines;
}
old_str[strlen(old_str)] = '\0';
new_str[strlen(new_str)-1] = '\0';
int old_strlen = strlen(old_str);
int new_strlen = strlen(new_str);
char *str_old = malloc((old_strelen + 1) * sizeof(char));
strcpy(str_old, old_str);
char *str_new = malloc((new_strlen + 1) * sizeof(char));
strcpy(str_new, new_str);
word_change_t.old = *str_old;
word_change_t.old = *str_new;
//DEBUG Start
printf("%s\n",word_pair_t.old);
//printf("%lu\n",strlen(new_str));
// DEBUG End
lines[i] = word_change_t;
i++;
}
*len = i;
return lines;
}
当我得到错误信息时,我就运行它
error: expected identifier or '('
word_pair_t.old = *str_old;
如何将元素存储到结构数组中
非常感谢您的帮助。
最佳答案
实际上,您对解决问题的方法比原来想的要困惑的多。由于您已经声明了包含两个指针的结构,并且想要捕获old
中的new
和struct
单词,因此您不希望words
或lines
成为指向指针的指针char
,相反,您希望两者都只是指向word_change_t
的指针。这样,您可以为结构分配一个内存块,然后分配一个old
和new
并在每个结构中存储新旧单词对。然后,您可以在需要更多内容时realloc
结构块。
尽管可以自由地将文件名作为参数传递并在函数中打开文件-通常,您可以打开并验证在调用方中正在打开的文件,然后将open FILE*
指针作为参数传递给函数。如果文件打开失败,则无需进行函数调用。
让我们从更新函数声明开始,以打开一个FILE*
来读取成对的单词并更改指针的类型,该指针将保留返回到size_t
而不是int
时分配的结构数(您可以t的结构数为负)。我们还更新返回类型以返回指向分配的struct块的指针,例如
word_change_t *readfile (FILE *fp, size_t *n);
现在在
main()
中,您可以验证文件名是否作为参数给出(或者如果没有给出文件名,则默认从stdin
读取),然后调用readfile
,例如#define REALLOCSIZE 2 /* if you need a constant, #define one (or more) */
#define MAXC 1024
...
int main (int argc, char *argv[]) {
size_t length = 0; /* number of structs */
word_change_t *words = NULL; /* pointer to struct */
/* 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;
}
words = readfile (fp, &length); /* read all pairs of words into words */
现在在
readfile()
中,分配初始数量的struct(基于以上REALLOCSIZE
的2,并开始读取行。请记住,对于每次分配或重新分配,必须在使用内存之前验证它是否成功。word_change_t *readfile (FILE *fp, size_t *n)
{
size_t allocated = REALLOCSIZE, /* initial no. of structs */
used = 0; /* counter tracking no. used */
char buf[MAXC]; /* buf to hold line */
/* allocate initial struct */
word_change_t *lines = malloc (allocated * sizeof *lines);
if (!lines) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
...
现在,您将开始从文件中读取包含单词对的每一行。声明任何局部变量后,首先需要通过检查
used == allocated
(used
是跟踪填充的结构数的计数器,而allocated
是分配的结构数)来检查是否需要重新分配。请注意,对于realloc
,您始终使用临时指针来防止丢失指向原始内存块的指针,如果realloc
返回失败NULL
失败,则会造成内存泄漏。 while (fgets (buf, MAXC, fp)) { /* read each line */
size_t oldlen, newlen; /* old/new string lengths */
if (used == allocated) { /* check if realloc needed */
/* always realloc using a temporary pointer */
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate reallocation */
perror ("realloc-lines");
if (used) { /* if pairs stored */
*n = used; /* set no. stored */
return lines; /* return pointer to struct */
}
free (lines); /* none stored - free structs */
return NULL; /* return NULL */
}
lines = tmp; /* assign realloc'ed block to lines */
allocated *= 2; /* update no. allocated */
}
...
现在,您可以确保分配了一个结构,该结构提供了两个字符指针
old
和new
,您可以为其分配每个单词的存储空间,然后根据需要将起始地址分配给old
或new
。如果愿意,可以使用strtok
,但是如果您只处理用空格分隔的单词,则可以使用strchr
或strcspn
查找空格并获取字符数。对于第二个单词new
,它们都可以定位'\n'
,并允许您从头开始对其进行修整。例如,您可以执行以下操作: oldlen = strcspn (buf, " "); /* get no. chars to first space */
if (!(lines[used].old = malloc (oldlen + 1))) { /* alloc storage */
perror ("malloc-oldstr");
break;
}
memcpy (lines[used].old, buf, oldlen); /* copy from buf to old */
lines[used].old[oldlen] = 0; /* nul-terminate */
oldlen++; /* increment past space in buf */
newlen = strcspn (buf + oldlen, "\n"); /* get no. of chars to \n */
if (!(lines[used].new = malloc (newlen + 1))) { /* alloc storage */
perror ("malloc-oldstr");
break;
}
memcpy (lines[used].new, buf + oldlen, newlen); /* copy buf to new */
lines[used].new[newlen] = 0; /* nul-terminate */
used++; /* increment used struct counter */
}
...
而已。最后更新
used
计数器将完成您的读取循环。现在在返回之前,您只需要使用已使用的计数更新指针并返回lines
,例如 *n = used; /* update pointer with no. used before return */
return lines; /* return pointer to structs containing pairs */
}
完全可以做到:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REALLOCSIZE 2 /* if you need a constant, #define one (or more) */
#define MAXC 1024
typedef struct ChangedWord {
char *old;
char *new;
} word_change_t;
word_change_t *readfile (FILE *fp, size_t *n);
int main (int argc, char *argv[]) {
size_t length = 0; /* number of structs */
word_change_t *words = NULL; /* pointer to struct */
/* 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;
}
words = readfile (fp, &length); /* read all pairs of words into words */
if (!words) {
fputs ("error: readFile() failed.\n", stderr);
return 1;
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
printf ("\n%zu words_chang_t:\n\n", length); /* number of structs */
for (size_t i = 0; i < length; i++) { /* loop over each */
printf ("words[%2zu] : %s %s\n", /* output word pair */
i, words[i].old, words[i].new);
free (words[i].old); /* free old */
free (words[i].new); /* free nwe */
}
free (words); /* free pointers */
return 0;
}
word_change_t *readfile (FILE *fp, size_t *n)
{
size_t allocated = REALLOCSIZE, /* initial no. of structs */
used = 0; /* counter tracking no. used */
char buf[MAXC]; /* buf to hold line */
/* allocate initial struct */
word_change_t *lines = malloc (allocated * sizeof *lines);
if (!lines) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
size_t oldlen, newlen; /* old/new string lengths */
if (used == allocated) { /* check if realloc needed */
/* always realloc using a temporary pointer */
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate reallocation */
perror ("realloc-lines");
if (used) { /* if pairs stored */
*n = used; /* set no. stored */
return lines; /* return pointer to struct */
}
free (lines); /* none stored - free structs */
return NULL; /* return NULL */
}
lines = tmp; /* assign realloc'ed block to lines */
allocated *= 2; /* update no. allocated */
}
oldlen = strcspn (buf, " "); /* get no. chars to first space */
if (!(lines[used].old = malloc (oldlen + 1))) { /* alloc storage */
perror ("malloc-oldstr");
break;
}
memcpy (lines[used].old, buf, oldlen); /* copy from buf to old */
lines[used].old[oldlen] = 0; /* nul-terminate */
oldlen++; /* increment past space in buf */
newlen = strcspn (buf + oldlen, "\n"); /* get no. of chars to \n */
if (!(lines[used].new = malloc (newlen + 1))) { /* alloc storage */
perror ("malloc-oldstr");
break;
}
memcpy (lines[used].new, buf + oldlen, newlen); /* copy buf to new */
lines[used].new[newlen] = 0; /* nul-terminate */
used++; /* increment used struct counter */
}
*n = used; /* update pointer with no. used before return */
return lines; /* return pointer to structs containing pairs */
}
在没有供您测试的输入文件的情况下,我仅创建了一个来自
/var/lib/dict/words
的2列单词文件,其中每个单词与第一个单词之间用空格分隔。测试提供:使用/输出示例
$ ./bin/words_old_new dat/words2col.txt
50 words_chang_t:
words[ 0] : bobbysock Bakersville
words[ 1] : ailourophobes Basil
words[ 2] : Baathism begrim
words[ 3] : arachnid archiepiscopacy
words[ 4] : backbencher allowance
words[ 5] : babyproofs binocles
words[ 6] : bookplate animalcula
words[ 7] : aphetizes brecciating
words[ 8] : Aeginetans backpacking
words[ 9] : befortune besnows
<snip>
words[48] : besottedly bear
words[49] : antiderivative Alleghenies
(为简短起见,删去了38个字)
内存使用/错误检查
在您编写的任何可以动态分配内存的代码中,对于任何分配的内存块,您都有2个责任:(1)始终保留指向该内存块起始地址的指针,因此,(2)在不分配该内存块时可以将其释放需要更长的时间。
必须使用内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后确认您可以释放已分配的所有内存。
对于Linux,
valgrind
是通常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。$ valgrind ./bin/words_old_new dat/words2col.txt
==26460== Memcheck, a memory error detector
==26460== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26460== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==26460== Command: ./bin/words_old_new dat/words2col.txt
==26460==
50 words_chang_t:
words[ 0] : bobbysock Bakersville
words[ 1] : ailourophobes Basil
words[ 2] : Baathism begrim
words[ 3] : arachnid archiepiscopacy
words[ 4] : backbencher allowance
words[ 5] : babyproofs binocles
words[ 6] : bookplate animalcula
words[ 7] : aphetizes brecciating
words[ 8] : Aeginetans backpacking
words[ 9] : befortune besnows
<snip>
words[48] : besottedly bear
words[49] : antiderivative Alleghenies
==26460==
==26460== HEAP SUMMARY:
==26460== in use at exit: 0 bytes in 0 blocks
==26460== total heap usage: 109 allocs, 109 frees, 8,687 bytes allocated
==26460==
==26460== All heap blocks were freed -- no leaks are possible
==26460==
==26460== For counts of detected and suppressed errors, rerun with: -v
==26460== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细检查一下,如果您还有其他问题,请告诉我。
关于c - 将文件的行存储为c中的结构数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58823196/