![](/img/trans.png)
[英]How Can Read Values from File, store them in a structure and find their average, sum, product etc... using C
[英]C: Read a file and store them into a data structure
所以我有一个带有以下信息的.txt文档(可以是多行,但在这种情况下是3行)
Jane Smith 123 lala land 123-222-1231
Bob Fall 123 blue jay st 812-923-1111
Sally White 1 rose ave. +1-231-2318
我想创建一个读取文件的“读取”功能,然后创建一个将其写入数据结构的“写入”功能。
到目前为止,我有这个:
void read()
{
FILE *file;
file = fopen("fileName", "r");
write(file);
}
void write(FILE *file)
{
}
我想知道如何将每一行存储到数据结构中,因为C不支持向量。 我希望能够创建一个可以打印的打印功能:
1 //line #1 info here
2 //etc
3 //etc
首先,您使用open
或fopen
功能打开文档文件。
例如: fp=fopen (filename,"r");
然后使用fgets
逐行读取。
例如: while(fgets(array,BUFSIZ,fp) != NULL)
读取每一行后,使用sscanf
函数将数据存储在结构中。
例如: sscanf(array," %d %s
”,&var [i] .id,var [i] .name);`
文件中的数据将被加载到结构中。
#include<stdio.h>
#include<string.h>
#define MAXLENGTH 200
typedef struct node{
char query[MAXLENGTH];
}DATA;
typedef struct stack{
DATA data[MAXLENGTH];
int top;
}Stack;
void write(FILE *file,Stack* st)
{
while(!feof(file)){ //Check EOF
st->top++;
fgets(st->data[st->top].query,MAXLENGTH,file); //Scan data line by line and put into data structure
//printf("%s",st->data[st->top].query);
}
}
void read(Stack* st)
{
FILE *file;
file = fopen("h.txt", "r");
write(file,st);
}
int main(){
int i;
Stack st;
st.top = -1;
read(&st);
for(i = 0; i<= st.top; i++){ //DISPLAY DATA
printf("%s\n",st.data[i].query);
}
fflush(stdin);getchar();
return 0;
}
由于您试图解析包含空格的字符串,而这些空格又与其他字段之间用更多的空格(例如name
和address
)隔开,因此您无法读取行,然后使用sscanf
解析字符串。 根本不可能。 使用scanf/sscanf
,字符串的匹配会在第一个whitespace
终止(除非给出了width
说明符),这使其无法解析包含空格的长度可变的字符串。 例如:
Jane Smith 123 lala land 123-222-1231
尝试使用%s
进行解析只会读取Jane
内容。 除非保证您使用固定宽度的列,否则在这种情况下sscanf
将不起作用。
使问题更加复杂的是,字符串不仅包含空格,而且分隔符由多个空格组成。 因此,不幸的是,在这种情况下,您必须使用指针来解析字符串。 怎么样? 从已知信息开始。
唯一可以做到这一点的前提是电话号码包含无空格 。 因此,使用strrchr
(或在字符串和备份的末尾设置指针)可以简单地在电话号码开头之前找到空格。 在此空格之前设置一个end pointer (ep)
,将原始指针前进1
然后将电话号码复制到该结构中。
从ep
开始,向后进行操作,直到找到第一个非空格字符(地址字段的末尾)并在其中设置以null-terminating
字符为止。
下一个已知点是字符串的开头。 从那里开始,找到第一个double-space
。 (假设名称,地址和电话字段均由至少2个空格分隔)。 您知道双精度空格的第一个是名称字段的末尾,在此处设置一个以null-terminating
字符null-terminating
字符。 (您现在可以通过简单地读取字符串的开头将名称读取/复制到结构)
最后,继续前进,直到找到下一个非空格字符。 这是地址的开始。 将地址复制到结构中,即可完成操作。 (每行重复此过程)。
有时在没有理智的定界符的地方,您必须退后一步,简单地使用指针单步执行字符串并逐个处理它。 这是其中一种情况。 查看以下内容,如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EMAX 128
typedef struct entry {
char name[32];
char address[32];
char phone[16];
} entry;
size_t readtxtfile (char *fn, entry *array);
void prn_entries (entry *array);
int main (int argc, char **argv) {
/* validate number of arguments */
if (argc < 2 ) {
fprintf (stderr, "error: insufficient input, usage: %s <filename1>\n", argv[0]);
return 1;
}
/* initialize all variables */
size_t index = 0;
entry contacts[EMAX] = {{{0}, {0}, {0}}};
/* read file into an array of entries,
number of entries, returned to index */
index = readtxtfile (argv[1], contacts);
/* simple print function */
if (index > 0)
{
printf ("\nNumber of entries in contacts : %zu\n\n", index);
prn_entries (contacts);
}
else
fprintf (stderr, "error: no entries read from file '%s'\n.", argv[1]);
return 0;
}
size_t readtxtfile (char *fn, entry *array)
{
if (!fn) return 0; /* validate filename provided */
char *ln = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* max chars to read (0 - no limit) */
ssize_t nchr = 0; /* number of chars actually read */
size_t idx = 0; /* couner for number of entries */
FILE *fp = NULL; /* file pointer to open file fn */
/* open / validate file */
if (!(fp = fopen (fn, "r"))) {
fprintf (stderr, "%s() error: file open failed '%s'.", __func__, fn);
return 0;
}
/* read each line from file */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
/* strip newline or carriage rtn */
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0;
/* create a copy of ln to preserve start address */
char *lcpy = strdup (ln);
if (!lcpy) {
fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
continue;
}
char *p = strrchr (lcpy, ' '); /* find last space in line */
char *ep = p - 1; /* set end pointer 1 before */
p++; /* advance to next char */
strncpy (array[idx].phone, p, strlen (p)); /* copy p to phone */
while (ep > lcpy && *ep == ' ') ep--; /* find first space after addr */
*(++ep) = 0; /* null-terminat at that space */
p = lcpy; /* start at beginning of string and find first double-space */
while (*(p + 1) && !(*(p + 1) == ' ' && *p == ' ')) p++;
*p = 0; /* null-terminate at first space */
while (*(++p) == ' '); /* find first char in addr */
strncpy (array[idx].address, p, strlen (p)); /* copy p to address */
strncpy (array[idx].name, lcpy, strlen (lcpy)); /* copy lcpy to name */
free (lcpy); /* free memory allocated by strdup */
lcpy = NULL; /* reset pointer NULL */
idx++; /* increment entry index */
if (idx == EMAX) /* check if EMAX reached & return */
{
fprintf (stderr, "%s() warning: maximun number of entries read\n", __func__);
break;
}
}
if (ln) free (ln); /* free memory allocated by getline */
if (fp) fclose (fp); /* close open file descriptor */
return idx;
}
/* print an array of character pointers. */
void prn_entries (entry *array)
{
register size_t n = 0;
while (strlen (array[n].name) > 0)
{
printf (" (%2zu.) %-32s %-32s %s\n", n, array[n].name, array[n].address, array[n].phone);
n++;
}
}
输出量
$ ./bin/read_entries dat/nmaddph.txt
Number of entries in contacts : 3
( 0.) Jane Smith 123 lala land 123-222-1231
( 1.) Bob Fall 123 blue jay st 812-923-1111
( 2.) Sally White 1 rose ave. +1-231-2318
注意:使用getline
或在任何时候为字符串动态分配空间时,都需要进行复制,然后才能使用不保留字符串原始开头的功能(例如strtok
或手动遍历字符串)更改该内存块与字符串变量)。 getline
为您分配ln
的内存(如果最初设置为NULL),则getline
负责释放它。 如果你改变了起始地址字符串,或将它的一部分不可达,那么当getline
试图realloc
或free
的内存块,将发生内存错误。 复印将为您省去很多麻烦。
在上面的示例中,复制了由getline
分配的ln
。 根据需要分配字符指针以保留 lcpy
的起始地址 。 如果您要遍历字符串lcpy
(例如lcpy++;
),而不是使用第二个指针,则原始的起始地址将丢失。 当您(或退出程序)尝试释放lcpy
,可能会发生大量错误(或分段错误)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.