I have a problem with output of the make[] array is giving me. So first I tokenize the file called Carss.txt, only grabbing the third & fifth column, and store the result in make[] array and year[] array respectively.
I tested out the make[] array to see if it would output the results I wanted. It did inside the while(fgets) loop, but when I tested it on the outside of while(fgets), it's repeating.
I don't understand what's the problem. My program and the text file is provided below.
Output of make array inside while(fgets) loop: (what I wanted)
FORD
FORD
JAYCO
Output of make array outside while(fgets) loop:(Not what I wanted)
JAYCO
JAYCO
JAYCO
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
FILE * fp;
char * filename = "Carss.txt";
char lines[100000], blank[100000];
char * token, * del = " ";
int i = 0, c, d = 0, j;
char * make[10000];
char * year[10000];
if ((fp = fopen(filename, "r")) == NULL) /* Opens the file */ {
printf("can't open %s\n", filename);
exit(1);
}
c = 0;
while (fgets(lines, sizeof(lines), fp) != NULL) {
token = strtok(lines, "\t");
i = 1;
while (i < 5) {
token = strtok(NULL, "\t");
if (i == 2) {
make[c] = token;
}
if (i == 4) {
year[c] = token;
}
i++;
}
printf("%s\n", make[c]); /* OUTPUT OF THE ARRAY HERE IS WHAT I WANTED */
c++;
}
c = 0;
while (c < 3) {
printf("%d\n", c);
printf("%s\n", make[c]);
c++;
}
}
The Text File I tokenize: Carss.txt
1 02V288000 FORD FOCUS 2001 02S41 ELECTRICAL SYSTEM:BATTERY:CABLES FORD MOTOR COMPANY 19990719 20010531 V 291854 20030210 ODI Ford Motor Company 20021106 20021106 CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES. BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR. DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS. AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED. OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003. OWNERS WHO DO NOT RECEIVE THE FREE REMEDY WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332. ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215022000000202
2 02V288000 FORD FOCUS 2000 02S41 ELECTRICAL SYSTEM:BATTERY:CABLES FORD MOTOR COMPANY 19990719 20010531 V 291854 20030210 ODI Ford Motor Company 20021106 20021106 CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES. BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR. DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS. AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED. OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003. OWNERS WHO DO NOT RECEIVE THE FREE REMEDY WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332. ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215021000000202
3 02V236000 JAYCO FT EAGLE 10 SG 2003 EQUIPMENT:OTHER:LABELS JAYCO, INC. 20020730 20020813 V 86 20020923 MFR Jayco, Inc. 20020904 20020912 ON CERTAIN FOLDING TENT CAMPERS, THE FEDERAL CERTIFICATION (AND RVIA) LABELS HAVE THE INCORRECT GROSS VEHICLE WEIGHT RATING, TIRE SIZE, AND INFLATION PRESSURE LISTED. IF THE TIRES WERE INFLATED TO 80 PSI, THEY COULD BLOW RESULTING IN A POSSIBLE CRASH. OWNERS WILL BE MAILED CORRECT LABELS FOR INSTALLATION ON THEIR VEHICLES. OWNER NOTIFICATION BEGAN SEPTEMBER 23, 2002. OWNERS SHOULD CONTACT JAYCO AT 1-877-825-4782. ALSO, CUSTOMERS CAN CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015210000106403000000349
When you do this:
token = strtok(NULL, "\t");
if(i==2)
{
make[c] = token;
}
You do not take a copy of the string, only a pointer reference to it. Then the next time you call fgets the string is overwritten, and all the references point to the new string. So, when you get to the end of the program all your pointers point to the last thing that was written to that memory.
Try this:
make[c] = strdup(token);
That will allocate new memory, duplicate the string, and return the new pointer. The copy will not get overwritten. strdup calls malloc, internally, so you should free the memory when you are done with it.
Inside the loop, make[c] = token records an address inside char lines[]. Since that buffer is overwritten on the next iteration, you need to copy your tokens somewhere else to keep them safe.
Just use make[c] = strdup(token) (and the same for year) to do this - but remember in any real program, you should now free all your make and year strings when you're done with them.
You are using the wrong data structure to store your results.
The make array is an array of pointers. This means that all you store there is a memory address, in your case, the memory address of the pointer being used during the strtok operation. This is not what you want, because you don't know what is going to happen with that memory afterwards; in this case, it appears that during each successive call to the function it gets overwritten.
You need to either use a multidimensional array or to use malloc() or strdup() to create a copy of the char * value as returned by strtok().
strtok operates on a static buffer
when you write
strtok(lines, "\t");
the lines buffer is copied to an internal static buffer - and strtok has only one static buffer - so every time your while loop iterates, new content is placed intothe static buffer invalidating any previous pointers i.e. make[] and year[].
in order to preserve the tokens you need to allocate separate memory for them which is not destroyed at the end of the loop. this can be done in several ways like using strdup on each string like mentioned by other posters although there would be a lot of bookkeeping then freeing the meory. you could also use another data structure, e.g. a linked list to store the values instead of having lots of pointers:
typedef struct record
{
char make[32]; // lets say the max length of make is 32
char year[10];
struct record* next;
} record;
whenever you read a new line from the file create a new record:
record* first = NULL;
record* last = NULL;
while (fgets(lines, sizeof(lines), fp) != NULL)
{
// first create the record
record* newRecord = malloc(sizeof(record));
newRecord->next = NULL;
// parse the line
int column = 0;
for (char* token = strtok(lines,"\t"); token != NULL; token = strtok(NULL, "\t"), ++column)
{
switch(column)
{
case 2:
strncpy(newRecord->make, token, sizeof(newRecord.make));
break;
case 4:
strncpy(newRecord->year, token, sizeof(newRecord.year));
break;
default: // ignore
break;
}
}
// put in the list
if (first == NULL)
{
first = last = newRecord;
}
else
{
last->next = newRecord;
last = newRecord;
}
}
now you have the parts you are interested in a list. you can print out the list with
for (record* rec = first; rec != NULL; rec = rec->next)
{
printf( "%s, %s\n", rec->make, rec->year );
}
// disclaimer, haven't compiled this.
Related
Edit: If it matters, I'm using Dev C++ with -std=c99 as an option.
My professor was able to run this code in class and successfully open a file that eventually reads the data into a linked list. When running the exact same code, my program abruptly exits despite the file being successfully opened.
All I'm trying to do is get this code to run. I've solved what he wants us to solve in my own example, but I can't figure out why his code doesn't run on my machine.
I did add in a puts("Success") line to verify the file has been opened, and I've gone through the methods it calls to see if I can find an error, but I cannot.
Here is the method with the issue (I'm assuming)
int ReadFileStoreInList(void)
{
FILE *cfPtr;
if ((cfPtr = fopen("Hertz-Homework-9-List.txt", "r")) !=NULL)
{
char make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
char rented = 'A';
float daily_rate=0.0;
char dAilyRate[TWELVE];
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
while (!feof(cfPtr))
{
fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
rented = 'A';
add_at_end(make, model, size, color, power, daily_rate, rented);
}
printScreenTitleAndHeaderForCars();
traverse_in_order();
}
else
{
puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
}
fclose(cfPtr);
}
This calls traverse_in_order(); and printScreenTitleAndHeaderForCars();, which I will list below.
void traverse_in_order()
{
node *ptr;
if(start==NULL)
{
printf("list is empty\n");
return;
}
printScreenTitleAndHeaderForCars();
for(ptr=start; ptr!=NULL; ptr=(*ptr).next)
printf("%-12s%-12s%-12s%-12s%-12s%9.2f%12c\n", ptr->make, ptr->model, ptr->size, ptr->color, ptr->power, ptr->daily_rate, ptr->rented);
}
void printScreenTitleAndHeaderForCars()
{
system("Cls");
printf("%35s\n\n","Hertz Rental Cars");
printf("%79s\n","Avail");
printf("%-12s%-12s%-12s%-12s%-12s%-12s%8s\n", "Make", "Model", "Size", "Color", "Power", "Daily_Rate", "Rented");
for (int x=0; x< 7; x++)
printf("----------- ");
printf("\n");
}
In case the structure/header file was also needed:
#define TWELVE 12
typedef struct node_type
{
char make[TWELVE];
char model[TWELVE];
char size[TWELVE];
char color[TWELVE];
char power[TWELVE];
float daily_rate;
char rented;
struct node_type *next;
} node;
node *start=NULL;
int ReadFileStoreInList(void);
void add_at_beginning();
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented);
void add_after_element();
void add_before_element();
void traverse_in_order();
void traverse_in_reverse_order(node *);
void delete_at_beginning();
void delete_at_end();
void delete_after_element();
void delete_before_element();
void sort();
void doSomething(); // menu to ask operator what to do
void RentaCar();
void FindCarAndUpdateAsRented(char *modelSelected);
void ReturnCar();
void toTitleCase(char *aString);
void printScreenTitleAndHeaderForCars();
Requested add_at_end function:
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented)
{
node *ptr, *loc;
ptr = (node *) malloc(sizeof(node));
if(ptr==NULL)
{
printf("no space\n");
return;
}
strcpy((*ptr).make,make);
strcpy((*ptr).model,model);
strcpy((*ptr).size,size);
strcpy((*ptr).color,color);
strcpy((*ptr).power,power);
(*ptr).daily_rate = daily_rate;
(*ptr).rented = rented;
if(start==NULL)
{
start=ptr;
(*start).next=NULL;
}
else
{
loc = start;
while((*loc).next != NULL)
loc=(*loc).next;
(*loc).next=ptr;
(*ptr).next=NULL;
}
}
Interesting enough, if I rename this file to something that isn't correct, the proper printScreenTitleAndHeaderForCars() text executes and shows on screen. That's why I believe it has something to do with ReadFileStoreInList();
I've tried to debug this for a few hours now, but with the knowledge I've learned in class I just can not figure out why this doesn't run.
I expect the output to have the header information given from printScreenTitleAndHeaderForCars(), and then the data from the file I'm reading to appear on screen.
When the file is named improperly in my code, it runs this:
Hertz Rental Cars
Avail
Make Model Size Color Power Daily_Rate Rented
----------- ----------- ----------- ----------- ----------- ----------- -----------
list is empty
enter choice
1. Select Model and Rent
2. Select Model and Return
5. traverse in order
11. sort
12. exit
Where "list is empty" is situated, it should be populating the data from the text file and putting it there.
Instead, I just get:
--------------------------------
Process exited after 1.433 seconds with return value 3221225477
Press any key to continue . . .
I have a feeling this has to do with the way the pointers are written, but I'm struggling to understand how he could run the code and I couldn't.
Any knowledge as to why this might happen would be appreciated!
Edit: Text file contents:
make, model, size, color, power,daily rate.
Mazda,3,4-door,Black,4-Cyl,$99.73
Jeep,Cherokee,4-door,Blue,8-Cyl,$131.92
Buick,Regal,4-door,Purple,6-Cyl,$125.19
Fullsize,SUV,5-door,Brown,8-Cyl,$163.94
Chrysler,Pacifica,4-door,Green,6-Cyl,$127.49
Ford,Focus,2-door,Red,4-Cyl,$99.73
VW,Jetta,2-door,Orange,4-Cyl,$94.91
Chevrolet,Suburban,4-door,Yellow,8-Cyl,$204.92
Nissan,Pathfinder,4-door,White,6-Cyl,$145.11
Chevrolet,Spark,2-door,Teal,4-Cyl,$99.55
You concluded in comments that you thought the data file contents were to blame for your issue. You now having posted those, I can confirm that they are indeed a contributing factor. Imprudent details of your scanf format also contribute.
In the first place, the call for reading the header is dangerous:
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
Since the data are just going to be discarded, there is no point in storing them. Moreover, it's not necessarily safe to assume that the header data will have characteristics matched to those of the associated data. It would be better to read and not assign the whole line:
fscanf(cfPtr, "%*[^\n]");
The * in the format says that the field directive is not to be assigned, only read. Overall, that reads (and ignores) everything up to but not including the first newline. That also allows you to get rid of the abominably-named dAilyRate variable.
Then there is the format for reading the actual data:
fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
It simply does not match the data. Specifically, the %s field descriptor skips leading whitespace and matches a whitespace-delimited string. Your data are comma-delimited, not whitespace-delimited except for line terminators. As a result, that scanf call will try to write a whole line's worth of data into each of the first five strings, thus overrunning each of their bounds. That's a plausible reason for a segfault.
What's more, the line read into daily_rate will fail, since the next data available at that point will be non-numeric. Yet even if the commas were changed to spaces, the rate data would still not be read correctly, because $ is not a valid part of a number. And that, in turn will throw off the reads for the second and subsequent lines.
The field overruns could have been avoided by specifying maximum field widths in the format. It would, moreover, be prudent to check the return value of scanf() to verify that all fields were read, as #Achal demonstrated in his answer, before relying on that data.
Here's a data file in a format compatible with the formats you're actually using:
make model size color power rate
Mazda 3 4-door Black 4-Cyl 99.73
Jeep Cherokee 4-door Blue 8-Cyl 131.92
Buick Regal 4-door Purple 6-Cyl 125.19
Fullsize SUV 5-door Brown 8-Cyl 163.94
Chrysler Pacifica 4-door Green 6-Cyl 127.49
Ford Focus 2-door Red 4-Cyl 99.73
VW Jetta 2-door Orange 4-Cyl 94.91
Chevrolet Suburban 4-door Yellow 8-Cyl 204.92
Nissan Pathfinder 4-door White 6-Cyl 145.11
Chevrolet Spark 2-door Teal 4-Cyl 99.55
And here's a safer way to read it:
#define STR_SIZE 12
char make[STR_SIZE]={""}, model[STR_SIZE]={""}, size[STR_SIZE]={""}, color[STR_SIZE]={""},
power[STR_SIZE]={""};
float daily_rate;
fscanf(cfPtr, "%*[^\n]");
while (fscanf(cfPtr, "%11s%11s%11s%11s%11s%f", make, model, size, color, power, &daily_rate) == 6) {
add_at_end(make, model, size, color, power, daily_rate, 'A');
}
The %11s fields will read up to 11 characters into your 12-character arrays, leaving room for the string terminator that fscanf() will append to each. This will still run into trouble if there is overlong data in the file, but it should not segfault.
I should say also that scanf is difficult to use safely and properly, and that there are other, better alternatives for parsing and consuming the data -- either in the original or in the modified format.
Here are few observation.
Firstly provide more readbility to your code by giving some meaningful macro name instead of TWELVE. For e.g #define NUMBER_OF_ITEMS 12
Secondly, In function definition the formal argument make[TWELVE] doesn't look good as you are passing char array & it decays to char*, so only char *make is enough.
This
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented) { }
can be replaced as
void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented) { }
And most importantly, this
fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate); /* Just Remove it */
just before
while (!feof(cfPtr))
creates an issue i.e that information you are not using, its getting overwritten by second fscanf() statement inside loop. Also do read Why is “while (!feof(file))” always wrong?
Sample code
void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented)
{
/* same code */
}
And
int ReadFileStoreInList(void)
{
FILE *cfPtr;
if ((cfPtr = fopen("input", "r")) !=NULL)
{
char make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
char rented = 'A';
float daily_rate=0.0;
char dAilyRate[TWELVE];
while (fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate) == 6)
{
rented = 'A';
add_at_end(make, model, size, color, power, daily_rate, rented);
}
printScreenTitleAndHeaderForCars();
traverse_in_order();
}
else
{
puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
return 0; /* in case of fopen failed */
}
fclose(cfPtr);
}
The task is to read in a .txt file with a command line argument, within the file there is a list unstructured information listing every airport in the state of Florida note this is only a snippet of the total file. There is some data that must be ignored such as ASO ORL PR A 0 18400 - anything that does not pertain to the structured variables within AirPdata.
The assignment is asking for the site number, locID, fieldname, city, state, latitude, longitude, and if there is a control tower or not.
INPUT
03406.20*H 2FD7 AIR ORLANDO ORLANDO FL ASO ORL PR 28-26-08.0210N 081-28-23.2590W PR NON-NPIAS N A 0 18400
03406.18*H 32FL MEYER- INC ORLANDO FL ASO ORL PR 28-30-05.0120N 081-22-06.2490W PR NON-NPAS N 0 0
OUTPUT
Site# LocID Airport Name City ST Latitude Longitude Control Tower
------------------------------------------------------------------------
03406.20*H 2FD7 AIR ORLANDO ORLANDO FL 28-26-08.0210N 081-28-23.2590W N
03406.18*H 32FL MEYER ORLANDO FL 28-30.05.0120N 081-26-39.2560W N
etc.. etc. etc.. etc.. .. etc.. etc.. ..
etc.. etc. etc.. etc.. .. etc.. etc.. ..
my code so far looks like
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
typedef struct airPdata{
char *siteNumber;
char *locID;
char *fieldName;
char *city;
char *state;
char *latitude;
char *longitude;
char controlTower;
} airPdata;
int main (int argc, char* argv[])
{
char text[1000];
FILE *fp;
char firstwords[200];
if (strcmp(argv[1], "orlando5.txt") == 0)
{
fp = fopen(argv[1], "r");
if (fp == NULL)
{
perror("Error opening the file");
return(-1);
}
while (fgets(text, sizeof(text), fp) != NULL)
{
printf("%s", text);
}
}
else
printf("File name is incorrect");
fflush(stdout);
fclose(fp);
}
So far i'm able to read the whole file, then output the unstructured input onto the command line.
The next thing I tried to figure out is to extract piece by piece the strings and store them into the variables within the structure. Currently i'm stuck at this phase. I've looked up information on strcpy, and other string library functions, data extraction methods, ETL, I'm just not sure what function to use properly within my code.
I've done something very similar to this in java using substrings, and if there is a way to take a substring of the massive string of text, and set parameters on what substrings are held in what variable, that would potentially work. such as... LocID is never more than 4 characters long, so anything with a numerical/letter combination that is four letters long can be stored into airPdata.LocID for example.
After the variables are stored within the structures, I know I have to use strtok to organize them within the list under site#, locID...etc.. however, that's my best guess to approach this problem, i'm pretty lost.
I don't know what the format is. It can't be space-separated, some of the fields have spaces in them. It doesn't look fixed-width. Because you mentioned strtok I'm going to assume its tab-separated.
You can use strsep use that. strtok has a lot of problems that strsep solves, but strsep isn't standard C. I'm going to assume this is some assignment requiring standard C, so I'll begrudgingly use strtok.
The basic thing to do is to read each line, and then split it into columns with strtok or strsep.
char line[1024];
while (fgets(line, sizeof(line), fp) != NULL) {
char *column;
int col_num = 0;
for( column = strtok(line, "\t");
column;
column = strtok(NULL, "\t") )
{
col_num++;
printf("%d: %s\n", col_num, column);
}
}
fclose(fp);
strtok is funny. It keeps its own internal state of where it is in the string. The first time you call it, you pass it the string you're looking at. To get the rest of the fields, you call it with NULL and it will keep reading through that string. So that's why there's that funny for loop that looks like its repeating itself.
Global state is dangerous and very error prone. strsep and strtok_r fix this. If you're being told to use strtok, find a better resource to learn from.
Now that we have each column and its position, we can do what we like with it. I'm going to use a switch to choose only the columns we want.
for( column = strtok(line, "\t");
column;
column = strtok(NULL, "\t") )
{
col_num++;
switch( col_num ) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 9:
case 10:
case 13:
printf("%s\t", column);
break;
default:
break;
}
}
puts("");
You can do whatever you like with the columns at this point. You can print them immediately, or put them in a list, or a structure.
Just remember that column is pointing to memory in line and line will be overwritten. If you want to store column, you'll have to copy it first. You can do that with strdup but *sigh* that isn't standard C. strcpy is really easy to use wrong. If you're stuck with standard C, write your own strdup.
char *mystrdup( const char *src ) {
char *dst = malloc( (sizeof(src) * sizeof(char)) + 1 );
strcpy( dst, src );
return dst;
}
I'm attempting to fully justify (left and right columns line-up) input from files and this is what I came up with. The input files have embedded commands so from my pseudo output below I start justifying at the company's line and end at telephone As you can see it randomly joins two of the lines read together. Can someone please tell me why it's doing this? My input files definitely have newline characters in them since I double checked they were entered.
Also how do I do the following: Check if my read line will fit into my output array (of 40 char)? If it doesn't I want to move the overflowed string(s) into the next line or char(s) if it's easier. This one isn't as necessary as my first question but I would really like to make the output as nice as possible and I don't know how to restrict and carry overflow from read lines into the next output array.
Since it began to escape from AT&T's Bell Laboratories in
the early 1970's, the success of the UNIX
operating system has led to many different
versions: recipients of the (at that time free) UNIX system
code all began developing their own different
versions in their own different ways for use and sale.
Universities, research
institutes, government bodies and computer
companies all began using the powerful
UNIX system to develop many of the
technologies which today are part of a
UNIX system. Computer aided design,
manufacturing control systems,laboratorysimulations,even the Internet itself,
all began life with and because of UNIX
Today, without UNIX systems, the Internewould come to a screeching halt.
Most telephone calls could not be made,
electronic commerce would grind to a halt and
there would have never been "Jurassic Park"!
Below is my justify function that's passed the read file line using fgets in another function. The printf lines are just for debugging.
void justify(char strin[]){
int i = 0; //strin iterator
int j = 0; //out iterator
int endSpaces = LINE + 1 - strlen(strin);
int voids = countwords(strin) - 1;
printf("Voids: %d\n", voids);
printf("Input: %s", strin);
//No words in line, exit
if (voids <= 0)
return;
//How many to add between words
int addEvenly = endSpaces/voids;
int addUnevenly = endSpaces % voids;
printf("space to distribute: %d evenly: %d unevenly: %d\n", endSpaces, addEvenly, addUnevenly);
//Copy space left of array to output
while (strin[i] == ' '){
outLine[j++] = ' ';
i++;
}
//One word at a time
while (endSpaces > 0 || addUnevenly > 0){
//Copy letters into out
while (strin[i] != ' '){
outLine[j] = strin[i];
i++;
j++;
}
//Add the necessary spaces between words
if (addEvenly > 0){
for (int k = 0; k < addEvenly; k++){
outLine[j++] = ' ';
}
}
//Distribute to the left
if (addUnevenly > 0){
outLine[j++] = ' ';
endSpaces--;
addUnevenly--;
}
printf("Output: %s\n\n", outLine);
endSpaces = endSpaces - addEvenly;
//Finish copying rest of input to output when no more spaces to add
if (endSpaces == 0 && addUnevenly == 0){
while (strin[i] != '\0')
outLine[j++] = strin[i++];
printf("Output 2: %s\n", outLine);
}
}
fprintf(out, "%s", outLine);
}
On sunday I created a function (justifyline()) able to justify and indent a line you give it as input. It outputs a buffer containing the justified (formatted) text and any eventual text-remainder; such a remainder may be used as input to the function justifyline().
After this step I've used the file below (text.txt) to test the behaviour of such a function. That test demonstrates me the need to use also word wrapping between lines. Then I've written the function formatLineByLine(). The function formatLineByLine() doesn't care of void lines.
Text file (text.txt): (I used the text in your question trying to correct it, but not all I've corrected, then the input file suffers of this fact!)
Since it began to escape from AT&T's
Bell Laboratories in the early 1970's,
the success of the UNIX operating system
has led to many different versions:
recipients of the (at that time free)
UNIX system code all began developing
their own different versions in their
own different ways for use and sale.
Universities, research institutes,
government bodies and computer companies
all began using the powerful UNIX system
to develop many of the technologies which
today are part of a UNIX system.
Computer aided design, manufacturing
control systems, laboratory simulations,
even the Internet itself, all began life
with and because of UNIX Today, without
UNIX systems, the Internet would come to a
screeching halt. Most telephone calls
could not be made, electronic commerce
would grind to a halt and there would
have never been "Jurassic Park"!
The output of the function formatLineByLine()
ABCDE12345678901234567890123456789012345
Since it began to escape from
AT&T's Bell Laboratories in the
early 1970's, the success of the
UNIX operating system has led to
many different versions: recipients
of the (at that time free) UNIX
system code all began developing
their own different versions in
their own different ways for use
and sale. Universities, research
institutes, government bodies and
computer companies all began using
the powerful UNIX system to develop
many of the technologies which
today are part of a UNIX system.
Computer aided design,
manufacturing control systems,
laboratory simulations, even the
Internet itself, all began life
with and because of UNIX Today,
without UNIX systems, the Internet
would come to a screeching halt.
Most telephone calls could not be
made, electronic commerce would
grind to a halt and there would
have never been "Jurassic Park"!
Another step is the idea to use a paragraph per paragraph justifycation. Then I've written the function justifyParagraph(). The function formatInParagraphs() reads the file text.txt and prints it justified using the function justifyParagraph().
The output of the function formatInParagraphs()
ABCDE12345678901234567890123456789012345
Since it began to escape from
AT&T's Bell Laboratories in the
early 1970's, the success of the
UNIX operating system has led to
many different versions: recipients
of the (at that time free) UNIX
system code all began developing
their own different versions in
their own different ways for use
and sale.
Universities, research
institutes, government bodies and
computer companies all began using
the powerful UNIX system to develop
many of the technologies which
today are part of a UNIX system.
Computer aided design,
manufacturing control systems,
laboratory simulations, even the
Internet itself, all began life
with and because of UNIX Today,
without UNIX systems, the Internet
would come to a screeching halt.
Most telephone calls could not be
made, electronic commerce would
grind to a halt and there would
have never been "Jurassic Park"!
The function justifyline() is able to create a justified buffer with indentation (parameter size_t indent) and to use also a single space between the words (parameter int nospacing sent as 1).
The function justifyParagraph() is able to create a justified buffer with line indentation (parameter: size_t indent) and 1st line indentation (parameter: size_t indentstart). The formatted output may be directly printed when a NULL output buffer is sent to the function (parameter char **outbuf sent as NULL). The last line the function generates may be justified or not (parameter: int notFrmtLast sent as 1).
Both justification functions, when the parameter char **outbuf points a NULL pointer ( *outbuf == NULL ), allocate memory using malloc() . In this case you have to free the buffer after its use. If this parameter is passed as NULL to the function justifyParagraph(), the function prints the elaborated output, if outbuf is passed as NULL to the function justifyline(), the function returns an error.
The code is below. An issue of this code is that, in some cases, the length of the string should be computed using a function different from strlen(). To avoid this problem you may use these functions with lines that have a single space between the words. Such a problem affects the functions justifyParagraph() and formatLineByLine().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int justifyLine(char *inbuf, char **outbuf, size_t linelen, char ** endptr, size_t indent, int nospacing);
int justifyParagraph(char *inbuf,char **outbuf,size_t linelen,size_t indentstart,size_t indent,int notFmtLast);
int formatLineByLine(FILE *f, size_t linelen,size_t indent, int notFrmtLast);
int formatInParagraphs(FILE *f, size_t linelen,size_t indentstart,size_t indent, int notFrmtLast);
int justifyParagraph(char *inbuf,char **outbuf,size_t linelen,size_t indentstart,size_t indent,int notFmtLast)
{
char *optr=NULL,*endp=NULL;
size_t len,s;
int retval,nf;
for(;;) { //Error control loop
if (inbuf==NULL) {
retval=0x10;break;
}
if (indent+indentstart>linelen) {
retval=0x20;break;
}
if (outbuf!=NULL) {
if (*outbuf==NULL) {
if ( (*outbuf=malloc(linelen+1))==NULL ){
retval=0x30;break;
}
}
optr=*outbuf;
}
endp=inbuf;
indent+=indentstart;
len=linelen-indent;
s=indentstart;nf=0;
while( *endp!=0) {
if (notFmtLast && strlen(endp)<linelen-indent)
nf=1;
if ( (retval=justifyLine(endp,&optr,linelen,&endp,
indent,nf)) ) {
retval|=0x40;break;
}
if (outbuf!=NULL) {
optr+=strlen(optr);
*optr++='\n';
*optr=0;
} else {
puts(optr);
}
indent-=s;
len+=s;
s=0;
}
break; //Close error ctrl loop!
}
if (outbuf==NULL && optr!=NULL)
free(optr);
return retval;
}
int justifyLine(char *inbuf,char **outbuf,size_t linelen, char ** endptr,size_t indent,int nospacing)
{
size_t textlen,tmp;
size_t spctoadd,spcodd,spcin;
size_t timetoodd;
size_t ibidx,obidx,k,wc;
char * endp;
char * outb=NULL;
int retval=0;
for(;;) { //Error control loop
endp=inbuf;
if (inbuf==NULL) {
retval=1;break;
}
if (indent>linelen) {
retval=2;break;
}
if (outbuf==NULL) {
retval=3;break;
}
if (*outbuf==NULL) {
if ( (*outbuf=malloc(linelen+1))==NULL ){
retval=4;break;
}
}
outb=*outbuf;
//Leave right spaces
while(*inbuf==' ')
inbuf++;
if (*inbuf==0) {
endp=inbuf;
*outb=0;
break; //exit from error loop without error!
}
linelen-=indent;
//Count words and the minimum number of characters
ibidx=0;
wc=0;textlen=0;k=1;endp=NULL;
while ( *(inbuf+ibidx)!=0 ) {
if (*(inbuf+ibidx)==' ') {
ibidx++;continue;
}
//There's a char!
k=ibidx; //last word start
tmp=textlen;
wc++;textlen++; //add the space after the words
//textlen<linelen because textlen contains also the space after the word
// while(textlen<=linelen && *(inbuf+ibidx)!=' ' && *(inbuf+ibidx) ) {
while(*(inbuf+ibidx)!=' ' && *(inbuf+ibidx) ) {
textlen++;ibidx++;
}
if (textlen>linelen+1) {
endp=inbuf+k;
textlen=tmp;
wc--;
break;
}
}
textlen=textlen-wc;
if (endp==NULL) {
endp=inbuf+ibidx;
}
if (textlen<2) {
*outb=0;
break; //exit from error loop without error!
}
//Prepare outbuf
memset(outb,' ',linelen+indent);
*(outb+linelen+indent)=0;
ibidx=0;
obidx=indent;
if (wc>1) {
if (!nospacing) {
//The odds are max in number == wc-2
spctoadd=linelen-textlen;
} else {
spctoadd=wc-1;
}
spcin=spctoadd/(wc-1);
spcodd=spctoadd % (wc-1);
if (spcodd)
timetoodd=(wc-1)/spcodd;
k=timetoodd;
while(spctoadd) {
while(*(inbuf+ibidx)!=' ') {
*(outb+obidx++)=*(inbuf+ibidx++);
}
obidx+=spcin;spctoadd-=spcin;
if (spcodd && !(--k)) {
k=timetoodd;
spcodd--;
spctoadd--;
obidx++;
}
while(*(inbuf+ ++ibidx)==' ');
}
}
while(*(outb+obidx) && *(inbuf+ibidx) && *(inbuf+ibidx)!=' ')
*(outb+obidx++)=*(inbuf+ibidx++);
//There're words longer then the line!!!
if (*(inbuf+ibidx) && *(inbuf+ibidx)!=' ')
endp=inbuf+ibidx;
break; //Terminate error ctrl loop.
}
if (endptr!=NULL)
*endptr=endp;
return retval;
}
int formatLineByLine(FILE *f, size_t linelen,size_t indent, int notFrmtLast)
{
char text[250],*app;
//justifyLine allocates memory for the line if the outbuf (optr) value is NULL
char * optr=NULL;
size_t j,k;
//print a ruler
for(j=0;j<indent;j++)
printf("%c",'A'+(char)j);
for(j=1;j<=linelen-indent;j++)
printf("%c",'0'+(char)(j%10));
printf("\n");
//starts printing
fseek(f,0,SEEK_SET);
j=0;
while(fgets(text+j,sizeof(text)-j,f)) {
if ( (app=strrchr(text+j,'\n')) ) {
*app=0;
}
k=strlen(text);
if (strlen(text)<linelen-indent) {
if (!*(text+k) && *(text+k-1)!=' ') {
*(text+k++)=' ';
*(text+k)=0;
}
j=k;
continue;
}
app=text;
do {
//justifyLine allocates memory for the line if the outbuf (optr) value is NULL
if ( justifyLine(app,&optr,linelen,&app,indent,0) ) {
if (optr!=NULL)
free(optr);
return 1;
}
printf("%s\n",optr);
j=(*app!=0)?strlen(app):0;
} while(j>linelen-indent);
if (j) {
strcpy(text,app);
*(text+j++)=' ';
*(text+j)=0;
}
}
if (*text!=0 && j) {
if ( justifyLine(text,&optr,linelen,NULL,indent,notFrmtLast) )
{
if (optr!=NULL)
free(optr);
return 2;
}
printf("%s\n",optr);
}
//justifyLine allocates memory for the line if the outbuf value is NULL
if (optr!=NULL)
free(optr);
return 0;
}
int formatInParagraphs(FILE *f, size_t linelen,size_t indentstart,size_t indent, int notFrmtLast)
{
char text[1024], *app;
//To uncomment when you use the commented justifyParagraph line.
//see below
//char *outbuf=NULL;
size_t j;
//print a ruler
for(j=0;j<indent;j++)
printf("%c",'A'+(char)j);
for(j=1;j<=linelen-indent;j++)
printf("%c",'0'+(char)(j%10));
printf("\n");
//starts printing
fseek(f,0,SEEK_SET);
j=0;
while(fgets(text+j,sizeof(text),f)) {
if ( (app=strrchr(text+j,'\n')) ) {
*app++=' ';*app=0;
}
if ( *(text+j)==' ' && !*(text+j+1) ) {
//The following commented line allocates memory creating a paragraph buffer!
//doesn't print the formatted line.
//justifyParagraph(text,&outbuf,linelen,indentstart,indent,notFrmtLast);
//This line directly print the buffer allocating and de-allocating
//only a line buffer. It prints the formatted line.
justifyParagraph(text,NULL,linelen,indentstart,indent,notFrmtLast);
j=0;
//To uncomment when you use the commented justifyParagraph line.
// printf("%s\n\n",outbuf);
puts("");
} else {
j+=strlen(text+j);
}
}
return 0;
}
int main(void)
{
FILE * file;
file=fopen("text.txt","r");
formatLineByLine(file,40,5,1);
puts("");
formatInParagraphs(file,40,5,5,1);
fclose(file);
return 0;
}
You were incredibly close – but you forgot one thing!
After copying a word into outLine, you insert the correct number of additional spaces, and continue with 'the next word'. However, at that point the input pointer i still is at the end of the previously copied word (so it points to the first space immediately after that). The test while (strin[i] != ' ') then immediately fails and you insert the additional spaces at that point again. This continues until you run out of spaces to add, and at the very end you add what was not processed, which is "the entire rest of the string".
The fix is simple: after copying your word into outLine, copy the original space(s) as well, so the i iterator gets updated to point to the next word.
//One word at a time
while (endSpaces > 0 || addUnevenly > 0)
{
//Copy letters into out
while (strin[i] != ' ')
{
outLine[j] = strin[i];
i++;
j++;
}
//Copy original spaces into out <-- FIX!
while (strin[i] == ' ')
{
outLine[j] = strin[i];
i++;
j++;
}
With this, your code works entirely as you intended. Output:
|Since it began to escape from AT&T's Bell Laboratories in|
|the early 1970's, the success of the UNIX|
|operating system has led to many different|
|versions: recipients of the (at that time free) UNIX system|
|code all began developing their own different|
|versions in their own different ways for use and sale.|
| Universities, research|
|institutes, government bodies and computer|
|companies all began using the powerful |
|UNIX system to develop many of the |
|technologies which today are part of a |
|UNIX system. Computer aided design, |
|manufacturing control systems,laboratorysimulations,even the Internet itself, |
|all began life with and because of UNIX |
|Today, without UNIX systems, the Internewould come to a screeching halt.|
|Most telephone calls could not be made,|
|electronic commerce would grind to a halt and|
|there would have never been "Jurassic Park"! |
Possible improvements
Justified lines should never begin with whitespace (your Copy space left of array to output part). Just increment the pointer there:
//Copy space left of array to output
while (strin[i] == ' ')
{
// outLine[j++] = ' ';
i++;
endSpaces++;
}
(and move the calculation for How many to add between words below this, because it changes endSpaces).
The same goes for spaces at the end. You can adjust endSpaces at the start
int l = strlen(strin);
while (l > 0 && strin[l-1] == ' ')
{
l--;
endSpaces++;
}
and suppress copying the trailing spaces into outLn at the bottom. (That needs some additional tinkering, I couldn't get it right first time.)
It is much neater to ignore multiple spaces inside the input string as well, but that takes a bit more code.
With these three implemented, you get a slightly neater output:
|Since it began to escape from AT&T's Bell Laboratories in|
|the early 1970's, the success of the UNIX|
|operating system has led to many different|
|versions: recipients of the (at that time free) UNIX system|
|code all began developing their own different|
|versions in their own different ways for use and sale.|
|Universities, research|
|institutes, government bodies and computer|
|companies all began using the powerful|
|UNIX system to develop many of the|
|technologies which today are part of a|
|UNIX system. Computer aided design,|
|manufacturing control systems,laboratorysimulations,even the Internet itself,|
|all began life with and because of UNIX|
|Today, without UNIX systems, the Internewould come to a screeching halt.|
|Most telephone calls could not be made,|
|electronic commerce would grind to a halt and|
|there would have never been "Jurassic Park"!|
A drawback of this one-line-at-a-time method is that it cannot easily be rewritten to gather input until a line overflows. To do so, you need:
a routine that skips all spaces and return a pointer to the next word.
a routine that reads words until a line is 'overfull' – that is, the number of words plus (the number of words - 1) for spaces is larger than your LINE value. This uses routine #1 and outputs exactly one justified line.
You need to pass on the location and number of strings from your main to both these routines, and in both check if you are at the end of either a single input line or the entire input array.
I've written this main that contains two simple methods to center a text in a line. The first method only prints the text variable without modifying it, the second method modifies the text variable and then prints it. (Here method is not intended as function, the code contains two examples which you may translate easily in simple functions)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char text[81],fmt[10];
int linelen=80,tlen;
int spacetocenter=0;
printf("Insert text to center [max %lu char]:\n",sizeof(text)-1);
if (scanf("%[^\n]",text)<1) {
perror("scanf");
return -1;
}
getchar(); //Leaves return from the buffer
tlen=strlen(text);
spacetocenter=(linelen-tlen)/2;
if (spacetocenter<0)
spacetocenter=0;
//Method one (this doesn't modify text)
//This method directly prints the contents of text centered.
//----------------------------------------------------------
snprintf(fmt,sizeof(fmt),"%%%+ds\n",spacetocenter+tlen);
//printf("%s\n",fmt); // prints the used format
printf(fmt,text);
//Method two (this modifies text)
//This method modifies the contents of the variable text
//----------------------------------------------------------
memmove(text+spacetocenter,text,tlen+1);
memset(text,' ',spacetocenter);
printf("%s\n",text);
return 0;
}
Note:
After the second method is applied tlen no longer contains the length of text!
The program consider the line of 80 chars, if you need shorter/longer lines you have to modify the value of the variable linelen.
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.
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.