Previous Next |
StreamsTo 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 somewhere else. The name is inspired by water 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 a text source. Whether you work with a stream of bytes, characters, or other kinds of information, the overall process is the same:
FilesIn 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 or files you want to create. To create a File object, use the name of the file as the constructor, as in this example: File tutorialName = new File("address.dat"); This creates an object for a file named address.dat in the current folder. You also can include a path in the filename: File tutorialName = 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 StreamThe 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:
You will create an app 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 app reads an MP3 file using a file input stream, skipping everything but the last 128 bytes. The remaining bytes are examined to see whether 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. Listing 20.1. The Full Text of ReadID3.java1: 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: } 23: file.close(); 24: } catch (Exception e) { 25: System.out.println("Error -- " + e.toString()); 26: } 27: } 28: } When you compile this source file, a ReadID3 class file will be created. You can run this class as an app, specifying an MP3 file on your system as a command-line argument. For example: java ReadID3 "Perfect Silence.mp3" If you have the song Perfect Silence.mp3 on your system (and you really should), here's an example of what the ReadID3 app will display: Title: Perfect Silence Artist: Scapegoat Wax Album: Swax Year: 2002
The app 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 app 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 the app can read. The file Perfect Silence.mp3 will contain readable ID3 information if you created it from a copy of the Swax CD that you downloaded, 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 23. You should always close streams when you are finished with them to conserve resources in the Java interpreter.
Buffered Input StreamsOne 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 app. 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 apps. Enter the text of Listing 20.2 in your editor and save the file as ReadConsole.java. Listing 20.2. The Full Text of ReadConsole.java1: 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 app, 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. The ReadConsole class contains one class method, readLine(), which receives characters from the console. When the Enter key is pressed, 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.
|
Previous Next |