Sams Teach Yourself Java 2 in 24 Hours

Sams Teach Yourself Java 2 in 24 Hours

By Rogers Cadenhead

Streams

To save data permanently within a Java program, or to retrieve that data later, you must use at least one stream.

A stream is an object that takes information from one source and sends it to another. The name is inspired by streams that take fish, boats, inner tube riders, and industrial pollutants from one place to another.

Streams connect a diverse variety of sources, including computer programs, hard drives, Internet servers, computer memory, and CD-ROMs. Because all these things use streams, once you learn how to work with one kind of data, you will be able to work with others in the same manner.

During this hour, you will use streams to read and write data stored in files on your computer.

There are two kinds of streams:

All input and output streams are made up of bytes, individual integers with values ranging from 0 to 255. You can use this format to represent data, such as executable programs, word-processing documents, and MP3 music files, but those are only a small sampling of what bytes can represent. A byte stream is used to read and write this kind of data.

A more specialized way to work with data is in the form of characters—individual letters, numbers, punctuation, and the like. A character stream can be used when you are reading and writing text files, word-processing documents, Web pages and the like.

Whether you work with a stream of bytes, characters, or other kinds of information, the overall process is the same:

  1. You create a stream object associated with the data.
  2. You call methods of the stream to either put information in the stream or take information out of it.
  3. You close the stream by calling the object's close() method.

Files

In Java, files are represented by the File class, which also is part of the java.io package. Files can be read from hard drives, floppy drives, CD-ROMs, and other storage devices.

A File object can represent files that already exist and files you want to create. To create a File object, use the name of the file as the constructor, as in this example:

File bookName = new File("address.dat"); 

This creates an object for a file named address.dat in the current folder. You can also include a path in the filename:

File bookName = new File("data\address.dat"); 

Once you have a File object, you can call several useful methods on that object:

You also can use a File object to represent a folder on your system rather than a file. Specify the folder name in the File constructor, which can be absolute (such as "C:\MyDocuments\") or relative (such as "java\database").

Once you have an object representing a folder, you can call its listFiles() method to see what's inside the folder. This method returns an array of File objects representing every file and subfolder it contains.

Reading Data from a Stream

The first project of the hour is to read data from a file using an input stream. You can do this using the FileInputStream class, which represents input streams that will be read as bytes from a file.

You can create a file input stream by specifying a filename or a File object as the argument to the FileInputStream() constructor method.

The file must exist before the file input stream is created. If it doesn't, an IOException will be generated when you try to create the stream. Many of the methods associated with reading and writing files will generate this exception, so it's often convenient to put all statements involving the file in their own try-catch block, as in this example:

try { 
    File cookie = new File("cookie.web"); 
    FileInputStream = new FileInputStream(cookie); 
    System.out.println("Length of file: " + cookie.length()); 
} catch (IOException e) { 
    System.out.println("Could not read file."); 
} 

File input streams read data in bytes. You can read a single byte by calling the stream's read() method without an argument. If no more bytes are available in the stream because you have reached the end of the file, a byte value of -1 will be returned.

When you read an input stream, it begins with the first byte in the stream—such as the first byte in a file. You can skip some bytes in a stream by calling its skip() method with one argument: an int representing the number of bytes to skip. The following statement skips the next 1024 bytes in a stream named scanData:

scanData.skip(1024); 

If you want to read more than one byte at a time, do the following:

  1. Create a byte array that is exactly the size of the number of bytes you want to read.
  2. Call the stream's read() method with that array as an argument. The array will be filled with bytes read from the stream.

You will create an application that reads ID3 data from an MP3 audio file. Because MP3 is such a popular format for music files, 128 bytes are often added to the end of an ID3 file to hold information about the song, such as the title, artist, and album it is from.

The ReadID3 application reads an MP3 file using a file input stream, skipping everything but the last 128 bytes. The remaining bytes are examined to see if they contain ID3 data. If they do, the first three bytes will be the numbers 84, 65, and 71.

Open the editor you have been using, and enter the text of Listing 20.1. Save the file as ReadID3.java.

Example 20.1. The Full Text of ReadID3.java

 1: import java.io.*; 
 2: 
 3: public class ReadID3 { 
 4:     public static void main(String[] arguments) { 
 5:         try { 
 6:             File song = new File(arguments[0]); 
 7:             FileInputStream file = new FileInputStream(song); 
 8:             int size = (int)song.length(); 
 9:             file.skip(size - 128); 
10:             byte[] last128 = new byte[128]; 
11:             file.read(last128); 
12:             String id3 = new String(last128); 
13:             String tag = id3.substring(0, 3); 
14:             if (tag.equals("TAG")) { 
15:                 System.out.println("Title: " + id3.substring(3, 32)); 
16:                 System.out.println("Artist: " + id3.substring(33, 62)); 
17:                 System.out.println("Album: " + id3.substring(63, 91)); 
18:                 System.out.println("Year: " + id3.substring(93, 97)); 
19:             } else 
20:                 System.out.println(arguments[0] + " does not contain" 
21:                      + " ID3 info.");  
22:             file.close(); 
23:         } catch (Exception e) { 
24:             System.out.println("Error — " + e.toString()); 
25:         } 
26:     } 
27: } 

When you compile this source file, a ReadID3 class file will be created. You can run this class as an application, specifying an MP3 file on your system as a command-line argument. For example:

java ReadID3 Everlong.mp3 

If you have the song Everlong.mp3 on your system (and you really should), here's an example of what the ReadID3 application will display:

Title: Everlong 
Artist: Foo Fighters 
Album: The Colour and the Shape 
Year: 1997 

icon05.gif

The application reads the last 128 bytes from the MP3 in Lines 10–11 of Listing 20.1, storing them in a byte array. This array is used in Line 12 to create a String object that contains the characters represented by those bytes.

If the first three characters in the string are "TAG," the MP3 file being examined contains ID3 information in a format the application understands.

In Lines 15–18, the string's substring() method is called to display portions of the string. The characters to display are from the ID3 format, which always puts the artist, song, title, and year information in the same positions in the last 128 bytes of an MP3 file.

Some MP3 files either don't contain ID3 information at all or contain ID3 information in a different format than application can read.

The file Everlong.mp3 will contain readable ID3 information if you created it from a copy of The Colour and the Shape CD that you purchased, because programs that create MP3 files from audio CDs read song information from a music industry database called CDDB.

After everything related to the ID3 information has been read from the MP3's file input stream, the stream is closed in Line 22. You should always close streams when you are finished with them to conserve resources in the Java interpreter.

Buffered Input Streams

One of the ways to improve the performance of a program that reads input streams is to buffer the input. Buffering is the process of saving data in memory for use later when a program needs it. When a Java program needs data from a buffered input stream, it looks in the buffer first, which is faster than reading from a source such as a file.

To use a buffered input stream, you create an input stream such as a FileInputStream object, then use that object to create a buffered stream. Call the BufferedInputStream( InputStream ) constructor with the input stream as the only argument. Data will be buffered as it is read from the input stream.

To read from a buffered stream, call its read() method with no arguments. An integer from 0 to 255 will be returned that represents the next byte of data in the stream. If no more bytes are available, -1 is returned instead.

As a demonstration of buffered streams, the next program you create will add a feature to Java that many programmers miss from other languages they have used: console input.

Console input is the ability to read characters from the console (also known as the command line) while running an application.

The System class, which contains the out variable used in the System.out.print() and System.out.println() statements, has a class variable called in that represents an InputStream object. This object receives input from the keyboard and makes it available as a stream.

You can work with this input stream like any other. The following statement creates a buffered input stream associated with the System.in input stream:

BufferedInputStream bin = new BufferedInputStream(System.in); 

The next project, the ReadConsole class, contains a class method you can use to receive console input in any of your Java applications. Enter the text of Listing 20.2 in your editor and save the file as ReadConsole.java.

Example 20.2. The Full Text of ReadConsole.java

 1: import java.io.*; 
 2: 
 3: public class ReadConsole { 
 4:     public static String readLine() { 
 5:         StringBuffer response = new StringBuffer(); 
 6:         try { 
 7:             BufferedInputStream bin = new 
 8:                 BufferedInputStream(System.in); 
 9:             int in = 0; 
10:             char inChar; 
11:             do { 
12:                 in = bin.read(); 
13:                 inChar = (char) in; 
14:                 if (in != -1) { 
15:                     response.append(inChar); 
16:                 } 
17:             } while ((in != -1) & (inChar != '\n')); 
18:             bin.close(); 
19:             return response.toString(); 
20:         } catch (IOException e) { 
21:             System.out.println("Exception: " + e.getMessage()); 
22:             return null; 
23:         } 
24:     } 
25: 
26:     public static void main(String[] arguments) { 
27:         System.out.print("You are standing at the end of the road "); 
28:         System.out.print("before a small brick building. Around you "); 
29:         System.out.print("is a forest. A small stream flows out of "); 
30:         System.out.println("the building and down a gully.\n"); 
31:         System.out.print("> "); 
32:         String input = ReadConsole.readLine(); 
33:         System.out.println("That's not a verb I recognize."); 
34:     } 
35: } 

The ReadConsole class includes a main() method that demonstrates how it can be used. When you compile and run it as an application, the output should resemble the following:

You are standing at the end of the road before a small brick building. 
Around you is a forest. A small stream flows out of the building and 
down a gully. 

> go north 
That's not a verb I recognize. 

icon05.gif

The ReadConsole class contains one class method, readLine(), which receives characters from the console. When the Enter key is hit, readLine() returns a String object that contains all the characters that were received.

If you save the ReadConsole class in a folder that is listed in your CLASSPATH environment variable, you can call ReadConsole.readLine() from any Java program that you write.

Share ThisShare This

Informit Network