繁体   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