When reading and writing text files :
- it is almost always a good idea to use buffering (default size is 8K)
- it is often possible to use references to abstract base classes, instead of references to specific concrete classes
- there is always a need to pay attention to exceptions (in particular, IOException and FileNotFoundException)
- always needs to be called, or else resources will leak
- will automatically flush the stream, if necessary
- calling close on a "wrapper" stream will automatically call close on its underlying stream
- closing a stream a second time has no consequence
- BufferedReader - readLine
- BufferedWriter - write + newLine
- Scanner - allows reading files in a compact way (example)
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader in = new InputStreamReader(fis, "UTF-8");
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8");
Scanner scanner = new Scanner(file, "UTF-8");
Example 1
This example uses JDK 1.5. To make it compatible with JDK 1.4, just change StringBuilder to StringBuffer:
import java.io.*;
public class ReadWriteTextFile {
/**
* Fetch the entire contents of a text file, and return it in a String.
* This style of implementation does not throw Exceptions to the caller.
*
* @param aFile is a file which already exists and can be read.
*/
static public String getContents(File aFile) {
//...checks on aFile are elided
StringBuilder contents = new StringBuilder();
try {
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(aFile));
try {
String line = null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*/
while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
return contents.toString();
}
/**
* Change the contents of text file in its entirety, overwriting any
* existing text.
*
* This style of implementation throws all exceptions to the caller.
*
* @param aFile is an existing file which can be written to.
* @throws IllegalArgumentException if param does not comply.
* @throws FileNotFoundException if the file does not exist.
* @throws IOException if problem encountered during write.
*/
static public void setContents(File aFile, String aContents)
throws FileNotFoundException, IOException {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
if (!aFile.exists()) {
throw new FileNotFoundException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Should not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}
//use buffering
Writer output = new BufferedWriter(new FileWriter(aFile));
try {
//FileWriter always assumes default encoding is OK!
output.write( aContents );
}
finally {
output.close();
}
}
/** Simple test harness. */
public static void main (String... aArguments) throws IOException {
File testFile = new File("C:\\Temp\\blah.txt");
System.out.println("Original file contents: " + getContents(testFile));
setContents(testFile, "The content of this file has been overwritten...");
System.out.println("New file contents: " + getContents(testFile));
}
}
Example 2
This example demonstrates using Scanner to read a file line by line (it does not perform a write operation) :
import java.io.*;
import java.util.Scanner;
public final class ReadWithScanner {
public static void main(String... aArgs) throws FileNotFoundException {
ReadWithScanner parser = new ReadWithScanner("C:\\Temp\\test.txt");
parser.processLineByLine();
log("Done.");
}
/**
* @param aFileName full name of an existing, readable file.
*/
public ReadWithScanner(String aFileName){
fFile = new File(aFileName);
}
/** Template method that calls {@link #processLine(String)}. */
public final void processLineByLine() throws FileNotFoundException {
Scanner scanner = new Scanner(fFile);
try {
//first use a Scanner to get each line
while ( scanner.hasNextLine() ){
processLine( scanner.nextLine() );
}
}
finally {
//ensure the underlying stream is always closed
scanner.close();
}
}
/**
* Overridable method for processing lines in different ways.
*
*This simple default implementation expects simple name-value pairs, separated by an
* '=' sign. Examples of valid input :
* height = 167cm
* mass = 65kg
* disposition = "grumpy"
* this is the name = this is the value
*/
protected void processLine(String aLine){
//use a second Scanner to parse the content of each line
Scanner scanner = new Scanner(aLine);
scanner.useDelimiter("=");
if ( scanner.hasNext() ){
String name = scanner.next();
String value = scanner.next();
log("Name is : " + quote(name.trim()) + ", and Value is : " + quote(value.trim()) );
}
else {
log("Empty or invalid line. Unable to process.");
}
//(no need for finally here, since String is source)
scanner.close();
}
// PRIVATE //
private final File fFile;
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private String quote(String aText){
String QUOTE = "'";
return QUOTE + aText + QUOTE;
}
}
Example run of this class :
Name is : 'height', and Value is : '167cm'
Name is : 'mass', and Value is : '65kg'
Name is : 'disposition', and Value is : '"grumpy"'
Name is : 'this is the name', and Value is : 'this is the value'
Done.
No comments:
Post a Comment