Data Structures and File Streams

NULL is defined in the stdio.h header file. NULL is defined as either 0 or (void*)0; either way, the two values evaluate to logical false and, when compared to each other, return true. Still, it’s not a good idea to initialize a pointer to the constant value 0.

#include <stdio.h>
#include <stdlib.h>

/* ------ main function ------- */

int main ( int argc, char *argv[] )
{
    if(NULL){
        printf("NULL is true.\n");
    } else {
        printf("Null is false.\n");
    }

    if(0==NULL){
        printf("0 == NULL\n");
    } else {
        printf("0 != NULL\n");
    }


    return EXIT_SUCCESS;
}

/* ----------  end of main function  ---------- */

 Remember, when using pointers, we ought to use NULL, and not 0.

When data is read as a character stream, the application requests each character sequentially, each of which is returned as an integer (not a char) until end-of-file is reached. When EOF is reached,  a negative value, which cannot represent an ASCII character, is returned.

The main concept behind stream I/O is the files can be seen as ordered sequences of characters, which can likewise be processed in a sequential manner.

The getchar() and putchar() functions are usually implemented as macros, which makes them even faster.

 Our next program will read a flat text file and use the information to populate an array of structures. The flat text file, named sales.txt, is as follows:

Atlanta 782038.46
Austin 299772.84
Chicago 816093.76
Dallas 751815.11
Denver 936772.82
Houston 759756.27
Indianapolis 574831.60
Kansas City 844308.93
Los Angeles 969770.37
Mexico City 510140.09
Minneapolis 787488.02
Montreal 316987.23
New York 657590.42
Pittsburgh 898253.56
St. Louis 677488.73
San Francisco 513126.19
Toronto 905377.23

Each line will correspond to a structure that contains two fields, the regional office name, a char array, and the amount of sales, a double value.

The following program will read through this list using the fgetc() function, which retrieves a single character from the stream at a time. We will also use the ungetc() function to return a character to the stream; we will do this when implementing a peek() function of our own design.

#include     <stdio.h>
#include    <stdlib.h>
#include    <string.h>

#define TRUE 1
#define FALSE 0
#define OFFICE_NAME_LENGTH 32
#define BUFFER_SIZE 256

struct sales
{
    char office[OFFICE_NAME_LENGTH+1];
    double revenue;
};



char buffer[BUFFER_SIZE];

char peek(FILE *fp);

/* ------ main function ------- */

int main ( int argc, char *argv[] )
{
    struct sales SalesInfo[20];
    double total = 0;
    int ch, ch2;
    int i = 0;
    int entry = 0;
    int word = 0;
    //flags
    int endOfWord = FALSE;
    int endOfLine = FALSE;

    FILE *fp = NULL;

    fp = fopen("sales.txt", "r");

    //let's do an explicit test
    if(fp==NULL){
        printf("Could not open file.\n");
        return EXIT_FAILURE;
    }


    while((ch=fgetc(fp))!=EOF){
        //item is finished when we
        //hit end of line
        //or space without a number after it
        if(ch=='\n'){
            endOfWord = TRUE;
            endOfLine = TRUE;
        }
        if(ch=='\r'){
            continue;
        }
        //check to see if space is just part of
        //city name or else if it is between
        //the name and number
        if(ch==' '){
            ch2 = peek(fp);
            if('0' <= ch2 && ch2 <= '9'){
                endOfWord = TRUE;
            }
        }

        if(endOfWord==FALSE){
            buffer[i++] = ch;
        } else {
            //put string termination character
            buffer[i] = '';
            if(word % 2 == 0){
                strcpy(SalesInfo[entry].office, buffer);
            } else {

                SalesInfo[entry].revenue = atof(buffer);
            }
            i = 0;
            word++;    
            //reset end of word flag
            endOfWord = FALSE;
        }
        //check for end of line
        if(endOfLine==TRUE){
            //new line, new entry
            entry++;
            //reset flag
            endOfLine=FALSE;
        }        
    }//end while loop

    for(i = 0; i < entry; i++){
        //format office field to be 32 chars in length
        //format double to display two digits to the right
        //of the decimal
        printf("%32s  $ %-32.2f\n", SalesInfo[i].office, SalesInfo[i].revenue);
    }

    fclose(fp);

    return EXIT_SUCCESS;
}



/* ----------  end of main function  ---------- */
char peek(FILE *fp){
    char c = fgetc(fp);
    ungetc(c, fp);
    return c;
}

When dealing with flat text files that store data, it’s best to have some sort of delimiter, that is to say a special character that separates the fields. Commas and colons are the two delimiters most often used. For our final program, we will use colons.

Atlanta:GA:172381.48:32854.08:Schwartz
Chicago:IL:451776.83:9516.12:Burton
Dallas:TX:286582.26:56528.12:Han
Denver:CO:381825.96:69108.36:Lopez
Houston:TX:588617.94:22489.38:Patel
Kansas City:MO:394415.29:25890.03:Johnson
Los Angeles:CA:578920:99141.30:Verma
Minneapolis:MN:377488.50:23511.77:Harris
Raleigh-Durham:NC:210149.24:81298.44:Kadivar
San Francisco:CA:751622.91:18520.31:Shen
St. Louis:MO:395114.47:68191.38:Foshay

We will name the above file sales2.txt. It consists of eleven lines, with each line being a record. Each line is divided into five fields. The first field contains the city, the second field contains the state, the third field contains the revenue, the fourth field contains the expenses, and the last field contains the last name of the manager.

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>

int countRecords(FILE *pFile);

struct SRecord{
    char szCity[32];
    char szState[2];
    double dRevenue;
    double dExpenses;
    char szManager[32];
};

char buffer[256];

/* ------ main function ------- */

int main ( int argc, char *argv[] )
{
    int iCounter = 0;
    int iNumRecords;
    int iField = 0;
    int iRecord = 0;
    int iChar = 0;

    FILE *pFile = fopen("sales2.txt", "r");

    if(pFile==NULL){
        printf("Problem opening file.\n");
        EXIT_FAILURE;
    }

    iNumRecords = countRecords(pFile);

    printf("There are %d records.\n", iNumRecords);

    struct SRecord *pSRecords = malloc(sizeof(struct SRecord)*iNumRecords);

    
    while((iChar=fgetc(pFile))!=EOF){
        if(iChar=='\n'|| iChar==':'){
            buffer[iCounter]='';
            switch(iField){
                case 0:
                    strcpy(pSRecords[iRecord].szCity, buffer);
                    break;
                case 1:
                    strcpy(pSRecords[iRecord].szState, buffer);
                    break;
                case 2:
                    pSRecords[iRecord].dRevenue = atof(buffer);
                    break;
                case 3:
                    pSRecords[iRecord].dExpenses = atof(buffer);
                    break;
                case 4:
                    strcpy(pSRecords[iRecord].szManager, buffer);
                    break;
            }
            //move to next record
            //reset field count
            if(iChar=='\n'){
                iField = 0;
                iRecord++;
            } else {
                //move to the next field
                iField++;
            }
            //reset buffer counter
            iCounter = 0;
        } else {
            buffer[iCounter++]=iChar;
        }
    }//end while loop


    for(iRecord = 0; iRecord < iNumRecords; iRecord++){
        printf("%32s %3s",pSRecords[iRecord].szCity, pSRecords[iRecord].szState);
        printf("%12.2f \t %12.2f\n", pSRecords[iRecord].dRevenue, pSRecords[iRecord].dExpenses);
    }

    //free the memory
    free(pSRecords);
            

    return EXIT_SUCCESS;
}

/* ----------  end of main function  ---------- */

//count the number of records
int countRecords(FILE *pFile){
    int iReturnVal = 0;
    int iTemp;
    //count number of newlines
    while((iTemp=fgetc(pFile))!=EOF){
        if(iTemp=='\n'){
            iReturnVal++;
        }    
    }//end while loop

    rewind(pFile);

    return iReturnVal;
}
// ---- end countRecords() -----

In the program above, we dynamically allocated the array of structs based on the number of lines we counted in the file. Note that we rewound the file pointer back to the beginning of the file after counting the number of lines.

 

 

 

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s