bubble sorting and counting a char array - c

I wrote this program for my C class. It basically takes the user to a horse track, display odds on different horses and allows the user to make a bet. Originally, my instructor only wanted us to write the results to a text or binary file, so the past results could be viewed whenever the user wanted.
He recently told us that he wants us to include bubble sort to group the horses in order i.e. horse 1, horse 1, horse 1, horse 1, horse 2, horse 2...etc.
I am sure I can figure out the bubble sort using strcmp(), but he also wants us to display how many times that horse has won the race in the past.
My question is: will I be able to make such a display dealing only with char/string arrays? I don't want to spend my next four hours building a solution that cannot work.
Thanks in advance,
p.s. Here is the function for that part of the program.
void viewWinners() {
FILE *zacksTrackStats;
char horses[MAX_SIZE] = {0};
if ((zacksTrackStats = fopen("zacksTrackStats.txt", "r")) == NULL)
{
perror ("error");
exit (0);
}
while (fgets(horses, sizeof(horses), zacksTrackStats) != NULL)
{
printf ("%s", horses);
}
fclose(zacksTrackStats);
pause;
}

Of course you can. Are the actual names "horse 1" and "horse 2"? If so you can just store each horse's data in an integer array. If not then you have to make a lookup table. Store information for how many times each horse has won and print the result.

It is entirely possible to translate txt to numbers and vice versa.
Check out this older post:
Converting string to integer C

Yes you can. To manipulate data from the file you can use fscanf (or sscanf).
sscanf(char *source, format, &dest, ...)
For example :
int occurences[NUMBER_OF_HORSES_MAX];
int count = 0;
int temp = 0;
int i;
for(i = 0; i < NUMBER_OF_HORSES_MAX; ++i)
{
occurences[i] = 0;
}
while (fgets(horses, sizeof(horses), zacksTrackStats) != NULL)
{
sscanf(horses, "%d", &temp);
occurences[temp] += 1;
printf ("Current horse : %s", horses);
count++;
}
for(i = 0; i < count; ++i)
{
printf("Horse %d has won %d times\n", i, occurences[i]);
}

Related

Bubblesort Using Values Read From File In C

I'm new to C programming language and I'm having a really difficult time trying to do this:
I have a file .txt created by my program that store the leaderboard of the 2048 game that I'm doing, and I think that the easiest way to make the leaderboard is by appending the result to the end of the file and than doing a bubble sort, but I can't find the correct value (score) of the first place to compare with the next place, and so on.
Here is my code:
FILE* ptr;
prt = fopen("Placar.txt", "a");
if(ptr == NULL)
{
printf("Error!");
}
fclose(ptr);
ptr = fopen("Placar.txt", "r");
int ch, lines = 0, aux;
char line[150];
while(!feof(ptr))
{
ch = fgetc(ptr);
if (ch == '\n')
{
lines++;
}
}
fclose(ptr);
ptr = fopen("Placar.txt", "a");
fprintf(ptr, "%d: Name: %s - Score: %d - Movements: %d\n", lines+1, name, score, movements);
fclose(ptr);
// bubble sort
ptr = fopen("Placar.txt", "r+");
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
while (!feof(ptr))
{
fgets(line, 150, ptr);
if (strstr(linha, "Score: ") != NULL)
{
// dont know how to do
}
}
}
}
fclose(ptr);
// TODO: delete the 11th line to make the top 10 only
If you think that I'm overcomplicating this, let me know how would you do.
Thanks for the help.
I think that the easiest way to make the leaderboard is by appending the result to the end of the file and than doing a bubble sort
I don't think that's easiest at all. It's especially bad if, as appears to be the case, you have in mind to use the file as your workspace instead of reading the contents into memory, sorting there, and then writing the results back out.
If you think that I'm overcomplicating this, let me know how would you do.
Consider this alternative:
open the high-score file and a temporary file
read scores from the original file one by one and write them to the temp file, until you find the first that is less than the new score you are recording (or reach the end of the file)
write the new score to the temp file
copy the rest of the scores from the original file to the temp file
close both files and replace the original with the temp file.
That involves at most all the same reading, at most all the same writing, and no sorting except for just inserting the new score at the correct place in the sequence. If you like, you can consider it a variation on Insertion Sort. As a bonus, it also has the same, minimal, storage requirements no matter how many scores there are, and therefore does not require any dynamic memory allocation.

How can I execute an 'if' statement only once per iteration in a for loop?

Assuming there are no duplicate words in either list, I would like to compare the words of listA with the words in listB.
If there is a match, I want to print the word that matches and compare the next 'n' words in listB to see if there is a match.
Likewise, if there is no match, (i.e once I reach the last word in listA), I want to print the word that could not be found and compare the next 'n' words in listB to see if there is a match.
I am stuck on how I should implement statements (if, break, continue) in my for loop so that it meets the specifications listed above.
When I run the code below, it only prints the instance in which there is a match, but it does not print anything at all if there is no match.
alineno & blineno refer to current line number in the arrays aline & bline where the words are stored
// index through listA
for(i = 0; i < alineno; i++){
// index through all the words in listB
for(j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
}
continue;
if(strcmp(aline[strlen(aline[0])-1], bline[j]) != 0){
printf("%s is not in the list!", bline[j]);
}
}
}
Input:
listA: Aardvark,Cat,Bear,Dog
listB: Cat,Badger
Expected Output:
Cat is in the list!
Badger is not in the list!
Actual Output:
Cat is in the list!
EDIT:
I understand that my continue statement is the reason why the second condition is not being checked. Removing it would print a word is / is not in the list 'j' amount of times, which is not my desired output. In other words, I would appreciate guidance on how I should implement such statements in order to meet the specifications.
My suggestion is that you change the loops, so you have the loop over "listB" as the outer loop, and iterate over "listA" in the inner loop.
Then you can easily set a flag in the inner loop, and break out of it when a match is found. In the outer loop you check this flag to decide what to print.
In pseudo code perhaps something like this
for (to_find in listB)
{
found_flag = false;
for (animal in listA)
{
if (to_find == animal)
{
found_flag = true;
break;
}
}
if (found_flag)
printf("Animal found");
else
printf("Animal not found");
}
Your continue is always executed; you will never reach your second if.
The best way to do this is probably binary search or a hash table, depending on the amount of data. That being said, the code could be improved in the following way:
for(int i = 0; i < alineno; i++)
{
int j;
for(j = 0; j < blineno; j++)
{
if(strcmp(aline[i], bline[j]) == 0)
break;
}
if(j == blineno)
printf("%s is not in the list!", aline[i]);
else
printf("%s is in the list!", bline[j]);
}
Note: aline[i] not bline[i] in the printf. bline[i] would be a potential array out of bounds bug, if alineno and blineno are allowed to have different lengths.
First, use goto, like this:
void something(void) {
// index through listA
for(int i = 0; i < alineno; i++){
// index through all the words in listB
for(int j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
goto doneAnimal;
}
}
printf("%s is not in the list!", bline[i]);
doneAnimal: ;
}
}
Second; to avoid the risk of "goto is bad" nonsense (see Historical Note below), make the code harder to read by splitting it into 2 different functions, so that you can convert the goto into a return, like this:
void something(void) {
// index through listA
for(int i = 0; i < alineno; i++){
doAnimal(i, blineno);
}
}
void doAnimal(int i, int blineno) {
for(int j = 0; j < blineno; j++){
if(strcmp(aline[i], bline[j]) == 0){
printf("%s is in the list!", bline[j]);
return;
}
}
printf("%s is not in the list!", bline[i]);
}
Historical Note
Once upon a time higher level languages (like assembly language) did not have structured programming features (do, while, break, continue, switch, ...). Instead programmers would write code using goto, like (e.g.) "if(x < MAX) goto loopStart; instead of a "} while(x < MAX);.
To encourage the adoption of structured programming features, in 1968 Edsger W. Dijkstra wrote a letter to the editor of ACM entitled "go to statement considered harmful". This letter had the desired effect - structured programming features (do, while, break, continue, switch, ...) were adopted in all major languages.
However; it also had one unintended side-effect - the letter was a little too effective; and ignorant people (that failed to read the letter or understand its context) started becoming zealots, making their code worse (for cases where the new structured language features aren't enough) to avoid goto without understanding why, and encouraging other people to make their code worse without understanding why.
Examples of this include complicating code by introducing extra variables purely for the sake of avoiding a simpler goto, and/or complicating code to introduce extra branches purely for the sake of avoiding a simpler goto.
Later (in conversations with Donald E. Knuth); Dijkstra himself said "Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"
Sadly; once ignorance begins to spread common sense is fighting a losing battle.

How to read date from file into structure?

I had really hard time in finding solution mostly because popular searchers think that data and date are the same, by date i mean e.g 11/10/2014 9:00.
Alright so i have a structure
struct Rezervations{
char user_login[10];
int room_number;
char date[40];
} rezervations[100];
i have file "reservations.txt" where I'm holding my data's like this:
LOGIN1;5;11/10/14 09:00:00; LOGIN2;1;12/10/14 09:00:00;
etc
To this point i was reading this kind of files using if like this
int loadReservation()
{
char reserved[200];
char temNumber[2];
fp=fopen("rezervations.txt", "r");
fscanf(fp, "%s", reserved);
int i;
int z=0;
int k=0;
char temp[10];
for(i=0;i<strlen(reserved);i++)
{
int j=0;
while(reserved[i]!=59)
{
temp[j]=reserved[i];
j++;
i++;
}
if (z==0||z%3==0)
{
memcpy(reservations[k].user_login,temp,j);
}
else if (z==1||z%3==1)
{
memcpy(tempLiczba,temp,j);
reservations[k].room_number = tempLiczba[0] - '0';
}
else if (z==2||z%3==2)
{
memcpy(reservations[k].date,temp,j);
k++;
}
z++;
}
fclose(fp);
return k;
}
I know this code is messy but I hope u will see what I'm trying to accomplish here. I haven't used malloc yet (with reserved e.g) but thats no my question, I'd like to know how to read this date.
but unfortunately for me with date it stopped working. My guess it stopped working because my date contains / and : but I have no idea how to bypass it. This date is generated by strftime() and I would like not to change it.
Also if you can look at rezervations[k].room_number = tempLiczba[0] - '0'; because that's how I'm reading integer from text file.
Well, there's probably a hundred ways to achieve this in C, but one (easy one) might be to use one of the variants of sscanf.
http://www.tutorialspoint.com/c_standard_library/c_function_sscanf.htm
Example/pseudocode
char * line_from_file = read_next_line();
sscanf(line_from_file, "%s;%d;%s", rezervations[k].user_login, &rezervations[k].room_number, rezervations[k].date);
Edit:
Maybe %s only stops reading on whitespace, and as yours is semicolon seperated, you may need something like
sscanf(line_from_file, "%[^;];%d;%[^;]", ...)

Accessing data from file A using an element shared with a file B (Foreign Keys)

I have a file car.txt and a file reservation.txt. Both the files have a reservation number (Rxxx).
I want to use the reservation number from reservation.txt to print the details of the car with the corresponding reservation number. Or more accurately, I'm trying to find the details of all the cars that are available at any given date.
car.txt:
(from let to right: reservationID, carID, carYOM, carMake, carModel, carFuel, catagory)
R002;V001;2003;Toyota;Camry;Petrol;Budget
R007;V002;2005;Toyota;Prius;Petrol;Economy
R001;V003;1999;Ford;Falcon;Petrol;Midsize
R008;V004;2007;Ford;Territory;Diesel;Fullsize
R011;V005;2010;Ferrari;599;Petrol;Fullsize
R035;V006;1998;Holden;Comadore;Diesel;Midsize
R006;V007;2008;Honda;Civic;Petrol;Budget
R004;V008;2000;Mazda;MX5;Petrol;Economy
reservation.txt:
(from let to right: reservationID, customerID, reservationStartDate, reservationStartTime, reservationEndDate, reservationEndTime)
R001;C005;12/02/2012;09:15A.M;15/03/2012;05:00P.M
R002;C002;15/04/2012;10:00A.M;22/04/2012;10:30A.M
R003;C003;16/01/2012;02:11P.M;15/04/2012;12:00P.M
R004;C004;05/05/2012;03:00P.M;08/05/2012;10:40A.M
R005;C005;15/04/2012;10:00A.M;23/04/2012;05:00P.M
R006;C006;11/04/2012;05:30P.M;15/04/2012;10:00A.M
R010;C008;15/05/2012;03:15P.M;18/05/2012;11:00A.M
R011;C007;15/04/2012;11:40P.A;23/04/2012;09:00A.M
If I enter any date It only gets up to the point where it decides if the entered date is between the reservation start and end date. "all the cars are available".
However, if I enter 13/02/2012, It prints "no matching resID in cars.txt" 7 times.
The code in question:
#include <stdio.h>
#include <string.h>
#define MAX_CAR 100
#define MAX_RES 100
int main(){
typedef struct{ //car struct
char reservationID[20];
char carID[20];
char carYOM[20];
char carMake[20];
char carModel[50];
char carFuel[20];
char catagory[20];
} car_t;
typedef struct{ //res struct
char reservationID[20];
char customerID[20];
char reservationStartDate[20];
char reservationStartTime[20];
char reservationEndDate[50];
char reservationEndTime[20];
} res_t;
car_t car[MAX_CAR]; //car array
res_t reservation[MAX_RES]; //res array
FILE *carHandle;
FILE *resHandle;
char line[100];
char *item;
int rescount = 0;
int carcount =0;
int k;
int i;
int option;
char choice[20];
resHandle = fopen("reservation.txt","r");
while (fgets(line, 99, resHandle)){ //cut up the reservation file line by line and put the bits into the res array.
item = strtok(line,";");
strcpy(reservation[rescount].reservationID,item);
item = strtok(NULL,";");
strcpy(reservation[rescount].customerID,item);
item = strtok(NULL,";");
strcpy(reservation[rescount].reservationStartDate,item);
item = strtok(NULL,";");
strcpy(reservation[rescount].reservationStartTime,item);
item = strtok(NULL,";");
strcpy(reservation[rescount].reservationEndDate,item);
item = strtok(NULL,"\n");
strcpy(reservation[rescount].reservationEndTime,item);
rescount++;
}
fclose(resHandle);
carHandle = fopen("car.txt","r");
while (fgets(line, 99, carHandle)){ //cut up the car file line by line and put the bits into the car array.
item = strtok(line,";");
strcpy(car[carcount].reservationID,item);
item = strtok(NULL,";");
strcpy(car[carcount].carID,item);
item = strtok(NULL,";");
strcpy(car[carcount].carYOM,item);
item = strtok(NULL,";");
strcpy(car[carcount].carMake,item);
item = strtok(NULL,";");
strcpy(car[carcount].carModel,item);
item = strtok(NULL,";");
strcpy(car[carcount].carFuel,item);
item = strtok(NULL,"\n");
strcpy(car[carcount].catagory,item);
carcount++;
}
fclose(carHandle);
printf("Enter todays date:");
scanf("%s", choice);
for (k=0;k<=rescount; k++){
if (strcmp(choice,reservation[k].reservationEndDate)<0 && strcmp(choice,reservation[k].reservationStartDate)>0){
for (i=0;i<=carcount; i++){
if (strcmp(car[k].reservationID,reservation[i].reservationID)==0){
printf("\nreservationID: %s\nreservationStartTime: %s\ncustomerID: %s\ncarid: %s\nyom: %s\nmake: %s\nmodel: %s\nfueltype: %s\ncategory: %s\n\n", car[k].reservationID, reservation[i].reservationStartTime, reservation[i].customerID, car[k].carID, car[k].carYOM, car[k].carMake, car[k].carModel, car[k].carFuel, car[k].catagory);
//printf("This works");
goto outofloop;
}else printf("\n\nno matching resID in cars.txt\n");
}
}
else printf("\nall the cars are available\n");
break;
}
outofloop:
return(0);
}
Any help would be appreciated. :)
EDIT: Updated code.
This is the output, still wrong :(:
Enter todays date:13/02/2012
no matching resID in cars.txt
reservationID: R002
reservationStartTime: 10:00A.M
customerID: C002
carid: V001
yom: 2003
make: Toyota
model: Camry
fueltype: Petrol
category: Budget
Press any key to continue . . .
Enter todays date:13/02/2012
all the cars are available
Press any key to continue...
You're not counting the number of cars and reservations correctly:
In both loops (the one that reads cars, and the one that reads reservations), you're using reccount as the counter.
You need to use two counters, carcount and rescount.
Later on, then, you need to use carcount and rescount in your for loops.
for (k=0;k<=rescount; k++){
if (strcmp(choice,reservation[k].reservationEndDate)<0 && strcmp(choice,reservation[k].reservationStartDate)>0){
for (i=0;i<=carcount; i++){
You should also consider dynamically allocating memory for the car and reservation arrays, as well as for the character arrays inside car_t and reservation_t, since your current code will (even when the obvious bug is fixed) segfault or give unexpected results as soon as your input files have over MAX_CAR/MAX_RES lines, or the strings in the lines are too long.
Update
There are still several issues with your updated code:
You should not store data that's not supposed to be a string in a string variable. Store as much as possible in integer or unsigned variables, especially IDs and dates.
Speaking about dates: Simply use time(NULL) to get the current time in UNIX time format ("seconds from 1970-01-01 00:00"). Store all dates as an integer in UNIX time.
When reading the file, check if carcount is greater than CAR_MAX
for(carcount = 0; carcount < CAR_MAX; carcount++)
You should use fscanf when reading the textfile:
if(fscanf(filehandle, "R%03u...\n", &car[carcount].reservationID, ...) == EOF)
break;
This will make your file scanning much more robust and turn it into a one-liner.
Avoid string comparisions wherever possible. Instead, use 'real datatypes'.
Avoid gotos, really. It is true that Linux kernel developers use them, but they know what they're doing.
Your outer for loop contains a break statement that will, in every case, break the loop execution after the first run. You probably forgot braces after 'else'.
I think your program flow logic contains some errors. You printf("\n\nno matching resID in cars.txt\n") after every single car.
In general, have you tried debugging your code? gdb is a really powerful utility. You can use it to follow the program flow, pause your program at look at variable contents at any time.
I have fixed the issue. The problem was using resID as a common identifier instead of carID.
Thanks for the help everyone.

Why does my program read an extra structure?

I'm making a small console-based rpg, to brush up on my programming skills.
I am using structures to store character data. Things like their HP, Strength, perhaps Inventory down the road. One of the key things I need to be able to do is load and save characters. Which means reading and saving structures.
Right now I'm just saving and loading a structure with first name and last name, and attempting to read it properly.
Here is my code for creating a character:
void createCharacter()
{
char namebuf[20];
printf("First Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].fname,namebuf);
printf("Last Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].lname,namebuf);
/*Character created, now save */
saveCharacter(party[nMember]);
printf("\n\n");
loadCharacter();
}
And here is the saveCharacter function:
void saveCharacter(character party)
{
FILE *fp;
fp = fopen("data","a");
fwrite(&party,sizeof(party),1,fp);
fclose(fp);
}
and the loadCharacter function
void loadCharacter()
{
FILE *fp;
character tempParty[50];
int loop = 0;
int count = 1;
int read = 2;
fp= fopen("data","r");
while(read != 0)
{
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
}
fclose(fp);
}
So the expected result of the program is that I input a name and last name such as 'John Doe', and it gets appended to the data file. Then it is read in, maybe something like
1. Jane Doe
2. John Doe
and the program ends.
However, my output seems to add one more blank structure to the end.
1. Jane Doe
2. John Doe
3.
I'd like to know why this is. Keep in mind I'm reading the file until fread returns a 0 to signify it's hit the EOF.
Thanks :)
Change your loop:
while( fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp) )
{
// other stuff
}
Whenever you write file reading code ask yourself this question - "what happens if I read an empty file?"
You have an algorithmic problem in your loop, change it to:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
while(read != 0)
{
//read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
}
There are ways to ged rid of the double fread but first get it working and make sure you understand the flow.
Here:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
You are not checking whether the read was successful (the return value of fread()).
while( 1==fread(&tempParty[loop],sizeof*tempParty,1,fp) )
{
/* do anything */
}
is the correct way.
use fopen("data","rb")
instead of fopen("data","r") which is equivalent to fopen("data","rt")
You've got the answer to your immediate question but it's worth pointing out that blindly writing and reading whole structures is not a good plan.
Structure layouts can and do change depending on the compiler you use, the version of that compiler and even with the exact compiler flags used. Any change here will break your ability to read files saved with a different version.
If you have ambitions of supporting multiple platforms issues like endianness also come into play.
And then there's what happens if you add elements to your structure in later versions ...
For robustness you need to think about defining your file format independently of your code and having your save and load functions handle serialising and de-serialising to and from this format.

Resources