![](/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.