为什么我的fscanf不读?

时间:2014-03-15 05:01:45

标签: c parsing input token scanf

我正在尝试将file.txt中的信息读入BST。 首先,我考虑将信息保存到临时小型数组中,而不是将这些值分配给树节点。显然出现了问题,我确信有一件事是不正确的正则表达式,可能是别的东西。请帮我弄清楚如何将数据保存到BST,我这个项目真的很晚。

输入file.txt中包含的数据样本:

3800 Lee, Victor; 2.8
3000 Brown, Joanne; 4.0
1300 South, Frankie; 2.6
4000 Trapp, Dave; 3.9

必须阅读并解析它们:1)int= id,2)char= name,3)float= gpa

他们必须妥善保存,没有任何符号,所以当我在下一个功能中搜索时,它不会给我任何错误。

以下是我的代码(更新):

int readFile(BST_TREE *list) {
int id  = 0;
int val = 0;
int ln  = 0;
float gpa = 0.0;
char name[MAX_NAME_LEN];
STUDENT *stuptr = 0;
char line[MAX_LEN];

stuptr = (STUDENT*)malloc(sizeof(STUDENT));


FILE *fp = fopen("gpa.txt", "r");
if(fp == NULL) {
    printf("Error in opening file\n");
    return 1;
}

while (fgets(line, sizeof(line), fp)) {

ln++;
if (sscanf(line, "%d %[^;]; %f ", &id, name, &gpa) < 3) {
    printf("Syntax error in line %d.\n", ln);
} else {
    printf("1 %4d  %-29s%f\n", id, name, gpa);
  }
 }

    stuptr->id = id;
    strcpy(stuptr->name, name);
    (stuptr->gpa) = gpa;
    return 0;
}

// Typedef
 typedef struct
{
    int   id;
    char  name[MAX_NAME_LEN];
    float gpa;
} STUDENT;

3 个答案:

答案 0 :(得分:2)

这是因为fscanf的格式字符串错误。你需要的是"%d %[^;];%f "。 这意味着

  1. 首先读取十进制整数并存储它。
  2. 然后阅读并丢弃任意数量的空白字符。
  3. 然后匹配除分号;以外的任何数量的所有字符并存储它。写入字符的缓冲区必须足够大,以便读取字符加上fscanf添加的终止空字节。
  4. 然后匹配分号;并丢弃它
  5. 然后读取一个丢弃任意数量的前导空白字符的浮点值。
  6. 然后阅读并丢弃任意数量的空白字符。
  7. 如您所见,如果文件中的任何行格式不正确,则fscanf将因匹配失败而失败,并可能将文件指针留在未知位置。

    您应该使用fgets来读取一行,然后使用sscanf来处理该行并从中读取元素。这是你应该怎么做的。但是,您必须事先知道文件中一行的最大长度。

    您的代码段中还有其他错误。

    1. 您正在分配内存以存储指针,而不是由此构建结构 - stuPtr = (STUDENT*)malloc (sizeof (stuPtr));您应该执行以下操作。另外,不要投射malloc的结果并检查NULL的结果 -
      stuPtr = malloc(sizeof *stuPtr);

    2. 您无法分配数组。您应该复制数组的每个字符。此外,结构成员name是一个字符,而不是一个数组。
      stuPtr->name = tempName;
      在上面的语句中,tempName是一个数组,它衰变成指向其第一个元素的指针。该指针被分配给结构成员name,这是一个字符 - 这显然是一个错误。您应该将结构成员name的类型更改为char数组。

    3. 这是我修改过的修改版本。

      #define MAX_LEN 100
      #define MAX_NAME_LEN 40
      
      typedef struct {
          int id;
          char name[MAX_NAME_LEN]; 
          float gpa;
      } student;   
      
      void add_student(BST_TREE *list) {
          int id, val;
          float gpa;
          char name[MAX_NAME_LEN]
          student *stuptr;
          char line[MAX_LEN];
      
          FILE *fp = fopen("gpa.txt", "r")       
          if(fp == NULL) {
              printf("error in opening file\n");
              return 1;
          }       
      
          while(fgets(line, sizeof line, fp) != NULL)
              stuptr = malloc(sizeof *stuptr);
              if(stuptr == NULL) {
                  printf("not enough memory to allocate\n");
                  return 1;
              }
              val = sscanf(line, "%d %[^;];%f", id, name, gpa)
              if(val != 3) {
                  printf("input format error\n");
                  // handle it
              }
              stuptr->id = id;
              strcpy(stuptr->name, name);
              stuptr->gpa = gpa;
          }
      }
      

答案 1 :(得分:1)

关于阅读文件:如果未能转换所有占位符,则返回值不一定为EOF,并且您的格式字符串与文件中的格式不对应。

fscanf返回成功转化的次数。当输入用完时,它返回EOF。在您的情况下,格式字符串将仅转换第一个项目,因此返回值为1.您正在寻找3.(当fscanf未转换所有项目时,它会被卡住。当您尝试阅读时下一行,你真的试着从fscanf放弃转换的地方读取,这通常不是你想象的那样。)

文件中条目的格式为:

3800 Lee, Victor; 2.8

您的格式字符串是:

"%4d, %[^\t\n,;]c %f"

这里有几件事。

  • ID后面没有逗号,但您的格式字符串有一个。
  • 括号后的虚假c无法理解
  • 您将逗号视为以名称结尾的字符之一,但所有名称都包含逗号。
  • 名称条目以分号结尾,因此格式字符串中应该有一个分号c
  • 超过30个字符的名称将导致缓冲区溢出。最好指定最大字符串长度。

所以你的格式应该是这样的:

"%d %29[^;]; %f "

全力以赴:

while (fscanf (f, "%d %[^;]; %f ", &id, name, &gpa) == 3) {
    printf("1 %4d  %-20s%f\n", id, name, gpa);
}

这将停止在第一行无法转换,所以如果你有一个拼写错误,或许在第一行中丢失分号,则不会读取任何内容。这是不可取的。由于您的文件格式是基于行的,因此我建议先阅读fgets行,然后使用sscanf解析该行:

char buffer[80];
int ln = 0;

while (fgets(buffer, sizeof(buffer), f)) {
    int id;
    float gpa;
    char name[30];

    ln++;
    if (sscanf(buffer, "%d %[^;]; %f ", &id, name, &gpa) < 3) {
        printf("Syntax error in line %d.\n", ln);
    } else {
        printf("1 %4d  %-20s%f\n", id, name, gpa);
    }
}

现在跳过坏行,并显示(简洁)错误消息。

哦,专注于真正的问题,不要过于花哨。虽然像这样塞满了所有东西:

if(!(fpStudents = fopen("gpa.txt", "r")))
    printf("-*- Could not open file for reading! -*-\n"), exit(100);

可能会给你h4xx0r学分,你的代码会更好地呈现为:

fpStudents = fopen("gpa.txt", "r");
if (fpStudents == NULL) {
    printf("-*- Could not open file for reading! -*-\n");
    exit(100);
}

您可以在glace中看到所需的一切:打开文件。如果出现问题,请打印一条消息并退出。我很难从你的代码中读到这些而没有眯眼。条件和逗号运算符中的赋值是七十年代

答案 2 :(得分:0)

我对你的代码的说明:

sizeof (stuPtr) should be sizeof (STUDENT).

你必须将类型定义传递给大小,分配该类型的字节数。

Here you can find more about that