#1
  1. Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2003
    Location
    Redditch, UK
    Posts
    20
    Rep Power
    0

    Reading and writing files


    Hi all,

    Im trying to write and read to a random access file in my c program, Im pretty new to the game so need a simple solution.

    Ive written to my file in the following way:

    /* Write the data to the disk */
    fp = fopen("A:vehicles.raf", "a");
    fwrite(&theVehicle, sizeof(theVehicle), 1, fp);
    fclose(fp);

    theVehicle is a structure that holds:

    char registration[10]
    char make[20]
    char model[20]
    int insuranceCat

    I can write to the file fine, no problem, but how to I make it drop to a new line, so I can do a search for new lines later on? If I keep appending the new record onto the last, then it doesn't let me enter any more than 3 vehicle details for some reason.

    Also, when I look in the file, I find I get loads of characters appear that weren't typed in. For example:

    I type in as the model: Mondeo

    The rest of the elements in the array sometimes come out as unrecognisable characters, do I need to pad out the array with " ", and if so, how?

    The next model I type in is: 25

    Then I look at the .raf file and it says '25 deo' Im assuming that its taking a bit of 'Mondeo' and sticking it on the end, how do I prevent this?

    Finally, my problem is reading the files back, Ive read about fseek() but dont really know how to use it, Im trying to read one record back at a time, not the whole lot, as I want to perform matching searches on it.

    I really appreciate any feedback and time you spend answering my questions.

    Regards,
    Mike.
  2. #2
  3. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,155
    Rep Power
    2222
    Originally posted by flyingbuddha
    Hi all,

    Im trying to write and read to a random access file in my c program, Im pretty new to the game so need a simple solution.

    Ive written to my file in the following way:

    /* Write the data to the disk */
    fp = fopen("A:vehicles.raf", "a");
    fwrite(&theVehicle, sizeof(theVehicle), 1, fp);
    fclose(fp);

    theVehicle is a structure that holds:

    char registration[10]
    char make[20]
    char model[20]
    int insuranceCat

    I can write to the file fine, no problem, but how to I make it drop to a new line, so I can do a search for new lines later on? If I keep appending the new record onto the last, then it doesn't let me enter any more than 3 vehicle details for some reason.

    What do you mean by "drop to a new line"?

    Do you mean that once you have entered one car and written it to the file, that you want to have it ready to accept the entry of another car? That would be up to the program itself. E.g., I would have separate functions:
    1. a function that initializes the data structure and sets up a new form on the display (don't know what OS or type of display you're doing, but this functionality should not depend on those details).
    2. a function that processes the form; ie, that accepts input from the user, updates the form on the display, updates the data structure (might want to hold this one off until you exit the form, keeping the entered values in local variables), and accepts user-entered commands to accept the form or cancel out. If you don't update the data structure until you exit the form, you would do that when the user accepts the form, but of course not if he cancels it.
    3. a function to write the data structure to the file to a specified record number. This function would interpret a special record number as meaning "append" (eg, -1 or MAX_INT), perform an open, write the record as you describe, and close the file. Of course, it would use fseek() to position the file pointer. BTW, I think you would also want to make this a binary file.

    I'm also not sure what you mean by "do a search for new lines later on". And since you only specify 3 vehicle details in your struct, I don't see why you would expect to be able to enter more than three.

    Also, when I look in the file, I find I get loads of characters appear that weren't typed in. For example:

    I type in as the model: Mondeo

    The rest of the elements in the array sometimes come out as unrecognisable characters, do I need to pad out the array with " ", and if so, how?

    The next model I type in is: 25

    Then I look at the .raf file and it says '25 deo' Im assuming that its taking a bit of 'Mondeo' and sticking it on the end, how do I prevent this?
    Just basic C character strings. In C, a character string can be any length, but must always be terminated with a NULL, written as '\0' (ASCII code zero). You just have to be sure that you declare a char array that is large enough to hold your longest string plus one for the NULL. So your registration array of [10] can hold a string of up to nine characters and your make and model arrays of [20] can hold strings of up to 19 characters.

    Because of the null-terminator, anything else in your char array after the NULL doesn't matter at all. All that your program will pay any attention to will be what starts at the beginning of the array and ends right before the NULL. The rest doesn't matter. Get a hex dump of your file and take a look at the actual character in the middle of "25 deo"; you should find that it is a 00, which is the null-terminator. None of your code will ever see that "deo", so it doesn't matter.

    The same applies for those garbage characters you sometimes see. Your data structure, theVehicle, was declared within a function, wasn't it?

    This gets into C storage classes, but the upshot is that a global variable or a static variable goes into static storage, which would be in the Data Segment of an Intel machine (eg, in DOS or Windows). Static variables are usually automatically initialized to all zeros at the start of the program (but don't ever count on it), unless you explicitly initialize them to something yourself.

    But variables declared within a function are of the auto storage class and are stored on the stack (eg, in Intel's Stack Segment). Auto variables are not initialized unless you do initialize them explicitly yourself. Otherwise, they just contain whatever was in memory before, AKA "garbage".

    As long as you are going to store something into every field of theVehicle, it doesn't matter what garbage is in it to begin with nor what garbage remains after the NULL in each array. But if there's any chance of reading that data before you write something to it, then you should initialize it explicitly; eg:
    Code:
    Vehicles theVehicle = {"","","",0};  // just making all strings blank
    An interesting security note, BTW:
    In DOS and on Windows, disk space is allocated in blocks that were called "clusters". If the clusters are 8K each, then for every file, even if it's only one byte long, at least 8K is allocated. And when a file gets longer than its cluster size, another cluster is allocated to it, and so on. When you delete a file, its clusters are returned to the free list with their contents unchanged. So when one of those clusters is reallocated to a new file and that new file does not completely use that cluster, then the old data is still there. Even though that old data cannot normally be accessed (like the remaining characters in a char array after the NULL), there have long been forensics tools that can read that old data. And for just as long, there have been security tools to delete a file by first overwriting all of its data so that none remains.


    Finally, my problem is reading the files back, Ive read about fseek() but dont really know how to use it, Im trying to read one record back at a time, not the whole lot, as I want to perform matching searches on it.
    You use fseek() to position the file pointer, then you use fread to read starting at that point:
    Code:
    fseek(fp,rec_no*sizeof(Vehicles), SEEK_SET); // from beginning of file, first record is #0
    bytesread = fread(&aVehicle, sizeof(Vehicles), 1, fp); // read one record
    Please note that fread() advances the file pointer, so after the fread() the file pointer is now pointing to the next record. You can verify this with ftell().

    Hope that helps.
    Last edited by dwise1_aol; May 11th, 2003 at 12:12 PM.
  4. #3
  5. Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2003
    Location
    Redditch, UK
    Posts
    20
    Rep Power
    0

    Unhappy Great answer but still stuck


    Hi again,

    Wow, thanks for the lengthy answer! I feel so stupid for saying this, but Im still stuck :(

    Ive managed to incorporate the fseek into the search function ok, and also done so for the adding (but not sure it is entirely correct). I got a problem though when I go to add the fourth record. I add the first 3 fine, and the error check works when I type in a registration number that has been entered before, but when I go to add a fourth vehicle, the error message "[REGISTRATION NUMBER EXISTS]" appears no matter if it does not.

    I have a single line in my raf file that has the following:

    Code:
    t398gut   Ford                Mondeo              6 w145hbf   Rover               25 deo              7 v654fre   Rover               75 deo              8
    Thats why I was asking how to make it drop to the next line, Im assuming that its adding up to the end of the third record, then returning to the beginning of the first line when checking against the fourth vehicle registration.

    I have attached a more lengthy code listing below to show you if Im doing anything wrong that I didn't explain in the first. I may have other things wrong with my code or inappropriate, but Im really just looking for an answer on how to write to the file and read it back (record by record)

    I really appreciate any response.

    Mike.

    Code:
    =======================================
    
    VARIABLES
    
    =======================================
    
    int validateRegistration(char Reg[10]);
    int	searchForRegistration(char sReg[10]);
    
    /* A record contains the reg, make, model and insurance of a vehicle */
    typedef struct{
    	char	registration[10];
    	char	make[20];
    	char	model[20];
    	int		insuranceGroup;
    } vehicles;
    
    /* Each vehicle can be manipulated globally */
    vehicles	theVehicle;
    vehicles	tmpVehicle;
    
    /* Create a file pointer */
    FILE	*fp;
    
    
    =======================================
    
    ADDING THE VEHICLE DATA
    
    =======================================
    
    printf("[ADD INFORMATION]\n\n");
    
    /* WOULD THE USER LIKE TO ADD ANOTHER AFTER? (LOOP) */
    
    
    regInvalid = 1;
    /* Add registration */
    while(regInvalid == 1){
    	printf("Please enter the vehicles registration number\n");
    	printf(":> ");
    	scanf("%s", &theVehicle.registration);
    
    	/* Validate registration */
    	regInvalid = (validateRegistration(theVehicle.registration) == 1) ? 0 : 1;
    	if(regInvalid == 1){
    		printf("\n[REGISTRATION NUMBER INVALID]\n\n");
    	}
    	else{
    		/* Check for duplicate registration number */
    		regInvalid = (searchForRegistration(theVehicle.registration) == 0) ? 0 : 1;
    		if(regInvalid == 1) printf("\n[REGISTRATION NUMBER EXISTS]\n\n");
    	}
    }
    
    /* Add Make */
    printf("Please enter the vehicles make\n");
    printf(":> ");
    scanf("%s", &theVehicle.make);
    
    /* Add model */
    printf("Please enter the vehicles model\n");
    printf(":> ");
    scanf("%s", &theVehicle.model);
    
    /* Add insurance group */
    printf("Please enter the insurance group for this vehicle\n");
    printf(":> ");
    scanf("%s", &theVehicle.insuranceGroup);
    
    /* Write the data to the disk */
    fp = fopen("A:vehicles.raf", "a");
    fseek(fp, ftell(fp) +1, SEEK_END);
    fwrite(&theVehicle, sizeof(theVehicle), 1, fp);
    fclose(fp);
    
    /* Inform user of add, then display menu */
    printf("\n[RECORD ADDED]\n\n");
    displayMenu = 1;
    
    
    =======================================
    
    READING THE FILE
    
    =======================================
    
    int searchForRegistration(char sReg[10]){
    	/* Declare variables */
    	int		regCount, rowNum;
    	int		regFound;
    
    	/* Initiate status of find and record position */
    	regFound = 0;
    	rowNum = 0;
    
    	/* Open the file, perform search on all records */
    	fp = fopen("A:Vehicles.raf", "r");
    
    	while(! feof(fp)){
    		
    		fseek(fp, rowNum *sizeof(vehicles), SEEK_SET);
    		fread(&tmpVehicle, sizeof(vehicles), 1, fp);
    
    		for(regCount = 0; regCount < sizeof(tmpVehicle.registration); regCount++){
    			if(sReg[regCount] == tmpVehicle.registration[regCount]){
    				regFound += 1;
    			}
    			else{
    				regFound = regFound;
    			}
    		}
    	rowNum += 1;
    	}
    	fclose(fp);
    
    	/* Return the status of our search */
    	if(regFound >= 10){
    		return 1;
    	}
    	else{
    		return 0;
    	}
    }
  6. #4
  7. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,155
    Rep Power
    2222
    In this code fragment for adding a vehicle:
    Code:
    /* Write the data to the disk */
    fp = fopen("A:vehicles.raf", "a");
    fseek(fp, ftell(fp) +1, SEEK_END);
    fwrite(&theVehicle, sizeof(theVehicle), 1, fp);
    fclose(fp);
    You have already opened the file for appending, so the file pointer is already in the correct position.

    However, your fseek is wrong, which might be causing your problem by writing to the wrong location. The origin constants of SEEK_SET and SEEKCUR will add the offset you provide to the beginning of the file or to the current file position respectively. But since there's nothing after the end of the file, SEEK_END will subtract the offset.

    Also, fseek and ftell offsets are in bytes, so to move the pointer by n records, you would need to use an offset of n*sizeof(theVehicle). Adding one to the current file pointer would only move it by one byte, not by one record.

    In reading the help file on ftell and fseek, I found references to possible problems in using ftell before an I/O operation has occurred; from the Visual C++1.52 help file on ftell():
    The ftell function gets the current position of the file pointer (if any) associated with stream. The position is expressed as an offset relative to the beginning of the stream.

    Note that when a file is opened for appending data, the current file position is determined by the last I/O operation, not by where the next write would occur. For example, if a file is opened for an append and the last operation was a read, the file position is the point where the next read operation would start, not where the next write would start. (When a file is opened for appending, the file position is moved to end-of-file before any write operation.) If no I/O operation has yet occurred on a file opened for appending, the file position is the beginning of the file.

    Return Value

    The ftell function returns the current file position. The value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage-return-line-feed translation. Use ftell in conjunction with the fseek function to return to file locations correctly. On error, the function returns -1L and errno is set to one of the following constants, defined in ERRNO.H:
    Constant Description
    EBADF Bad file number. The stream argument is not a valid file-handle value or does not refer to an open file.
    EINVAL Invalid argument. An invalid stream argument was passed to the function.
    This might do more to explain why you can't seem to add more records after a point. And if you examine a hex dump of your file, you should be able to see just where the latest record is actually being written. Or output the value of ftell() at strategic points.

IMN logo majestic logo threadwatch logo seochat tools logo