File Writer and File Reader

FileWriter is a convenience class for writing characters to files. It subclasses OutputStreamWriter, and its constructors call OutputStreamWriter(OutputStream). An instance of this class is equivalent to the following code fragment:

FileOutputStream fos = new FileOutputStream(pathname); OutputStreamWriter osw;

osw = new OutputStreamWriter(fos, System.getProperty("file.encoding"));

In Chapter 4, I presented a logging library with a File class (Listing 4-19) that did not incorporate file-writing code. Listing 10-28 addresses this situation by presenting a revised File class that uses FileWriter to log messages to a file.

Listing 10-28. Logging messages to an actual file package logging;

import java.io.FileWriter; import java.io.IOException;

class File implements Logger {

private final static String LINE_SEPARATOR = System.getProperty("line.separator"); private String dstName; private FileWriter fw;

File(String dstName) {

this.dstName = dstName;

public boolean connect() {

if (dstName == null) return false;

fw = new FileWriter(dstName);

catch (IOException ioe) {

return false;

return true;

public boolean disconnect() {

catch (IOException ioe) {

return false;

return true;

public boolean log(String msg) {

fw.write(msg + LINE_SEPARATOR);

catch (IOException ioe) {

return false;

return true;

Listing 10-28 refactors Listing 4-19 to support FileWriter by making changes to each of the connect(), disconnect^), and log() methods:

■ connect() attempts to instantiate FileWriter, whose instance is saved in fw upon success; otherwise, fw continues to store its default null reference.

■ disconnect() attempts to close the file by calling FileWriter's close() method, but only when fw does not contain its default null reference.

■ log() attempts to write its String argument to the file by calling FileWriter's void write(String str) method, but only when fw does not contain its default null reference.

connect()'s catch clause specifies IOException instead of FileNotFoundException because FileWriter's constructors throw IOException when they cannot connect to existing normal files; FileOutputStream's constructors throw FileNotFoundException.

log()'s write(String) method appends the line.separator value (which I assigned to a constant for convenience) to the string being output instead of appending \n, which would violate portability.

FileReader is a convenience class for reading characters from files. It subclasses InputStreamReader, and its constructors call InputStreamReader(InputStream). An instance of this class is equivalent to the following code fragment:

FileInputStream fis = new FileInputStream(pathname); InputStreamReader isr;

isr = new InputStreamReader(fis, System.getProperty("file.encoding"));

Unix introduced a command-line utility called grep (global regular expression print) that searches files or standard input globally for those lines matching a given regex, and prints matching lines to the standard output device.

To demonstrate FileReader, Listing 10-29 presents a FindAll application as a vastly scaled down version of grep. FindAll is useful for identifying the paths and names of those files that contain content matching the specified regex, and that is it.

Listing 10-29. Finding all files that contain content matching a regex import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException;

import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException;

public class FindAll {

static Matcher m;

public static void main(String[] args) {

System.err.println("usage: java FindAll regex [pathname]"); return;

Pattern p = Pattern.compile(args[0]); m = p.matcher("");

String cwd = System.getProperty("user.dir"); findAll(new File(args.length == 2 ? args[l] : cwd), p);

catch (PatternSyntaxException pse) {

pse.printStackTrace();

static void findAll(File file, Pattern p) {

System.err.println(file + " is not a directory"); return;

System.err.println("unable to access " + file + "'s contents"); return;

for (int i = 0; i < files.length; i++) if (files[i].isDirectory())

findAll(files[i], p); else if (find(files[i].getPath(), p))

System.out.println(files[i].getPath());

static boolean find(String pathname, Pattern p) {

BufferedReader br = null;

FileReader fr = new FileReader(pathname); br = new BufferedReader(fr);

String line;

catch (IOException ioe) {

ioe.printStackTrace();

finally {

catch (IOException ioe) {

ioe.printStackTrace();

return false;

After compiling the regex into a Pattern object, creating a Matcher object, and obtaining the current working directory (also known as the current user directory), main() calls the recursive findAll() method with the starting search path and Pattern object.

NOTE: The Apache Commons IO library (http://commons.apache.org/io/) includes a FileUtils class (http://commons.apache.org/io/api-

1.4/org/apache/commons/io/FileUtils.html) that provides methods for recursively listing the contents of all subdirectories of a directory while applying a filter (and more).

findAll() verifies that its File argument is a directory and then calls listFiles() on this object to obtain an array of File objects for all directory entries. For each File object that is a directory, findAll() calls itself with the File and Pattern objects. However, if the entry is a file, findAll() calls find() with the File object's pathname string and Pattern object.

find() attempts to open the pathname for input via the FileReader(String pathname) constructor. Assuming success, it chains a BufferedReader object to the file reader in order to speed up file reading through fewer calls to the file reader's read() methods.

BufferedReader declares a handy String readLine() method for reading a line of text (not including line-termination characters) into a String object. readLine() returns null when the end of the input is reached; it throws IOException when an I/O error occurs.

When a line is read, the Matcher object's reset() method is called with the line's String object as an argument. The matcher is reset so that the same Matcher object can be reused (to avoid creating unnecessary Matcher objects).

Finally, Matcher's find() method is called to look for a match between the Pattern object's compiled regex and the line. If a match is found, find() returns true and the loop ends by returning true from find(); otherwise, the next line is read and searched.

Now that you know how FindAll works, you will probably want to try it out. The following examples show you how I might use this application on my XP platform:

java FindAll Emboss \prj\dev

This example searches my \prj\dev directory on my default drive (C:) for all files that contain the word Emboss (case is significant) and generates the following output:

\prj\dev\ebooks\javase\mj2dipq\c04\code\IP\Emboss.java

\prj\dev\ebooks\javase\mj2dipq\c04\code\IP\EmbossOp.java

\prj\dev\ebooks\javase\mj2dipq\c04\code\IP\IP.java

\prj\dev\ljfad\c10\1-4302-3156-1_Friesen_Ch10.doc

\prj\dev\ws\java\java.html

\prj\dev\wsold\java\java.html

If I now specify java FindAll emboss \prj\dev, I observe the following slightly different output:

\prj\dev\ebooks\javase\mj2dipq\c04\code\IP\Emboss.java

\prj\dev\ebooks\javase\mj2dipq\c04\code\IP\EmbossOp.java

\prj\dev\ljfad\c10\1-4302-3156-1_Friesen_Ch10.doc

\prj\dev\ws\java\java.html

\prj\dev\wsold\java\java.html

At the end of Chapter 9, I presented a useful demonstration of RegExDemo for matching phone numbers with or without area codes. Suppose I have a file named withareacodes.txt that contains (800) 555-1212, and a file named withoutareacodes.txt that contains 555-1212 in my current directory. When I execute java FindAll "(\(\d{3}\))?\s*\d{3}-\d{4}", I observe the following output:

C:\prj\dev\ljfad\c10\code\FindAll\withareacodes.txt C:\prj\dev\ljfad\c10\code\FindAll\withoutareacodes.txt

This output reveals that regex (\(\d{3}\))?\s*\d{3}-\d{4} matched (800) 555-1212 in withareacodes.txt and 555-1212 in withoutareacodes.txt. I can now identify all files in a starting directory/subdirectories containing phone numbers with/without area codes.

EXERCISES

The following exercises are designed to test your understanding of Java's classic I/O APIs:

1. What is the purpose of the File class?

2. What do instances of the File class contain?

3. What does File's listRoots() method accomplish?

4. What is a path and what is a pathname?

5. What is the difference between an absolute pathname and a relative pathname?

6. How do you obtain the current user (also known as working) directory?

7. What is a parent pathname?

8. File's constructors normalize their pathname arguments. What does normalize mean?

9. How do you obtain the default name-separator character?

10. What is a canonical pathname?

11. What is the difference between File's getParent() and getName() methods?

12. True or false: File's exists() method only determines whether or not a file exists.

13. What is a normal file?

14. What does File's lastModified() method return?

15. True or false: File's list() method returns an array of Strings where each entry is a filename rather than a complete path.

16. What is the difference between the FilenameFilter and FileFilter interfaces?

17. True or false: File's createNewFile() method does not check for file existence and create the file if it does not exist in a single operation that is atomic with respect to all other filesystem activities that might affect the file.

18. File's createTempFile(String, String) method creates a temporary file in the default temporary directory. How can you locate this directory?

19. Temporary files should be removed when no longer needed after an application exits (to avoid cluttering the filesystem). How do you ensure that a temporary file is removed when the virtual machine ends normally (it does not crash or the power is not lost)?

20. How would you accurately compare two File objects?

21. What is the purpose of the RandomAccessFile class?

22. What is the purpose of the "rwd" and "rws" mode arguments?

23. What is a file pointer?

24. True or false: When you call RandomAccessFile's seek(long) method to set the file pointer's value, and if this value is greater than the length of the file, the file's length changes.

25. What is a flat file database?

26. What is a stream?

27. What is the purpose of OutputStream's flush() method?

28. True or false: OutputStream's close() method automatically flushes the output stream.

29. What is the purpose of InputStream's mark(int) and reset() methods?

30. How would you access a copy of a ByteArrayOutputStream instance's internal byte array?

31. True or false: FileOutputStream and FileInputStream provide internal buffers to improve the performance of write and read operations.

32. Why would you use PipedOutputStream and PipedInputStream?

33. What is a filter stream?

34. What does it mean for two streams to be chained together?

35. How do you improve the performance of a file output stream or a file input stream?

36. How do DataOutputStream and DataInputStream support FileOutputStream and FileInputStream?

37. What is object serialization and deserialization?

38. What three forms of serialization and deserialization does Java support?

39. What is the purpose of the Serializable interface?

40. What does the serialization mechanism do when it encounters an object whose class does not implement Serializable?

41. Identify the three stated reasons for Java not supporting unlimited serialization.

42. How do you initiate serialization? How do you initiate deserialization?

43. True or false: Class fields are automatically serialized.

44. What is the purpose of the transient reserved word?

What does the deserialization mechanism do when it attempts to deserialize an object whose class has changed?

How does the deserialization mechanism detect that a serialized object's class has changed?

How can you add an instance field to a class and avoid trouble when deserializing an object that was serialized before the instance field was added? What JDK tool can you use to help with this task?

How do you customize the default serialization and deserialization mechanisms without using externalization?

How do you tell the serialization and deserialization mechanisms to serialize or deserialize the object's normal state before serializing or deserializing additional data items?

How does externalization differ from default and custom serialization and deserialization?

How does a class indicate that it wishes to support externalization?

True or false: During externalization, the deserialization mechanism throws InvalidClassException with a "no valid constructor" message when it does not detect a public noargument constructor.

What is the difference between PrintStream's print() and println() methods?

What does PrintStream's noargument void println() method accomplish?

True or false: PrintStream's %tR format specifier is used to format a Calendar object's time as HH:MM.

Why are Java's stream classes not good at streaming characters?

What does Java provide as the preferred alternative to stream classes when it comes to character I/O?

True or false: Reader declares an available() method.

What is the purpose of the OutputStreamWriter class? What is the purpose of the InputStreamReader class?

How do you identify the default character encoding?

What is the purpose of the FileWriter class? What is the purpose of the FileReader class?

Create a Java application named Touch for setting a file's or directory's timestamp to the current or specified time. This application has the following usage syntax: java Touch [-d timestamp] pathname. If you do not specify [-d timestamp], pathname's timestamp is set to the current time; otherwise, it is set to the specified timestamp value, which has the formatyyyy-MM-dd HH:mm:ss z (2010-08-13 02:37:45 UTC and 2006-04-22 12:35:45 EST are examples). Hints: The Date class has a getTime() method whose return value can be passed to File's setLastModified() method. Also, you will find Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").parse(args[1]); and System.err.println("invalid option: " + args[0]); to be helpful.

NOTE: Wikipedia's "touch (Unix)" entry

(http://en.wikipedia.org/wiki/Touch_%28Unix%29) introduces you to a standard Unix program named touch. In addition to changing a file's access and modification timestamps, touch is used to create a new empty file.

63. Suppose you are creating a Media class whose static methods perform various media-oriented utility tasks; for example, a getID3Info() method returns an object containing information about an MP3 file (such as song title and artist). This information is typically stored in a 128-byte block at the end of the file according to a format known as ID3. The block begins with ASCII sequence TAG.

Listing 10-30 reveals the skeletal contents of the Media class and this method, and begins with a description of the ID3v1.1 format that getID3Info() targets (there are several versions of this format).

Listing 10-30. The Media class and lis getID3Info() method

In 1996, Eric Kemp devised ID3, a small file format for storing metadata in MP3 files. This metadata consisted of song title, artist, album, year, comments, and genre; and was organized into a 128-byte block stored at the end of the file.

The following table describes the format of ID3 version 1 (ID3v1): Offset (decimal) Field Name Field Size (byte)

0 Signature (TAG) 3

3 Song title 30

33 Artist 30

63 Album 30

93 Year 4

97 Comment 30

127 Genre 1

Each field except for Genre is a string of ASCII characters. Strings are padded on the right with zeros or spaces. An uninitialized field is equivalent to the empty string ("").

In 1997, Michael Mutschler made a small improvement to ID3. Because the Comment field is too small to write anything of use, he trimmed this field by two bytes and used those bytes to store the CD track number where the song is located.

If a track number is stored, the second-last byte of the Comment field is set to 0 and the subsequent byte stores the track number. The resulting format is known as ID3v1.1.

Offset (decimal) Field Name Field Size (byte)

0 Signature (TAG) 3

3 Song title 30

33 Artist 30

Album 30

Year 4

Comment 29 (last byte must be a binary 0)

Unlike most of the fields, Track and Genre are single-byte integer fields. The legal values that can appear in the Genre field and their descriptions

Are described in

the

following table

0 Blues

IO

Alternative

4O

AlternRock

SO

Top 40

1 Classic Rock

Il

Ska

4l

Bass

Sl

Christian Rap

2 Country

II

Death Metal

4I

Soul

SI

Pop/Funk

3 Dance

IB

Pranks

4B

Punk

SB

Jungle

4 Disco

I4

Soundtrack

44

Space

S4

Nat American

5 Funk

IS

Euro-Techno

4S

Meditative

SS

Cabaret

6 Grunge

IS

Ambient

4S

Instrumental Pop

SS

New Wave

7 Hip-Hop

I7

Trip-Hop

47

Instrumental Rock

S7

Psychadelic

8 Jazz

IB

Vocal

4B

Ethnic

SB

Rave

9 Metal

I9

Jazz+Funk

49

Gothic

S9

Showtunes

10 New Age

BO

Fusion

SO

Darkwave

7O

Trailer

11 Oldies

Bl

Trance

Si

Techno-Industrial

7l

Lo-Fi

12 Other

BI

Classical

SI

Electronic

7I

Tribal

13 Pop

BB

Instrumental

SB

Pop-Folk

7B

Acid Punk

14 R&B

B4

Acid

S4

Eurodance

74

Acid Jazz

15 Rap

BS

House

SS

Dream

7S

Polka

16 Reggae

BS

Game

SS

Southern Rock

7S

Retro

17 Rock

B7

Sound Clip

S7

Comedy

77

Musical

18 Techno

BB

Gospel

SB

Cult

7B

Rock & Roll

19 Industrial

B9

Noise

S9

Gangsta

79

Hard Rock

WinAmp expanded

this

table with the

following Genre codes:

80 Folk

9I

Progressive Rock lO4

Chamber Music

llS

Ballad

81 Folk-Rock

9B

Psychedelic Rock lOS

Sonata

ll7

Power Ballad

82 National-Folk

94

Symphonic Rock

lOS

Symphony

llB

Rhythmic Soul

83 Swing

9S

Slow Rock

lO7

Booty Brass

ll9

Freestyle

84 Fast Fusion

9S

Big Band

lOB

Primus

lIO

Duet

85 Bebob

97

Chorus

lO9

Porn Groove

lIl

Punk Rock

86 Latin

9B

Easy Listening

llO

Satire

lII

Drum Solo

87 Revival

99

Acoustic

lll

Slow Jam

lIB

A cappella

88 Celtic

lOO

Humour

llI

Club

lI4

Euro-House

89 Bluegrass

lOl

Speech

llB

Tango

lIS

Dance Hall

90 Avantgarde

lOI

Chanson

ll4

Samba

91 Gothic Rock

lOB

Opera

llS

Folklore

Although there are probably additional defined codes, treat any other value stored in the Genre field as Unknown.

To learn more about ID3 and new versions, visit the official site at id3.org.

import java.io.IOException; import java.io.RandomAccessFile;

public class Media

public static class ID3 {

private String songTitle, artist, album, year, comment, genre; private int track; // -1 if track not present public ID3(String songTitle, String artist, String album, String year, String comment, int track, String genre)

this.songTitle = songTitle; this.artist = artist; this.album = album; this.year = year; this.comment = comment; this.track = track; this.genre = genre;

String getSongTitle() { return songTitle; } String getArtist() { return artist; } String getAlbum() { return album; } String getYear() { return year; } String getComment() { return comment; } int getTrack() { return track; } String getGenre() { return genre; }

public static ID3 getID3Info(String mp3path) throws IOException {

return null;

Your job is to fill in the getID3Info() method by using the RandomAccessFile class to obtain the data from the 128-byte block, and then create and populate an ID3 object with this data. getID3Info() subsequently returns this object.

After completing getID3Info(), you will want to test this method. Listing 10-31 presents the source code to a TestMedia application that you can use for this purpose.

Listing 10-31. The TestMedia class import java.io.IOException;

public class TestMedia {

public static void main(String[] args) {

System.err.println("usage: java TestMedia mp3path"); return;

Media.ID3 id3Info = Media.getID3Info(args[0]);

System.err.printf("%s not MP3 or has no ID3 block%n", args[0]); return;

System.out.println("Song title = " + id3Info.getSongTitle()); System.out.println("Artist = " + id3Info.getArtist());

" + id3Info.getAlbum()); + id3Info.getYear()); = " +id3Info.getComment()); " + id3Info.getTrack()); " + id3Info.getGenre());

System.out.println("Album System.out.println("Year = " System.out.println("Comment System.out.println("Track = System.out.println("Genre =

catch (IOException ioe) {

ioe.printStackTrace();

Suppose you have ripped a track from a CD that contains Tom Jones' "She's a Lady" into a file called Lady.mp3. When you execute java TestMedia Lady.mp3, you should see output similar to the following:

Song title = She's A Lady Artist = Tom Jones

Album = Billboard Top Soft Rock Hits -

Year = 1998

Comment =

Genre = Rock

64. Create a Java application named Split for splitting a large file into a number of smaller partx files (where x starts at 0 and increments; for example, part0, parti, part2, and so on). Each partx file (except possibly the last partx file, which holds the remaining bytes) will have the same size. This application has the following usage syntax: java Split pathname. Furthermore, your implementation must use the BufferedInputStream, BufferedOutputStream, File, FileInputStream, and FileOutputStream classes.

NOTE: I find Split helpful for storing huge files that do not fit onto a single CD/DVD across multiple CDs/DVDs, and also for emailing huge files to friends. To recombine the part files on a Windows platform, I use the copy command and its /B binary option. When recombining the part files, recombine them in order: part0, parti ... part9, parti0, and so on.

65. It is often convenient to read lines of text from standard input, and the

InputStreamReader and BufferedReader classes make this task possible. Create a Java application named CircleInfo that, after obtaining a BufferedReader instance that is chained to standard input, presents a loop that prompts the user to enter a radius, parses the entered radius into a double value, and outputs a pair of messages that report the circle's circumference and area based on this radius.

Was this article helpful?

0 0

Responses

Post a comment