Sending It To The Stream
Introduction to File Output Operations
The following is an example of the simplest way to implement file output. In this example, and as mentioned in the previous topic, the stdio.h library is included which will allow your output (and later, input) operations to work. The following is the process that connects the file stream “plumbing” from your program to the hard drive.
fopen( "SomeFileName", "w" );
The above statement works quite well, but needs a little support. There are other parts of the program that have to pitch in to make this successful. Consider the following:
// At the top of the source file in the header files area,
// the stdio.h library is included
#include <stdio.h>
// It is always a good idea to create a variable for the file name
// this can be in the function where the file open operation is occurring
char fileName[] = "myfile.txt";
// A local variable or constant should be set
// to make the write character readable
// this can also be in the function
char writeCharacter = "w";
// Finally, a pointer to the data stream must be defined
FILE *filePointer;
filePointer = fopen( fileName, writeCharacter );
// other code
// other code
// other code
That is the code. Let's walk through it. First, if you are going to use the file output operations, you have to include the stdio.h library. This is conducted at the top of the code source file as you have seen before. Once that is done, you can call the fopen function anywhere in that source code file. When you call fopen, the system creates a file if it is not already there, and assigns a FILE pointer to the filePointer variable. This is your program's link to the data stream.
Either way, you are now ready to send your data to an output stream on your local directory that flows to myfile.txt. For example, to send data to this particular file, you would write the following code, which may look familiar to you, since it is very much like the printf you have seen previously:
fprintf( filePointer, "This data goes to the myfile.txt file.\n" );
That really is all there is to it. The function fprintf works almost exactly like printf so if the parameter has something that looks like a String, it will be output to the file. However, note that the first function parameter is filePointer. This is necessary so that the data gets sent to the correct data stream, and from there, the correct file. Note that if you needed to output an integer or double, you can add it to the string, as shown here.
fprintf( filePointer, "The answer is: %d\n", answerValue );
Note the back slash n ( '\n' ). Just like when using printf, this must be placed at the end of the output string if you wish for the line to be ended.
The last thing that needs to happen with output files is that you need to close them. Sometimes, just closing them will work. However, there are some circumstances where it is a good idea to add one or more end of line (i.e., new line) characters, then flush the buffer, and then close it. All the stdio.h operations, and indeed, almost all computer I/O operations, use a buffer, or temporary storage area, to store data that is in, or on the way to, the file stream, and in some cases, the data has not been moved from the buffer to the file when you are ready to close the file. That is why you "flush" the buffer, and then you can close the file. Theoretically, the closing action should flush the buffer but this does not always happen which is why adding a new line or two is a good idea. The code for these operations is provided here.
fprintf( filePointer, "\n\n" );
fflush( filePointer );
fclose( filePointer );
To follow up on the stream abstraction, and maintain the stream metaphor, you should think of the filePointer as your connection to the “plumbing” in the system. As long as the filePointer is doing its job, a “pipeline” that connects the stream to the hard drive or other storage device is available.
A person could get to liking this kind of abstraction. It makes life pretty simple.
Side Note: File Name Paths and Control Characters
There is another point to bring out about the use of file I/O. While it is better for educational reasons to access files from your local directory, there are times you may need to get data from a file that is in another directory. This is workable, but there is a little tactic of which you need to be aware. Normally, in order to use directory structures, you would need to use the backslash character ("\") to specify subdirectories. An example might look like c:\directory1\sublevel\myfile.txt.
However, in a computer, the backslash can mean other things when it is placed into a string; specifically, the backslash means that a control character might follow to send a tab ('\t'), a newline ('\n'), a carriage return ('\r'), or one of many other control characters. You have seen a little bit about control characters previously and you will be looking at more of them later in this topic. The problem here is that every time you show the backslash to your program, it thinks it is about to get a control character. To be able to use your backslash as a directory divider, you will need to tell the program that it is NOT a control character. Once you know about this, it is easy. The following is the correct way to pass a multiple-directory file path to a function for opening files in code. You just use two backslashes for each one, as shown. Note this is only required in your written code. If you are inputting this to a working program, a single backslash will work fine.
filePointer = fopen( "c:\\directory1\\sublevel\\myfile.txt", writeCharacter );
Another condition that occurs in streams is that sometimes you want to put quotes around outputted text. As you can see from the file names above, quotes are used by the program to identify the beginning and end of the text. Once again, you can use the backslash to "desensitize" the program to the quotes. Here are a couple of examples, using an output statement from an example above. For placing the myfile.txt quantity in double quotes, simply place the backslashes before each double quote, as shown here.
fprintf( filePointer, "This data is going to the \"myfile.txt\" file.\n" );
Actual output: This data is going to the "myfile.txt" file.
The backslashes themselves will not be shown in the outputted statement, but the quotes will, and they will not affect the rest of the output operation. The same process works for single quotes, as shown here.
fprintf( filePointer, "This data is going to the \'myfile.txt\' file.\n" );
Actual output: This data is going to the 'myfile.txt' file.
Some file output examples
For reasons that will become clear soon, the output process is just a little easier – or at least less messy – than the input process. This topic will finish with a few simple examples of file output operations to help you get started.
The following is an example of code that stores data in a text file. The program is run in the main function.
// in the global constants area
const char COMMA_SPACE[] = ", ";
const char WRITE_CHARACTER[] = "w";
// in a function
// initialize variables
FILE filePointer;
char fileName[] = "testfile.txt";
char name[] = "Bill";
int age = 16;
double payRate = 12.25;
// open file
// function: fopen
filePointer = fopen( fileName, WRITE_CHARACTER );
// print data to file
// function: fprintf
fprintf( filePointer,
"Name: %s, Age: %d, Pay Rate: %4.2f\n",
name, age, payRate );
// file close operations
// function: fprintf, fflush, fclose
fprintf( filePointer, "\n\n" );
fflush( filePointer );
fclose( filePointer );
Remember that you can put a lot into a given output string. Each percent sign ( '%' ) requires a parameter after the string in the fprintf (and printf) function, in order, to put the right data in the right places in the output string.
Watch this video to look at the whole program that would incorporate this strategy.
The various parts of the code shown might be in different areas of the program, but this is what the whole process looks like. Note the use of the fopen function used to get the file open, and the fprintf, fflush and fclose functions used to close the file.
There are times where you need to open a file in some function, like the main function, but conduct the output operations in another function. Here is an example of that process.
#include <stdio.h>
// global constants
const char COMMA_SPACE[] = ", ";
const char WRITE_CHARACTER[] = "w";
// function prototypes
void outputToFile( FILE *filePtr, const char *text,
int ageValue, double payValue );
// main function
int main()
{
// initialize variables
FILE *filePointer;
char fileName[] = "testfile.txt";
char name[] = "Bill";
int age = 16;
double payRate = 12.25;
// open file
// function: fopen
filePointer = fopen( fileName, WRITE_CHARACTER );
// print data to file
// function: outputToFile
outputToFile( filePointer, name, age, payRate );
// file close operations
// function: fprintf, fflush, fclose
fprintf( filePointer, "\n\n" );
fflush( filePointer );
fclose( filePointer );
return 0;
}
// supporting functions
void outputToFile( FILE *filePtr, const char *text,
int ageValue, double payValue )
{
// print data to file
// function: fprintf
fprintf( filePtr,
"Name: %s, Age: %d, Pay Rate: %4.2f\n",
text, ageValue, payValue );
}
Watch this video to look at the whole program that would incorporate this strategy.
A third way you might see output conducted would be if the whole process were implemented inside a function. In this case, the filePointer is not used in the main function, because it is only used inside the supporting function. Here is the example.
// under the header file area
#include <stdio.h>
// global constants
const char COMMA_SPACE[] = ", ";
const char WRITE_CHARACTER[] = "w";
// function prototypes
void outputToFile( const char *fileName, const char *text,
int ageValue, double payValue );
// main function
int main()
{
// initialize variables
char fileName[] = "testfile.txt";
char name[] = "Bill";
int age = 16;
double payRate = 12.25;
// print data to file
// function: outputToFile
outputToFile( fileName, name, age, payRate );
return 0;
}
// supporting functions
void outputToFile( const char *fileName, const char *text,
int ageValue, double payValue )
{
// initialize function, variables
FILE *filePointer;
// open file
// function: fopen
filePointer = fopen( fileName, WRITE_CHARACTER );
// print data to file
// function: fprintf
fprintf( filePointer,
"Name: %s, Age: %d, Pay Rate: %4.2f\n",
text, ageValue, payValue );
// file close operations
// function: fprintf, fflush, fclose
fprintf( filePointer, "\n\n" );
fflush( filePointer );
fclose( filePointer );
}
Watch this video to look at the whole program that would incorporate this strategy.
One important item to note here is that since you are passing a file name into the function, that means you expect to open and close the file inside the function; since that is the case, you will never pass both a file name (e.g., "myfile.txt") and a file pointer (e.g., filePointer) into this kind of function at the same time.
Any of these forms of output may be used, depending on your program needs. Generally speaking, if there is no need to access a file in any other location besides the one output operation, it should all be done in one function. However, you must also always consider the specifications you are given, in case there is some other reason to break up the process.
Results of Your Work: The Data File
The data file is nothing but a text file that can be opened with your IDE editor, or with MS Notepad, or any other simple text editor. You have already seen what the text file would look like from the output operations shown earlier. This kind of file must have something between the data values. In this case, there are both commas and spaces shown in the data between each value. However, either commas or spaces, or sometimes other characters alone are acceptable as delimiters, which are essentially characters used to divide valid data. Various ways to display data:
16 12.25 Bill
16, 12.25, Bill
16 - 12.25 - Bill
It is important to note that in the editor, the cursor must go down to the line below the data, as was shown in the videos. This is the reason the extra "\n\n" is always placed at the end which has the effect of pressing ENTER after outputting the data. If these characters are not placed and the file ends right at the end of the last data item, the input process may not work on the last item (e.g., in this case, the name "Bill") later on.
Also note that the data is separated by commas since that was done as part of outputting the data. This kind of data is called "comma-delimited", or "comma-delimited data" -- Microsoft™ calls it "comma-separated values" which is why they have .csv extension -- which again simply means that the data is divided by commas. Many spreadsheets can directly upload comma-delimited files. The format of this file will be important to consider when it comes time to input the data into a program.
The file output process is not very difficult, but does become more interesting when you start outputting larger quantities of data, as you will see in the next topic.
Upgrading the File I/O Process (and making it easier)
This topic presented activities and examples that demonstrate file output operations using standard C utilities, although this is just one class of many. It is good for you to see the inner workings of the standard file output operations in C However, implementing all this management while you are trying to learn file I/O can be problematic. From this point forward, a new utility called File_Output_Utility.c will be used for file output operations, and later, another class called File_Input_Utility.c will be used for file input operations. Again, like using the Console_IO_Utility.c, using these others will make your programming easier to start with.