无法将数据从文件读入结构

时间:2014-08-09 12:46:02

标签: c

我需要一些代码的帮助。我有一个文本文件,如:

131782 Mathematics 59
075160 Mathematics 92
580313 Physics 63
073241 Mathematics 32
487476 Mathematics 73
075160 Physics 98
472832 English 44
...

如果有80行数据对应20名学生,则有4个不同的科目和他们为该科目获得的分数。我想以这样的方式安排数据(学生ID在他们的ID旁边有四个标记):

第一张表:

868717 2 37 49 15
472832 44 88 91 95
580313 91 97 63 78
...

所以会有二十行数据。然后是另一张表,找出每个学生的平均分,并找到另一个学生的平均分。现在我对此没有任何问题,我遇到的麻烦就是将我的数据放入结构中。如果你查看我的代码,我会以字符串形式读取数据并将其存储在char字符串数组中。我仍然要使用malloc()进行动态分配,但是当我完成此操作时,我会这样做。我想在数组中搜索学生ID并使用update_student()函数填充他们的标记,但我在如何读取包含数据到函数中的字符串时遇到了麻烦,因为那里有#s; s每个字符串的三个部分,学生ID主题和标记。如果你查看我的代码的结尾,这就是我所拥有的。我计划设置for循环以将数据响铃到update_student()函数,但似乎无法设置代码。

有没有人有任何想法将这些数据存入我的结构?

#include <stdio.h>
#include <string.h>

#define STUDENTS (20)
#define NOBODY_ID (-1)
#define EMPTY_MARK (-1)
#define NO_STUDENT (-1)  

#define SUBJECT_ENGLISH (0)
#define SUBJECT_MATHEMATICS (1)
#define SUBJECT_PHYSICS (2)
#define SUBJECTS (3)

#define STRING_LENGTH 23
#define NUMBER_OF_LINES 80

struct student
{
    int id;
    int marks[SUBJECTS];
};  struct student db[STUDENTS];

void init_db()
{
    int i, j;
    for( i = 0; i < STUDENTS; ++i )
    {
        db[i].id = NOBODY_ID;
        for( j = 0; j < SUBJECTS; ++j )
        {
            db[i].marks[j] = EMPTY_MARK;
        }
    }
}

int find_student(const int id)
{
    int i;
    for( i = 0; i < STUDENTS; ++i )
    {
        if( db[i].id == id )
        {
            return i;
        }
    }
    return NO_STUDENT;
}

void update_student(const int id, const int subject, const int mark)
{
    int idx = find_student(id);
    if( idx == NO_STUDENT )
    {
        idx = find_student(NOBODY_ID);
        db[idx].id = id;
    }

    db[idx].marks[subject] = mark;
}

int main(void)
{
    FILE *input_file;

    int i=0;
    char buffer[STRING_LENGTH];
    char strings[NUMBER_OF_LINES][STRING_LENGTH];

    if((input_file=fopen("C:\\marks\\marks.txt", "r"))==NULL)
        perror("File open failed!");
    else
    {
        while(fgets(buffer, STRING_LENGTH, input_file)!=NULL)
        {
            strcpy(strings[i], buffer);
            i++;
        }
    }

    for() /*Here is where I need help with*/
    update_student();

    return 0;
}

3 个答案:

答案 0 :(得分:0)

有很多种方法,但就你而言,我认为你需要专注于走遍strings中存储的角色,首先穿过行(i)然后走下去字符。

创建一个缓冲区,在字符串中保存“word”,并专注于检测单词是否已完成(读取空格),如果没有,则将字符复制到缓冲区中。一旦你检测到一个完成的单词,现在你有了代表(可能)你的数字或字符串的字符。

然后是将正确的单词转换为正确的C数据类型。

一旦拥有了正确的C数据类型,就应该很容易将它们分配到结构中的正确位置。

答案 1 :(得分:0)

未编译但应该处理很少或没有更改。 基本上你将字符串sscanf为int + string + int,然后将字符串与选项进行比较,然后相应地更新update_student。 顺便说一句,请注意你说了四个科目,但只有三个科目。

#define SUBJECT_ENGLISH_STR "ENGLISH"
#define SUBJECT_MATHEMATICS_STR "MATHEMATICS"
#define SUBJECT_PHYSICS_STR "PHYSICS"

int j;
int id = 0;
int mark = 0;
char subject_str[20] = {0};
int subject = 0;

for(j=0 ; j<NUMBER_OF_LINES ; j++)
{
  if(strings[j][0] == "\0")
    break;
  if(sscanf(strings(j), "%d %s %d", &id, subject_str, &mark) ==3)
  {
    if(strncmp(subject, SUBJECT_ENGLISH_STR) == 0
      subject = SUBJECT_ENGLISH;
    else if(strncmp(subject, SUBJECT_MATHEMATICS_STR) == 0
      subject = SUBJECT_MATHEMATICS;
    else if(strncmp(subject, SUBJECT_PHYSICS_STR) == 0
      subject = SUBJECT_PHYSICS;
    else
      continue; //error handling
    update_student(id, subject, mark)
  }
  else
    continue;//error handling
}

答案 2 :(得分:0)

首先,对您的代码提出一些意见。

您谈论了4个不同的主题,但您的代码只有3个:#define SUBJECTS (3)

在这种情况下,为什么不使用枚举而不是定义?

enum Subject { ENGLISH, MATHEMATICS, PHYSICS };

您可能希望坚持使用一种命名约定:

#define STUDENTS (20)
#define SUBJECTS (3)
#define NUMBER_OF_LINES 80 // I think we all know this is a number

如果你做得足够好,你可以安全地默认初始化学生“数据库”,并在迭代数组时用正确的值写出所有内容。通过这样做,您可以删除NOBODY_ID以及NO_STUDENT。但是,如果没有相应的标记,您可能希望保留EMPTY_MARK(但如果有80个不同的数据行,则不会发生这种情况)。如果可以少于20个学生ID(并且可能将其重命名为“EMPTY_ID”),您可能还想保留NOBODY_ID。

另外,当你打开一个文件时,可以像你一样检查错误,但是如果实际发生了错误,你通常要退出该程序。

// Your code
if((input_file=fopen("C:\\marks\\marks.txt", "r"))==NULL)
    perror("File open failed!");
else // You don't need this, just exit !
{
    // ...
}

// Exiting when catching an error
input_file = fopen("C:\\marks\\marks.txt", "r");
if (input_file == NULL) {
    perror("File open failed!");
    return -1;
}
// ...

顺便说一句,我认为Windows接受文件路径中的斜杠:"C:/marks/marks.txt"

现在关注你的问题。

首先,您需要一种识别与主题相对应的字符串的方法(例如“物理”)。你可以简单地使用类似const char*的东西 然后,您希望能够将一串数字转换为数字,可能是一个独立的函数(提示:每个新数字只乘以10)。
最后,您必须隔离每一行的每个数据。在您的情况下,似乎空格(或换行符)将两个数据分开。

如果你想知道某个角色的类别,ctype.h中有一些功能;例如,如果逐个字符地读取文件,则可以使用isspace来方便所有看似分隔数据(包括换行符)的内容。您还可以使用isalpha和isdigit来验证输入,也可以使用tolower或toupper来比较字符串。但是,如果您只想检测空格,则不需要== ' '以外的任何内容。

另一种方法是使用the scanf family的函数(fscanf,sscanf ...)。但只有在您确定所读取的数据(输入格式)时才使用它。