我需要一些代码的帮助。我有一个文本文件,如:
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;
}
答案 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 ...)。但只有在您确定所读取的数据(输入格式)时才使用它。