簡體   English   中英

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

首先,您使用openfopen功能打開文檔文件。

例如: 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;
}

由於您試圖解析包含空格的字符串,而這些空格又與其他字段之間用更多的空格(例如nameaddress )隔開,因此您無法讀取行,然后使用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試圖reallocfree的內存塊,將發生內存錯誤。 復印將為您省去很多麻煩。

在上面的示例中,復制了由getline分配的ln 根據需要分配字符指針以保留 lcpy 的起始地址 如果您要遍歷字符串lcpy (例如lcpy++; ),而不是使用第二個指針,則原始的起始地址將丟失。 當您(或退出程序)嘗試釋放lcpy ,可能會發生大量錯誤(或分段錯誤)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM