【问题标题】:How to Dynamically Allocate a string within a struct with fscanf or fgets?如何使用 fscanf 或 fgets 在结构中动态分配字符串?
【发布时间】:2016-01-08 03:56:11
【问题描述】:

我正在尝试使用fscanf 在结构中分配一个字符串, 我试过这个:

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <Windows.h>
#include <string.h>

typedef struct _SPerson {
  char *name;
  char *surname;
  char *id;
  char *telephone;
}SPerson;

void main (void) {
  unsigned int ne;
  SPerson Archive[1000];
  Load(Archive,&ne);
}

int Load(SPerson Archive[],unsigned int *ne) {
  int k,i=0;
  char s[4][20];
  FILE *f;
  f = fopen("archive.txt","r");
  if(f==0) return 0;

  while((k=fscanf(f,"%s %s %s %s",s[0],s[1],s[2],s[3]))==4) {
    Archive[i].id = (char*) malloc( sizeof(char) *strlen(s[0])); 
    Archive[i].id =s[0];
    Archive[i].name = (char*) malloc( sizeof(char) *strlen(s[1])); 
    Archive[i].name = s[1];
    Archive[i].surname = (char*) malloc( sizeof(char) *strlen(s[2])); 
    Archive[i].surname = s[2];
    Archive[i].telephone = (char*) malloc( sizeof(char) *strlen(s[3])); 
    Archive[i].telephone =s[3];
    i++;    
  }

  *ne = i;
  fclose(f);
  return 1;
}

也许在我的脑海中它是正确的,但是在加载数据时出现了问题,那么这是动态读取字符串的正确且清晰的方法吗? 我想用fgets,但是我的字符串是用空格隔开的,所以我需要实现另一个函数,split。谁能帮帮我?

【问题讨论】:

  • 永远不要使用scanf 函数,它们按设计被破坏了。你想要fgets + strtok + strdup。另外,sizeof(char)根据定义是 1,所以永远不要写它。
  • 您是否收到任何错误或警告? ??
  • 输入文件的示例显示它是如何分隔的将有所帮助。如果所有行都包含相同的字段集,则只需几行。
  • 您所有的内存分配都太短了 1 个字节(您需要分配 strlen(name)+1 字节)。然后使用赋值而不是strcpy() 泄漏内存。此外,您不断地覆盖相同的变量,因此所有数据最终看起来就像读取的最后一行。您没有确保格式字符串没有缓冲区溢出。您应该检查您的 scanf() 版本是否足够类似于 POSIX 以支持 %ms,其中 scanf() 为您分配内存 - 但您传递的是 char ** 而不是 char *
  • 另外,Archive[i].name = s[1]; 并没有做你认为的那样......

标签: c string allocation scanf


【解决方案1】:
 while((k=fscanf(f,"%s %s %s %s",s[0],s[1],s[2],s[3]))==4) {
     //your code
     i++;    
 }

使用fgets 代替这个来读取完整数据,然后使用strtok 对其进行标记-

char data[1024],*token;
 int j=0;
while(fgets(data,sizeof data,f)!=NULL){         //read from file
       token=strtok(data," ");                  //tokenize data using space as delimiter
       while(token!=NULL && j<4){
            j=0;
            sprintf(s[j],"%s",token);          //store it into s[i]
            j++;
            token=strtok(NULL," ");
       }
       Archive[i].id = malloc(sizeof *Archive[i].id * (strlen(s[0])+1));    //allocate memory 
       strcpy(Archive[i].id,s[i]);          //copy at that allocated memory
         //similar for all
       i++;
}

这可以用来代替你的循环。

注意 - 不要这样做 -

 Archive[i].id = (char*) malloc( sizeof(char) *(strlen(s[0])+1)); 
 Archive[i].id =s[0];         <-- 2.

2. 语句之后,您将失去对先前分配的内存的引用,并且将无法free 它 - 导致内存泄漏。

这对于以下所有此类语句都是相同的。

【讨论】:

    【解决方案2】:

    几个快速改进的建议:

    1) void main (void) 真的不是main 的一个好的原型。使用:

    int main(void);
    

    或者:

    int main(int argc, char **argv);
    

    2) 不需要cast the return of [m][c][re]alloc in C
    您的代码中的这一行:

    Archive[i].id = (char*) malloc( sizeof(char) *strlen(s[0]));
                    ^^^^^^^         ^^^^^^^^^^^^^^              //^^^ == remove
    

    应该写成:

    Archive[i].id = malloc( strlen(s[0]) + 1);//no cast, and sizeof(char) is always == 1
                                              //"+ 1" for NULL termination
    

    3) 建议使用 fgets()strtok()strcpy() 作为读取、解析和复制字符串的最小方法从文件到结构成员:

    注意:这里会有很多对malloc()的调用,并且 每个人都必须在某个时候被释放。为了避免这一切,它会 如果您的结构包含具有硬编码堆栈的成员,那就更好了 记忆:

    typedef struct
    {
        char name[80];
        char surname[80];
        char id[80];
        char telephone[80];
    }SPerson;
    

    但假设你有理由使用 heap 内存,这里有一种使用你定义的结构的方法:

    char line[260];//line buffer (hardcoded length for quick demo)
    char *tok={0};//for use with strtok()
    int len = 0;//for use with strlen()
    FILE *f;
    
    f = fopen("archive.txt","r");//did not have example file for this demo
                                 //therefore do not have delimiters, will guess
    if(f==0) return 0;
    
    i = 0;//initialize your index
    while(fgets(line, 260, f))
    {
        tok = strtok(line, " ,\n\t");// will tokenize on space, newline, tab and comma
        if(tok)
        {
            len = strlen(tok);
            Archive[i].id = malloc(len + 1);//include space for NULL termination
            strcpy(Archive[i].id, tok);//note correct way to assign string
            //(Archive[i].id = tok is incorrect!!)
        }
        else {//handle error, free memory and return}
        tok = strtok(NULL, " ,\n\t");//note NULL in first arg this time 
        if(tok)
        {
            len = strlen(tok);
            Archive[i].name= malloc(len + 1);//include space for NULL termination
            strcpy(Archive[i].name, tok);
    
        }
        else {//handle error, free memory and return}
        //...And so on for rest of member assignments
        //
        i++;//increment index at bottom of while loop just before reading new line
    }
    

    【讨论】:

      猜你喜欢
      • 2017-02-15
      • 1970-01-01
      • 2017-06-14
      • 1970-01-01
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      相关资源
      最近更新 更多