Trouble with populating student's marks - c

This is in response to a similar threat I posted the other day with reading a file into the requisite data structure with the file data like so, I can't remember who said it but yes there's four subjects. (I wanted to post an overall reply to all responses but could only comment on each post made):
131782 Mathematics 59
075160 Mathematics 92
580313 Physics 63
073241 Mathematics 32
487476 Mathematics 73
075160 Physics 98
472832 English 44
...
I'm using fscanf() now to parse the data and this is a much better approach. I made another thread yesterday about removing duplicate strings. I've scrapped that idea now and just used qsort on the student IDs and created a for loop that skips every four elements and rings the unique student IDs into the structure. I did a printf() command earlier and they're successfully stored. Now I've got the IDs stored I'm now ready to search for that ID and populate their marks and I "think" it's almost there except for a slight problem inside the update_student() function.
If you look at my code, or even compile it, it's not liking the line that's supposed to populate the mark for the student, student_data[idx].marks[buffer_subjects]=marks. But buffer_subjects is a string but if you look at my defines those strings are constants which is the whole idea when it gets to this stage.
How can I fix this?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STUDENTS 20
#define COMPUTING 0
#define ENGLISH 1
#define MATHEMATICS 2
#define PHYSICS 3
#define SUBJECTS 4
#define ROWS 80
#define SIZE 100
int string_compare(void const *x, void const *y)
{
return strcmp(*(char**)x, *(char**)y);
}
struct student
{
char student_ID[SIZE];
int marks[SUBJECTS];
};struct student student_data[STUDENTS];
int find_student(char buffer_IDs[])
{
int j;
for(j=0;j<STUDENTS;j++)
if(strcmp(student_data[j].student_ID,buffer_IDs)==0)
return j;
}
void update_student(char buffer_IDs[], char buffer_subjects[], int marks[])
{
int idx = find_student(buffer_IDs);
student_data[idx].marks[buffer_subjects] = marks;
}
int main(void)
{
FILE *input;
int i,j, data_items;
char buffer_IDs[ROWS][SIZE];
char buffer_subject[ROWS][SIZE];
int marks[ROWS][SIZE];
char *string_ptrs[ROWS];
if((input=fopen("C:\\marks\\marks.txt","r"))==NULL)
perror("File open failed!");
else
{
for(i=0;i<ROWS;i++)
{
while((data_items=fscanf(input, "%s %s %d", buffer_IDs[i], buffer_subject[i], marks[i])!=3));
printf("%s %s %d\n", buffer_IDs[i], buffer_subject[i], *marks[i]);
string_ptrs[i]=buffer_IDs[i];
}
putchar('\n');
qsort(string_ptrs, sizeof(string_ptrs)/sizeof(char*), sizeof(char*), string_compare);
for(i=0;i<ROWS;i=i+4)
{
j=0;
strcpy(student_data[j].student_ID,string_ptrs[i]);
printf("%s\n",student_data[j].student_ID);
j++;
}
for(i=0;i<ROWS;i++)
update_student(buffer_IDs[i], buffer_subject[i], marks[i]);
}
return 0;
}
> Blockquote

There are numerous failures in this block of code. I would seriously recommend building it up block by block, and using a debugger to confirm that the data in each stage is as expected, and not proceeding till each of the lower level blocks work (and do not produce compile errors).
Running it through GDB with some sample data suggests problems even at the point of reading in and parsing data from the file on disk.
We can assist with individual issues as they arise from this approach.

Related

char** : Parse the entire memory properly

Im am trying to get some words from a txt file and store them into a char** . Although I think my array allocates corectly room for 100 words , my last one fails to be printed in a human-readable way.Also,if you look carefully there are 4 characters that are being printed (so this indicates that the correct size of memory refers to the lenght of the word,which is 4). Can you please point out for what am I missing ? I am running in linux Ubuntu 20.04 (VirtualBox).
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
#define N 100
#define STRLIM 60
char** cat;
int fd;
int main(int argc, char const *argv[])
{ int i;
cat=(char**)malloc(sizeof(char)*N);
fd=open("dictionary.txt",O_RDONLY);
if(fd==-1){
printf("fd error %d" , fd);
perror("Error:");
}
for(i=0;i<N;i++){
cat[i]=(char*)malloc(sizeof(char)*STRLIM);
read(fd,cat[i],40);
printf("%s", cat[i]);
}
printf("the value of i is: %d",i);
return 0;
}//end of main
PS: I am getting a warning that which goes like this :
v1.c:25:9: warning: implicit declaration of function ‘read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
Is it really necessary for "read" to have an unsigned int as a third arguement?
The words of the "dictionary.txt" are these (there a hundred words down bellow) :
shocking
boundary
post
dapper
zoom
bashful
damaging
sore
unadvised
fresh
birthday
wrathful
hook
nose
wonder
doubt
important
synonymous
bell
dare
selective
raspy
unused
heavy
wiggly
land
coal
humorous
humdrum
plausible
languid
depressed
imminent
helpless
parsimonious
verse
deep
tricky
window
sedate
torpid
earsplitting
protect
breath
drawer
pear
bomb
drum
can
superficial
crook
stimulating
majestic
innocent
steep
robin
weak
tumble
geese
bulb
channel
frantic
obtain
shave
nerve
boiling
picture
sand
measly
tasteful
steadfast
hallowed
rabid
fax
aspiring
utter
wave
confused
zephyr
absent
lamentable
idea
oatmeal
comfortable
cars
reduce
colossal
heat
lettuce
simple
homeless
decision
cellar
ruthless
time
railway
possible
silly
chance
food
Sorry, wasn't able to add a comment...
you won't get a warning if you include unistd.h.
I don't know why you want to use the malloc function in this code (maybe for another part of the code).
It is better to have a buffer and do something like this:
char buffer[STRLIM];
int x;
while ((x = read(fd, buffer, STRLIM)) > 0) {
write(1, buffer, x);
}
where x is the number of bytes that the read function was able to read from the file, and then we write x bytes to the output.

Check only last 3 digits of sensor output

I have a library from WiringPi for DHT11 sensor and I need to modify condition which checks if the value read from sensor is good.
Sometimes the library reads bad values which are 255.255,255.255 or 55,255.255 etc.
sample output
There is the condition in the library:
if(counter==255)
break;
But it doesn't work if the value is e.g. 55,255.255
How can I modify this condition the check last 3 digits of output?
If the output is wrong, there are always "255" at the end of value.
I tried to add conditions like
if(counter==255)
break;
else if(counter==255.255)
break;
But it doesn't solve all possible situations and I realy don't know anything about C/C++
Here is the whole library:
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define MAX_TIME 85
#define DHT11PIN 7
#define ATTEMPTS 5
int dht11_val[5]={0,0,0,0,0};
int dht11_read_val()
{
uint8_t lststate=HIGH;
uint8_t counter=0;
uint8_t j=0,i;
for(i=0;i<5;i++)
dht11_val[i]=0;
pinMode(DHT11PIN,OUTPUT);
digitalWrite(DHT11PIN,LOW);
delay(18);
digitalWrite(DHT11PIN,HIGH);
delayMicroseconds(40);
pinMode(DHT11PIN,INPUT);
for(i=0;i<MAX_TIME;i++)
{
counter=0;
while(digitalRead(DHT11PIN)==lststate){
counter++;
delayMicroseconds(1);
if(counter==255)
break;
}
lststate=digitalRead(DHT11PIN);
if(counter==255)
break;
// top 3 transistions are ignored
if((i>=4)&&(i%2==0)){
dht11_val[j/8]<<=1;
if(counter>16)
dht11_val[j/8]|=1;
j++;
}
}
// verify checksum and print the verified data
if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF)))
{
printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);
return 1;
}
else
return 0;
}
int main(void)
{
int attempts=ATTEMPTS;
if(wiringPiSetup()==-1)
exit(1);
while(attempts)
{
int success = dht11_read_val();
if (success) {
break;
}
attempts--;
delay(500);
}
return 0;
}
No single variable in your code can hold "255.255", that would require a string or a float. You are obviously referring to the output of
printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);.
This printf can never produce a three-value output like 55,255.255.
I assume that your output would be 55.255,255.255.
This in turn means that in case of error you will find the "last three digits" in dht11_val[3].
If my assumption is not correct please provide much more detail on the error circumstances.
On the other hand, I suspect that looking for that value is not the solution for your problem either. The function is more complicated. The value of 255 seems the result of an endless loop which is detected by breaking early at counter = 255. So I am pretty sure that checking "the last three digits" is a LESS precise check than what is already implemented.

Galton Box/Bean Machine-C

I want to code a simple bean machine program. The program will accept user input for the number of balls and the number of slots, and will calculate the path of each ball. The number of balls in each slot will be printed as a histogram as well.
I tried my best to keep the code short and sweet, yet the best I have managed is 112 lines long. When I ran my code, I received no errors. However, the output seems to have run into some sort of an infinity loop (The '#' symbol which was used to represent numbers in the histogram keeps on printing forever for some reason unknown to me).
Apparently, there is something wrong with my logic somewhere... or a silly little mistake in syntax(but it would have shown up as error, wouldn't it?)... In a nutshell, I cannot figure out exactly what is the problem. (I attempted to walk through the whole code process from start to finish, but my mind kept getting tangled up somewhere in the middle of the code, nowhere near the end of the code either).
Where exactly does my logic go wrong?(Or have I taken the wrong approach to the whole problem?) I do not wish to know the correct code, so that I am able to learn during the whole process of re-editing my code.
Any help (hopefully no model-code answers though), even as a single comment, is tremendously appreciated! :)
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
#include <stdbool.h>
#include <time.h>
//Pls excuse my extensive use of libraries even though I don't really use them
int intchecker(float x)
{
if (floor(x)==x && ceilf(x)==x)
{
return 0;
}
else {
return 1;
}
}
int main(){
char line[] = " +----+----+----+----+----+----+----+----+----+----+---+";
char numbers[] = " 0 5 10 15 20 25 30 35 40 45 50";
float balls,slots;
int slotarry[9],tlevel,ballnum,column,lcounter=0,slotsduplicate=1,y;//tlevel-number of levels in the triangle
srand(time(NULL));
int r;
printf("==========================================================\nGalton Box Simulation Machine\n==========================================================\n");
printf("Enter the number of balls [5-100]: ");
scanf("%f",&balls);
while (balls>100 || balls<5) {
printf("\nInput is not within the range. Please try again.");
printf("\nEnter the number of balls [5-100]: ");
scanf("%f",&balls);
}
while (intchecker(balls)==1) {
printf("\nInput is not an integer. Please try again.");
printf("\nEnter the number of balls [5-100]: ");
scanf("%f",&balls);
}
printf("Enter the number of slots [2-10] : ");
scanf("%f",&slots);
while (slots>10 || slots<2) {
printf("\nInput is not within the range. Please try again.");
printf("\nEnter the number of slots [2-10] : ");
scanf("%f",&slots);
}
while (intchecker(slots)==1) {
printf("\nHow can there be a fraction of a slot? Please re-enter slot number.");
printf("\nEnter the number of slots [2-10] : ");
scanf("%f",&slots);
}
tlevel=slots-1;
for(ballnum=1,column=0;balls>0;balls--,ballnum++,column++){
if (column%5==0){
printf("\n");
}
if (ballnum<10){
printf("[0%d]",ballnum);
}
else{
printf("[%d]",ballnum);
}
for(;tlevel>0;tlevel--){
r = rand() % 2;
if (r==0){
printf("R");
}
else {
printf("L");
lcounter++;
}
}
slotarry[lcounter]++;
tlevel=slots-1;
lcounter=0;
printf(" ");
}
printf("\n\n%s",numbers);
printf("%s",line);
char line2[] = "\n +----+----+----+----+----+----+----+----+----+----+---+";
for(;slotsduplicate<=slots;slotsduplicate++){
if (slotsduplicate<10){
printf("0%d|",slotsduplicate);
}
else{
printf("%d|",slotsduplicate);
}
y=slotarry[slotsduplicate];
if (y==0){
printf(" 0");
}
else{
for (;y>0;y--){
printf("#");
}
printf(" %d",slotarry[slotsduplicate]);
}
printf("%s",line2);
}
return 0;
}
Note:This is not completely error-free. This is just my first draft. I just wish to find out why there is an infinite loop.
Here's how I found the problem. First of all, I think it is a bit of a code smell to have a for loop without anything in the initial assignment section. Couple that with the fact that it seems to print # forever, and it looks like y has a garbage value at the beginning of the loop to print the #s.
So I ran your code in the debugger and paused it when it started printing loads of hashes. I checked the value of y and sure enough it was some unfeasibly high number.
Then I checked where y comes from and found you get it from slotarray. I printed it in the debugger and found that all the values in it were unfeasibly high or massively negative numbers. Obviously, slotarray wasn't being initialised correctly, so I looked for where it was initialised and bingo!
Stack variables (of which slotarray is one) must be explicitly initialised in C. I fixed your code with a call to memset.
The whole debugging process I have just outlined took something less than a minute.
ETA As #EOF points out, there is another bug in that slotarray is defined to contain nine slots (indexed 0 - 8) but you allow people to enter 10 slots. This is a buffer overflow bug.

C: Reading strings from binary file

I have a home assignment which says that I have to store information about a mansion in a binary file. On the first line of that file I have to keep information about how many floors there are in the building and how many apartments there are on a floor (size of two-dimensional array). And on the next lines I have to keep info about the apartments - a unique number for each apartment, number of rooms in that apartment, number of people living there, family name, date when the family moved in that apartment, monthly rent.
So, the question is: how should I store that information and how should I read it? If I use fread(buf, size, number_of_elements, FIlE *), how would I know the number of the elements?
For example, how do I read a family name when I don't know it's lenght?
Thanks in advance!
PS. Thank you, claptrap! I tried to write the data first into the binary file but I got some error :(
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define NAPARTMENTS 21
#define TAX_ADULT 3
#define TAX_KID 1
#define TAX_ELEVATOR 2
#define NFLOORS 7
#define NROOMS 3
void main()
{
FILE *fp;
int i, j;
struct info_apartments
{
unsigned numApartment, numRooms, numAdults, numKids, rent;
char family[30], date[10];
};
typedef struct info_apartments data;
data apartments[7][3];
for(i=0; i<NFLOORS; i++)
for(j=0; j<NROOMS; j++)
{
apartments[i][j].numApartment=i*10+j+1;
printf("\nEnter the number of the rooms: ");
scanf("%d", apartments[i][j].numRooms);
printf("\nEnter number of adults: ");
scanf("%d", apartments[i][j].numAdults);
printf("\nEnter number of kids: ");
scanf("%d", apartments[i][j].numKids);
printf("\nEnter family name: ");
scanf("%s", apartments[i][j].family);
printf("\nEnter date: ");
scanf("%s", apartments[i][j].date);
//first two floors don't pay for elevator
if(i+1<3)
apartments[i][j].rent=(TAX_ADULT*apartments[i][j].numAdults)+(TAX_KID*apartments[i][j].numKids);
else
apartments[i][j].rent=((TAX_ADULT+TAX_ELEVATOR)*apartments[i][j].numAdults)+((TAX_KID+TAX_ELEVATOR)*apartments[i][j].rent);
}
if((fp=fopen("myfile", "ab"))==NULL)
{
printf("Error");
exit(1);
}
if((fwrite(apartments, sizeof(apartments), 1, fp))!=1)
{
printf("Error!\n");
exit(1);
}
}
First of all I guess you need to change the "if" line at the end to:
if((fwrite(&apartments, sizeof(apartments), 1, fp))!=1)
You forgot to put &..
second... if you want to
read from the file you do the same thing with fread function
fread(&apartments, sizeof(apartments), 1, fp);
and the you can print it so you can see the data..
printf("%s",apartments[i][j].family);
OR
puts(apartments[i][j].family);
consider creating structs holding the information you need to store/retrieve. the header struct could look something like
typedef struct
{
short floors;
short apartments;
} header;
you do more structs for the other parts of the file.
then when you read (frwrite to write) from the file you do something like
header h;
fread( &h, sizeof(h), 1, fp );
now using the information in the header you can calculate how to read/write the rest of the file. for the rest of the file you would have other structs but I leave that for you to solve.

C Primer 5th - Task 14-6

A text file holds information about a softball team. Each line has data arranged as follows:
4 Jessie Joybat 5 2 1 1
The first item is the player's number, conveniently in the range 0–18. The second item is the player's first name, and the third is the player's last name. Each name is a single word. The next item is the player's official times at bat, followed by the number of hits, walks, and runs batted in (RBIs). The file may contain data for more than one game, so the same player may have more than one line of data, and there may be data for other players between those lines. Write a program that stores the data into an array of structures. The structure should have members to represent the first and last names, the at bats, hits, walks, and RBIs (runs batted in), and the batting average (to be calculated later). You can use the player number as an array index. The program should read to end-of-file, and it should keep cumulative totals for each player.
The world of baseball statistics is an involved one. For example, a walk or reaching base on an error doesn't count as an at-bat but could possibly produce an RBI. But all this program has to do is read and process the data file, as described next, without worrying about how realistic the data is.
The simplest way for the program to proceed is to initialize the structure contents to zeros, read the file data into temporary variables, and then add them to the contents of the corresponding structure. After the program has finished reading the file, it should then calculate the batting average for each player and store it in the corresponding structure member. The batting average is calculated by dividing the cumulative number of hits for a player by the cumulative number of at-bats; it should be a floating-point calculation. The program should then display the cumulative data for each player along with a line showing the combined statistics for the entire team.
team.txt (text file I'm working with):
4 Jessie Joybat 5 2 1 1
4 Jessie Joybat 7 3 5 3
7 Jack Donner 6 3 1 2
11 Martin Garder 4 3 2 1
15 Jaime Curtis 7 4 1 2
2 Curtis Michel 3 2 2 3
9 Gillan Morthim 9 6 6 7
12 Brett Tyler 8 7 4 3
8 Hans Gunner 7 7 2 3
14 Jessie James 11 2 3 4
12 Brett Tyler 4 3 1 3
Since I'm a beginner in C, either I misinterpreted the task from what was asked originally or it's unfairly complex (I believe the former is the case). I'm so lost that I can't think of the way how could I fill in by the criteria of index (player number) every piece of data, keep track of whether he has more than one game, calculate and fetch bat average and then print.
What I have so far is:
#define LGT 30
struct profile {
int pl_num;
char name[LGT];
char lname[LGT];
int atbat[LGT/3];
int hits[LGT/3];
int walks[LGT/3];
int runs[LGT/3];
float batavg;
};
//It's wrong obviously but it's a starting point
int main(void)
{
FILE *flx;
int i,jc,flow=0;
struct profile stat[LGT]={{0}};
if((flx=fopen("team.txt","r"))==NULL) {
fprintf(stderr,"Can't read file team!\n");
exit(1);
}
for( jc = 0; jc < 11; jc++) {
fscanf(flx,"%d",&i);
stat[i].pl_num=i;
fscanf(flx,"%s",&stat[i].name);
fscanf(flx,"%s",&stat[i].lname);
fscanf(flx,"%d",&stat[i].atbat[flow]);
fscanf(flx,"%d",&stat[i].hits[flow]);
fscanf(flx,"%d",&stat[i].walks[flow]);
fscanf(flx,"%d",&stat[i].runs[flow]);
flow++;
}
}
Advice 1: don't declare arrays like atbat[LGT/3].
Advice 2: Instead of multiple fscanf you could read the whole line in a shot.
Advice 3: Since the number of players is limited and the player number has a good range (0-18), using that player number as an index into the struct array is a good idea.
Advice 4: Since you need cumulative data for each player (no need to store his history points), then you don't need arrays of integers, just an integer to represent the total.
So:
#include <stdio.h>
#define PLAYERS_NO 19
typedef struct
{
char name[20+1];
char lastName[25+1];
int atbat;
int hits;
int walks;
int runs;
float batavg;
} Profile;
int main(int argc, char** argv)
{
Profile stats[PLAYERS_NO];
int i;
FILE* dataFile;
int playerNo;
Profile tmpProfile;
int games = 0;
for(i=0; i<PLAYERS_NO; ++i)
{
stats[i].name[0] = '\0';
stats[i].lastName[0] = '\0';
stats[i].atbat = 0;
stats[i].hits = 0;
stats[i].walks = 0;
stats[i].runs = 0;
}
dataFile = fopen("team.txt", "r");
if ( dataFile == NULL )
{
fprintf(stderr, "Can't read file team!\n");
exit(1);
}
for(i=0; i<PLAYERS_NO && !feof(dataFile); ++i, ++games)
{
fscanf(dataFile, "%d", &playerNo);
if ( playerNo <0 || playerNo > PLAYERS_NO )
{
fprintf(stderr, "Player number out of range\n");
continue;
}
fscanf(dataFile, "%s %s %d %d %d %d",
&tmpProfile.name,
&tmpProfile.lastName,
&tmpProfile.atbat,
&tmpProfile.hits,
&tmpProfile.walks,
&tmpProfile.runs);
printf("READ: %d %s %s %d %d %d %d\n",
playerNo,
tmpProfile.name,
tmpProfile.lastName,
tmpProfile.atbat,
tmpProfile.hits,
tmpProfile.walks,
tmpProfile.runs);
strcpy(stats[playerNo].name, tmpProfile.name);
strcpy(stats[playerNo].lastName, tmpProfile.lastName);
stats[playerNo].atbat += tmpProfile.atbat;
stats[playerNo].hits += tmpProfile.hits;
stats[playerNo].walks += tmpProfile.walks;
stats[playerNo].runs += tmpProfile.runs;
}
/* exercise: compute the average */
fclose(dataFile);
for(i=0; i<PLAYERS_NO; ++i)
{
if ( stats[i].name[0] == '\0' )
continue;
printf("%d %s %s %d %d %d %d\n",
i,
stats[i].name,
stats[i].lastName,
stats[i].atbat,
stats[i].hits,
stats[i].walks,
stats[i].runs);
}
return 0;
}
The first rule of programming: Divide and conquer.
So you need to identify individual operations. One such operation is "load one row of input", another is "look up a player". If you have some of those operations (more will come up as you go), you can start building your program:
while( more_input ) {
row = load_one_row()
player = find_player( row.name )
if( !player ) {
player = create_player( row.name )
add_player( player )
}
... do something with row and player ...
}
when you have that, you can start to write all the functions.
An important point here is to write test cases. Start with a simple input and test the code to read a row. Do you get the correct results?
If so, test the code to find/create players.
The test cases make sure that you can forget about code that already works.
Use a framework like Check for this.
If I were doing this, I'd start with a structure that only held one "set" of data, then create an array of those structs:
struct profile {
char name[NAMELEN];
char lname[NAMELEN];
int atbat;
int hits;
int walks;
int runs;
float batavg;
};
Since you're using the player's number as the index into an array, you don't need to store it into the structure too.
I think that will simplify the problem a little bit. You don't need to store multiple data items for a single player -- when you get a duplicate, you just ignore some of the new data (like the names, which should be identical) and sum up the others (e.g., at-bats, hits).

Resources