Sending It To The Stream
Introduction to File Output Operations
The following is an example of the simplest way to implement file I/O. In this example, the file output class is called FileWriter, and in the following example, outFile is the reference to the instantiated object. Once the following statement is implemented, all data output to the outFile reference will go to the file whose name is stored in the String variable fileName. This process connects the file stream “plumbing” from your program to the hard drive.
outFile = new FileWriter( fileName );
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, the FileWriter class must be imported
import java.io.FileWriter;
// Also, due to the try/catch operation below,
// the IOEXception class must also be imported
// also at the top of the source file
import java.io.IOException;
// Prior to instantiating the outFile object, it must be declared,
// usually at the beginning of the method
FileWriter outFile;
// Prior to using the String variable fileName, it must be declared,
// usually at the beginning of the method
String fileName = "myfile.txt";
// because this is an I/O operation, it must be monitored for exceptions
// using the try/catch process wrapped around the method
// Note that this is why the IOException class had to be imported
try
{
outFile = new FileWriter( fileName );
// other code
// other code
// other code
}
catch( IOException ioe )
{
// exception response
}
That is the code. Let's walk through it. First, if you are going to use the FileWriter class, you have to import it since it is not built in to the normal Java utilities. This is conducted at the top of the code source file as shown. Next, you must declare the outFile reference and as always, it is best to declare it at the beginning of the method. When you are ready to use it, you must wrap it in a try/catch process since all Java I/O operations may throw exceptions. Remember that this is because input and output operations may experience issues beyond the control of the programmer. To be able to use the try/catch process, you have to go back to the top of the source file and import the IOException class, as shown.
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:
outFile.write( "This data goes to the myfile.txt file." );
That really is all there is to it. The method write works almost exactly like print -- in System.out.print -- so if the parameter has something that looks like a String, it will be output to the file. Note that if you needed to output an integer or double, you can add it to the string, as shown here.
outFile.write( "The answer is: " + answerValue );
However, if you are attempting to output a numerical value by itself, you may have to fake out the system by pretending to have a string in the parameter, as shown here.
outFile.write( "" + answerValue );
Finally, while both a print and a println can be used with System.out, the same is not true with write. You must end your own strings to drop down to the next text line. This is not hard to do, and is shown here.
outFile.write( "This data goes to the myfile.txt file.\r\n";
The '\r' is used as a carriage return and the '\n' is used to create a new line. Some systems use one or the other but generally speaking you can safely use both and your text will be readable when it is displayed in most editors.
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 more end of line (i.e., carriage return/new line), then flush the buffer, and then close it. FileWriter uses 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 this is a good idea. The code for these operations is provided here but remember that this code must also be placed within the curly braces of the try code block.
outFile.write( "\r\n";
outFile.flush();
outFile.close();
To follow up on the stream abstraction, and maintain the stream metaphor, you should think of the output stream object (e.g., outFile in this case) as your connection to the “plumbing” in the system. As long as the outFile object is open, a “pipeline” that manages 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 method 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.
outFile = new FileWriter( "c:\\directory1\\sublevel\\myfile.txt" );
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.
outFile.write( "This data is going to the \"myfile.txt\" file.\r\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.
outFile.write( "This data is going to the \'myfile.txt\' file.\r\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 method.
// at the beginning of the class
private static final String COMMA_SPACE = ", ";
// variable initialization
FileWriter fileOut;
String name = "Bill";
String fileName = "textfile.txt";
int age = 16;
double payRate = 12.25;
// open try block
try
{
// instantiate the object, open the file
// method: FileWriter constructor
fileOut = new FileWriter( fileName );
// file output
// method: write
fileOut.write( age + COMMA_SPACE + payRate
+ COMMA_SPACE + name + "\r\n" );
// file closing
// method: write, flush, close
fileOut.write( "\r\n" );
fileOut.flush();
fileOut.close();
}
// catch exception as needed
catch( IOException ioe )
{
// exception response
}
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 FileWriter instantiation used to get the file open, and the write, flush and close methods used to close the file.
There are times where you need to open a file in some method, like the main method, but conduct the output operations in another method. Here is an example of that process. Note that because the fileOut object must be used in more than one method (i.e., the calling and the called methods), the fileOut object must be made global to the class, as shown. Note also that this method tests for output success using the try/catch operation.
// at the beginning of the class
private static final String COMMA_SPACE = ", ";
FileWriter fileOut;
// variable initialization
String name = "Bill";
String fileName = "textfile.txt";
int age = 16;
double payRate = 12.25;
// open try block
try
{
// instantiate the object, open the file
// method: FileWriter constructor
fileOut = new FileWriter( fileName );
// file output, test for success
// method: outputToFile
if( outputToFile( name, age, payRate ) )
{
// display output success
// method: printString, printEndline
conIO.printString( "Output successful" );
conIO.printEndline();
// file closing
// method: write, flush, close
fileOut.write( "\r\n" );
fileOut.flush();
fileOut.close();
}
}
// catch exception as needed
catch( IOException ioe )
{
// exception response
// method: printString, printEndline
conIO.printString( "Output failed" );
conIO.printEndline();
}
-----------------------------------------------------------
// supporting method implementation
private static boolean outputToFile( String text,
int ageValue, double payValue )
{
// open try block
try
{
// send data to file
// method: write
outFile.write( ageValue + COMMA_SPACE + payValue
+ COMMA_SPACE + text + "\r\n" );
}
// catch exception as needed
catch( IOException ioe )
{
// exception response
return false;
}
// method success - return true
return true;
}
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 method. In this case, the output object would not be global to the class, because it is only used inside the method. Here is the example.
// at the beginning of the class
private static char COMMA_SPACE = ", ";
// variable initialization
String fileName = “textfile.txt”;
String name = "Bill";
int age = 16;
double payRate = 12.25;
// output data to file in main method, check for success
// method: outputTofile
successFlag = outputToFile( fileName, name, age, payRate );
-----------------------------------------------------------
// supporting method implementation
public static boolean outputToFile( String fileName, String text,
int ageValue, double payValue )
{
// initialize method/variables
FileWriter fileOut;
// open try block
try
{
// instantiate the object, open the file
fileOut = new FileWriter( fileName );
// send data to file
// method: write
fileOut.write( ageValue + COMMA_SPACE + payValue
+ COMMA_SPACE + text + "\r\n" );
// close file
// method: write, flush, close
fileOut.write( "\r\n" );
fileOut.flush();
fileOut.close();
}
// catch exception as needed
catch( IOException ioe )
{
// exception response
return false;
}
// method success - return true
return true;
}
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 method, that means you expect to open and close the file inside the method; since that is the case, you will never pass both a file name (e.g., "myfile.txt") and a file object (e.g., fileOut) into this kind of method 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 method. 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 "\r\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 Java utilities, although this is just one class of many. It is good for you to see the inner workings of the FileWriter class and how it is managed with the exception (try/catch) management. However, implementing all this exception and other management while you are trying to learn file I/O can be problematic. From this point forward, a new class called File_Output_Class will be used for file output operations, and later, another class called File_Input_Class will be used for file input operations. Again, like using the Console_IO_Class, using these others will make your programming easier to start with.