Jaa Io (7)

  • November 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Jaa Io (7) as PDF for free.

More details

  • Words: 17,094
  • Pages: 70
The java.io package Learning objective

After completing this topic, you should be able to use the classes of the java.io package to access files, and read and write data for a given scenario.

1. The java.io.File class Programs often need to write and read data to and from external sources - such as files, other programs, or network resources. The Java Input/Output (I/O) package - java.io - enables programs written in Java to read and write data in various formats. Text, sound, graphics, and video files can all be processed by appropriate classes from the java.io package. The java.io package contains classes that enable you to access data both sequentially and at random.

Note In sequential data access, data is read or written in sequence, from the first record to the last. Nonsequential or random - data access involves reading or writing data in a random order. The Reader and Writer classes, and their various subclasses, are used for sequential data access. These classes input or output data sequentially as ordered streams of bytes. The RandomAccessFile class and its subclasses input or output data in nonsequential streams. The java.io.File class - which represents either a directory or a single file within the file system - allows you to navigate, describe, and access those files or directories. The Java security manager allows only specified operations to be performed within a given security context. Because most browsers don't allow any kind of file access, the File class and related I/O classes are usually used in applications instead of applets. Creating a File object does not necessarily mean that you create a real file or directory. While representing the name of a file, the File object does not represent, or enable you to access, the data in the file. Similarly, when a File object is deleted by the garbage collector, no physical files are deleted. Creating a File object does not actually create a file on the local system. It merely encapsulates the specified string in one of a number of constructors: •

File (File directoryObj, String fileName)



File (String pathName)



File (String pathName, String fileName)

File (File directoryObj, String fileName) This constructor creates a new File instance using the pathname from an existing File instance and a string containing the filename. File (String pathName) This constructor creates a new File instance using the given pathname string. File (String pathName, String fileName) This constructor creates a new File instance using the given pathname string and filename string. Consider the code that creates a simple file access application in Java. To access a file, you should first import the relevant Java classes, in this case File and RandomAccessFile. import import import import import import

java.io.File; java.io.RandomAccessFile; java.io.IOException; java.awt.*; java.awt.event.*; java.util.Date;

public class ReadTextFile extends Frame { TextField tb; Label lb; TextArea contents; public static void main(String args[]) { ReadTextFile readFile = new ReadTextFile(); readFile.setVisible (true); readFile.pack(); } } Once you've imported the relevant java.io classes, you need to create an instance of the File class. You can extract the filename from a text field, trim it, and construct a File instance from the result. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { String s ; RandomAccessFile textFile = null; long len; contents.setText(null) ; File f = new File (tb.getText().trim()) ; if(f.exists() && f.isFile() && f.canRead()) { try { textFile = new RandomAccessFile (f, "r") ; } catch (IOException error) { contents.setText ("Error creating RandomAccessFile") ; return ; }

Suppose that part of an application you're creating uses the static File and listRoots methods to return an array of File objects - allDrives - that represents the root directories on a system. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { ReportAction r = new ReportAction(); r.setPriority(Math.min(r.getPriority() + 1, Thread.MAX_PRIORITY)) ; r.start() ; File[] allDrives = MISSING CODE; for (int i=1; i
exists



getName



getAbsolutePath



isDirectory



isFile



canRead

exists You use the exists method to confirm whether a specified file exists. The method returns a value of true if the file exists. getName You use the getName method to return the name of a file or directory - the last element of a full pathname. getAbsolutePath The getAbsolutePath method returns the full absolute path to a file or directory, whether the file was initially constructed using a relative pathname or not. isDirectory The isDirectory method returns a value of true if the string specified in the object's constructor represents a directory instead of a file. isFile You use the isFile method to determine whether a File object represents a file or a directory. The method returns a boolean value of true if an abstract filename exists, and is a normal file - that is, the File object does not represent a directory. Otherwise it returns false. canRead You use the canRead method to determine whether data can be read from a file. It returns a boolean value of true only if the file exists and can be read by the application. Otherwise it returns false. Consider the code that uses the isDirectory method to determine whether an array element - a File instance - is a directory. If it is, the application will search it for a specified file. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { ReportAction r = new ReportAction() ; r.setPriority(Math.min(r.getPriority() + 1, Thread.MAX_PRIORITY)); r.start() ; File[] allDrives = File.listRoots(); for (int i=1 ; i
for (i=0; i
Question

Suppose you have created a File object named fileTarget to represent a target file. You want to ensure that the file represented by fileTarget is a normal file and that the application can read from it. Complete the code to check whether fileTarget is a normal file.

contents.setText(null) ; // Create a File object that represents the target file File fileTarget = new File (tb.getText().trim()) ; BufferedReader textFile = null ; if (fileTarget.exists() && MISSING CODE1() && MISSING CODE2 ) {

Answer To complete the code that ensures that fileTarget really is a file, you type fileTarget.isFile()

Question You now want to ensure that the application can read from the file represented by fileTarget. Complete the code to do this.

contents.setText(null) ; // Create a File object that represents the target file File fileTarget = new File (tb.getText().trim()) ; BufferedReader textFile = null ; if (fileTarget.exists() && fileTarget.isFile() && MISSING CODE ) {

Answer To complete the code that ensures that the application can read from the file represented by fileTarget you type fileTarget.canRead()

2. Low-level streams When Java reads or writes data, it opens a data stream, reads or writes the information, and closes the stream. Java uses the stream, reader, and writer classes for streamed data.

The stream classes deal with general data input and output, whereas the reader and writer classes deal specifically with Unicode and Unicode Transformation Format (UTF) string input and output. Data received from or sent to general I/O devices always consists of bytes. However, Java can support higher-level I/O by piecing together bytes to represent other types of data, such as integers, characters, or strings. For example, a sequence of four bytes can make up an int. Java uses a hierarchy of classes to deal with different types of data. The InputStream and OutputStream are abstract classes that use low-level I/O streams to read bytes from or send them to I/O devices such as files, network sockets, and pipes. Low-level streams provide access directly to underlying bytes, whereas high-level streams build upon lowlevel streams for additional capabilities. The FilterInputStream and FilterOutputStream classes, which extend InputStream and OutputStream respectively, use high-level filter streams to read or write data, such as strings and ints, from byte streams. All low-level stream classes inherit from either the InputStream or OutputStream abstract classes. Many stream input classes have a corresponding output class with similar methods. For example, the FileInputStream class, which is the input class for files, has a corresponding output class, FileOutputStream. The low-level streams that are direct descendants of the InputStream or OutputStream include •

ByteArrayInputStream and ByteArrayOutputStream



FileInputStream and FileOutputStream



ObjectInputStream and ObjectOutputStream



PipedInputStream and PipedOutputStream



SequenceInputStream

ByteArrayInputStream and ByteArrayOutputStream The ByteArrayInputStream and ByteArrayOutputStream classes read and write arrays of bytes with buffering. FileInputStream and FileOutputStream The FileInputStream receives data from a file in byte form. The FileOutputStream outputs the data to a file. ObjectInputStream and ObjectOutputStream The ObjectInputStream deserializes primitive data and objects that have been previously serialized using an ObjectOutputStream object. PipedInputStream and PipedOutputStream The PipedInputStream and PipedOutputStream classes deal with thread communication. They enable you to create two sides of a stream and connect them.

SequenceInputStream The SequenceInputStream class enables you to concatenate other input streams and to read from each of them, in turn. The corresponding InputStream and OutputStream subclasses have complementary structures and functions, including •

constructors



read and write methods



reading and writing arrays

constructors The FileInputStream and FileOutputStream classes have similar constructors. The syntax for the constructors is FileInputStream(String pathname) FileInputStream(File file) FileOutputStream(String pathname) FileOutputStream(File file) read and write methods The FileInputStream class uses the read method to read the next byte from a file, whereas the FileOutputStream class uses the write method to write a byte to a file. The syntax for these methods is int read () throws IOException void write (int ) throws IOException reading and writing arrays The FileInputStream and FileOutputStream classes have complementary read and write methods for reading and writing arrays of bytes. The syntax for these methods is int read(byte[] b) int read(byte[] b, int off, int len) void write(byte[] b) void write(byte[] b, int off, int len) Consider the code that reads information on sales from a file and prints it to the standard output. To do this, the code creates a DataInputStream. This class has an InputStream data member, which is inherited from FilterInputStream. for (int i = 0; i < descs.length; i ++) { myData = new SalesData (descs[i], amounts[i], values[i]) ;

writeSalesData ( myData ) ; } out.close() ;

// Prepare to read it in in = new DataInputStream(new FileInputStream(fruitfile)) ; for (int i=0; i<6; i++) { myData = readSalesData () ; System.out.println("You sold " + myData.desc + " at $" + myData.value + " each. Amount was " + myData.amount); } in.close() ; } The DataInputStream object wraps a FileInputStream instance to allow native datatypes to be read in a machine-independent fashion. You can layer stream objects together to create streams capable of performing very specific I/O functions. For example, you can extend FilteredInputStream to create custom filters that discriminately read formatted data. Your custom filter can, in turn, be chained to other streams.

Question Suppose you are using an input stream to access bytes from a disk file. Which low-level stream would you use to provide access directly to underlying bytes? Options:

1. 2. 3. 4.

ByteArrayInputStream FileInputStream ObjectInputStream PipedInputStream

Answer The FileInputStream allows you to access bytes from a file. Option 1 is incorrect. The ByteArrayInputStream class reads arrays of bytes with buffering. An object of this class operates on a memory byte buffer. Option 2 is correct. The FileInputStream is used to read data from a file in byte form. The FileOutputStream outputs the data to a file.

Option 3 is incorrect. The ObjectInputStream deserializes complex datatypes that have previously been written by an ObjectOutputStream object. Option 4 is incorrect. The PipedInputStream class deals with thread communication. Piped input streams are connected to piped output streams. Typically, one thread writes to a PipedOutputStream and another thread reads from a PipedInputStream.

3. High-level streams High-level input and output streams communicate with low-level streams rather than with I/O devices. You can use them for high-level input and output. Most of Java's high-level input and output classes inherit attributes from the FilterInputStream and FilterOutputStream superclasses. These classes in turn, inherit from the abstract InputStream and OutputStream classes. When using the constructor for one of these classes, for example DataInputStream, you need to pass an InputStream to the constructor as a parameter. DataInputStream(InputStream objectName) You can use any class that inherits from the InputStream class as an input source for a high-level stream. For example, you can use a FileInputStream object that you've already created, or use input from a socket or pipe. When a high-level stream object, such as an instance of the DataInputStream class, receives byte input from a low-level stream, it processes the bytes and converts them into the appropriate datatype. The DataInputStream class contains read methods that can convert bytes into all the primitive datatypes, as well as into UTF strings. For example, the readInt method reads the next four bytes and converts them into an int. So these four bytes must represent an int if the method is to work correctly. For this to happen, data must be read in the same order in which it is written to a stream. To close a DataInputStream object, you use the class's close method. If you need to close a chain of stream objects, you do so in reverse order so that the object that was created first is closed last. This prevents you closing an InputStream before you close the high-level stream that uses it as an input source. For example, the code sample that reads sales data from a file uses the close method to close an instance of the DataInputStream object. for (int i = 0; i < descs.length; i ++) { myData = new SalesData (descs[i], amounts[i], values[i]); writeSalesData ( myData ) ; }

out.close() ;

// Prepare to read it in in = new DataInputStream(new FileInputStream(fruitfile)) ; for (int i=0; i<6; i++) { myData = readSalesData () ; System.out.println("You sold " + myData.desc + " at $" + myData.value + " each. Amount was " + myData.amount) ; } in.close() ; } The subclasses of the FilterOutputStream class include •

DataOutputStream



BufferedOutputStream



PrintStream

DataOutputStream You use the DataOutputStream to write data to a stream by passing an OutputStream to a DataOutputStream object as a parameter when you create the object. BufferedOutputStream You use the BufferedOutputStream to write data to a buffer, which in turn writes it to the underlying stream. PrintStream A PrintStream allows other output streams to conveniently print data of various formats. This class never throws an IOException, unlike other output streams. The methods of the DataOutputStream class process data such as characters, integers, and UTF strings, convert it to bytes, and write it to the stream. File f = new File (myFileName); if(f.exists() && f.isFile() && f.canWrite()) { try { FileOutputStream fostream = new FileOutputStream(f); DataOutputStream dostream = new DataOutputStream(fostream); dostream.writeUTF("Some UTF data"); dostream.close(); fostream.close(); } catch (IOException e) {

} }

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View the DataInputStream and DataOutputStream methods. Consider the code that creates a FileOutputStream object named fostream, and a DataOutputStream object named dostream. File f = new File (myFileName); if(f.exists() && f.isFile() && f.canWrite()) { try { FileOutputStream fostream = new FileOutputStream(f); DataOutputStream dostream = new DataOutputStream(fostream); dostream.writeUTF("Some UTF data"); dostream.close(); fostream.close(); } catch (IOException e) { } } In doing so, the code writes the DataOutputStream to the FileOutputStream. - fostream. And it passes a string as a parameter to the writeUTF method, which writes it to the output stream. Finally, it closes the two streams in the correct order, so that the one that was created last is closed first. File f = new File (myFileName); if(f.exists() && f.isFile() && f.canWrite()) { try { FileOutputStream fostream = new FileOutputStream(f); DataOutputStream dostream = new DataOutputStream(fostream); dostream.writeUTF("Some UTF data"); dostream.close(); fostream.close(); } catch (IOException e) {

} }

Question Suppose you have created a FileOutputStream and named it foutstream. You wish to process data such as characters, integers, and UTF strings and write this data to foutstream. Complete the code to do this.

FileOutputStream foutstream = new FileOutputStream(f) ; DataOutputStream (doutstream) = new MISSING CODE ;

Answer To complete the code you type DataOutputStream(foutstream)

Question You are creating an application that provides a streaming news ticker. You have already coded the portion that broadcasts the information in an encrypted form, and now wish to build a factory method for converting the byte stream into characters. Which class would you use to convert the byte stream? Options:

1. DataInputStream 2. FileInputStream 3. PrintStream Answer The DataInputStream contains read methods that can convert bytes into all the primitive datatypes. Option 1 is correct. The DataInputStream class receives byte input from a low-level stream, processes the bytes, and converts them into the appropriate datatype. The data format of the file must be determinable. Option 2 is incorrect. The FileInputStream receives data from a file in byte form but does not convert the bytes to any other datatype. Option 3 is incorrect. The PrintStream constructors wrap another output stream and contain overloaded print methods for outputting formatted data.

4. Reader and Writer classes Reader classes are similar to input streams, and writer classes are similar to output streams. The family of related reader classes descend from the abstract class Reader, and Writer classes descend from the abstract Writer class. Like stream classes, readers and writers are divided into low-level classes that communicate with I/O devices and high-level classes that communicate with the low-level ones. Unlike streams, however, readers and writers are designed specifically for Unicode characters. So whereas low-level streams, for example, deal with bytes, low-level readers and writers deal with chars. The java.io package provides the following low-level Reader classes: •

FileReader



CharArrayReader



PipedReader



StringReader

FileReader The FileReader class is used to read streams of characters from a file. This class is useful to read text files. CharArrayReader The CharArrayReader class reads arrays of characters by implementing a character buffer. The character array acts like a character input stream. PipedReader The PipedReader class provides a piped character-input stream. It should be used with a piped character-output stream so that data written to the PipedWriter will be available from this reader. StringReader The StringReader class uses strings as its source of a character stream. Individual characters can be marked and read from the string. The high-level reader classes include •

BufferedReader



FilterReader



InputStreamReader

BufferedReader The BufferedReader class is used to read text from a character-input stream.

You can use the class to improve the efficiency of your code. This is because buffers enable you to write and read data in bulk. It's recommended to always use buffered I/O. FilterReader The FilterReader class is an abstract class that is used to filter character streams. By overriding the appropriate methods of FilterReader, a subclass can decide what gets read and how it is handled. For example, you can filter lines from a file, based on a regular expression. InputStreamReader The InputStreamReader is a class that is used to convert a byte stream into a set of characters, using a specified Charset. You can use InputStreamReader to accept input from System.In, up to a designated escape character or sequence. Consider the code for the InnerActionListener class. The FileReader class, which the application uses to read data, has two constructors, one of which takes a File object as a parameter. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { String s ; long len ; contents.setText(null) ; File f = new File (tb.getText().trim()) ; if (f.exists() && f.isFile() && f.canRead()) { try { FileReader buff = new FileReader (f) ; BufferedReader theFile = new BufferedReader(buff) ; while ((s = theFile.readLine()) != null) { contents.append (s + "\n") } target.setText(tb.getText().trim()+"2") ; theFile.close() ; FileReader(String pathname) FileReader(File file) So the application first creates a File object. The File object allows you to interrogate the file system. The file object is passed into the constructor for a FileReader called buff. The FileReader is a lowlevel object that allows you to read from a file. BufferedReader - one of the high-level readers in the java.io package - has an internal buffer that enables data to be read in large blocks, thus reducing I/O overhead. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { String s ; long len ;

contents.setText(null); File f = new File (tb.getText().trim()) ; if (f.exists() && f.isFile() && f.canRead()) { try { FileReader buff = new FileReader (f) ; BufferedReader theFile = new BufferedReader(buff); while ((s = theFile.readLine()) != null) { contents.append (s + "\n") ; } target.setText(tb.getText().trim()+"2") ; theFile.close(); And its readLine method can read the next line of text sent to it by a low-level reader. String readLine() throws IOException It's a good idea to wrap buffered readers around unbuffered readers to make I/O more efficient. A BufferedReader object can accept any type of low-level reader as an input source. For example, you can specify that the buff FileReader object will be used as an input source by passing it into the BufferedReader class's constructor as a parameter. class InnerActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { String s ; long len ; contents.setText(null); File f = new File (tb.getText().trim()) ; if (f.exists() && f.isFile() && f.canRead()) { try { FileReader buff = new FileReader (f) ; BufferedReader theFile = new BufferedReader(buff) ; while ((s = theFile.readLine()) != null) { contents.append (s + "\n") ; } target.setText(tb.getText().trim()+"2") ; theFile.close() ; And you can use a while loop to read the next line from the specified file and display it in the application's contents area. As with input and output streams, most reader classes have a corresponding writer class. In this example, an application uses a FileReader and a BufferedReader to read files, and it uses a FileWriter and a BufferedWriter to write them.

class innerButtonListener extends MouseAdapter{ public void mouseClicked(MouseEvent evt) { File f = new File(target.getText().trim()); Button b2; if(f.exists()) { enter.setText("This file already exists"); return; } try{ FileWriter output = new FileWriter(f); BufferedWriter out = new BufferedWriter(output); String s = contents.getText(); //write out contents of TextArea out.write(s,0,s.length()); //send output from write to file out.flush(); out.close(); //close files output.close(); } The write method of BufferedWriter writes the data from the application's contents area. But it does not write data to its destination if the amount of data is smaller than the BufferedWriter object's buffer. If that's the case, the object stores the data instead of writing it to the file. When the buffer's size limit is reached, the object writes the contents of the object's buffer to the file. To prevent this data being lost when you close the file, it's good practice to use the flush method to send all remaining data from the BufferedWriter object's buffer to the FileWriter object. When executing an application that uses the input and output classes, there are often exception errors that occur and can be thrown by the JVM. Some important ones include •

FileNotFoundException



EOFException



InterruptedIOException



ObjectStreamException

FileNotFoundException A FileNotFoundException occurs when an attempt to locate a file at a specified path is unsuccessful. EOFException An EOFException occurs when the end of a file is reached unexpectedly. InterruptedIOException An InterruptedIOException occurs when the input or output operation is interrupted unexpectedly. ObjectStreamException The ObjectStreamException class is the base class for errors thrown by ObjectStream classes.

Question Suppose fContent contains the name of the file you wish to read from. Complete the code to instantiate the FileReader object.

File fContent = new File (tb.getText().trim()) ; if (fContent.exists() && fContent.isFile() && fContent.canRead()) { try { FileReader buff = MISSING CODE ;

Answer To complete the code to instantiate the FileReader class you type new FileReader (fContent)

Question Suppose you want to use a BufferedReader object to read a line from a file. Complete the code to do this.

File fContent = new File (tb.getText().trim()) ; if (fContent.exists() && fContent.isFile() && fContent.canRead()) { try { FileReader buff = new FileReader (fContent) ; BufferedReader theFile = new BufferedReader(buff) ; while ((s = theFile.MISSING CODE) != null) { contents.append (s + "\n") ; }

Answer You type readLine() to complete the code to read a line from a file into variable s.

Question You have coded a basic word processing application. You mistype a document's file name when trying to open it. Which exception is thrown? Options:

1. EOFException

2. FileNotFoundException 3. InterruptedIOException 4. ObjectStreamException Answer The exception returned when you try to open a file that does not exist is FileNotFoundException. Option 1 is incorrect. An EOFException is returned if the end of a file is reached unexpectedly. Option 2 is correct. The FileNotFoundException occurs when the file does not exist in the path that is specified in the code. Option 3 is incorrect. An InterruptedIOException occurs when an input or output operation is interrupted unexpectedly. Option 4 is incorrect. The ObjectStreamException class is the base class for errors thrown by ObjectStream classes.

Summary When Java reads or writes data, it opens a data stream, reads or writes the information, and closes the stream. Java uses the stream, reader, and writer classes for streamed data. Java can support higher-level I/O by allowing anonymous bytes to be viewed as formatted data, such as integers, characters, or strings. Java uses low-level classes to communicate with I/O devices and it uses high-level classes to communicate with low-level streams and process data such as strings, integers, and floating-point numbers. So when you instantiate high-level streams, you need to specify a low-level stream as a data source or destination. High-level input streams convert bytes into datatypes such as strings and integers. High-level output streams, on the other hand, convert various datatypes into bytes. You should close input and output chains, starting with the high-level streams, so that you don't cut off the data source in the low-level streams before the high-level streams have finished with them. Reader and writer classes function in a similar way to input and output streams, except that they handle Unicode character data instead of bytes. Low-level readers and writers communicate with I/O devices, whereas high-level readers and writers communicate with low-level reader and writer objects.

Table of Contents | Top of page |

The java.nio package Learning objective

After completing this topic, you should be able to use the classes of the java.nio package to modify buffers, retrieve a channel, and transfer data between channels.

1. Functions of the java.nio package In the java.nio package, NIO stands for "new I/O", and java.nio replaces the java.io package. The primary purpose of NIO is to facilitate nonblocking I/O operations and partial file locking. In many aspects, NIO performs faster and more efficiently than standard I/O. For example, NIO enables you to read and write to a file using a single two-way FileChannel connection. The java.nio package is able to work in conjunction with the existing application programming interfaces (APIs). The features of the java.nio package include •

mapped and locked files



asynchronous I/O



channels



buffers



character set encoders and decoders

mapped and locked files The java.nio package offers two file-related features - memory-mapped files and file locking. Memory-mapped files create a map that enables you to work with file data as if it occupies a space in memory. File locking enables you to make a file available to multiple lock files to grant access to particular applications only. asynchronous I/O The java.nio package enables you to add nonblocking and selectable I/O to Java code. When using nonblocking I/O, all the necessary actions for a read or write operation are performed immediately. When using asynchronous I/O, the main thread is able to select a stream that is ready to transmit or receive data. It then moves to another stream if the original stream blocks. channels The java.nio package uses channels for communication in much the same way as the java.io package uses streams. A channel acts as an endpoint for communication. Devices then use their own methods for reading and writing data.

The Channel interface defines only two methods - isOpen returns whether a channel is open or closed and close() closes a channel. buffers The java.nio package is built around ByteBuffer objects, unlike the java.io package, which uses byte arrays. The ByteBuffer objects enable you to set buffers to read-only and to track the position of data written to or read from a buffer. ByteBuffer also allows you to write all primitive datatypes. You can set buffers as direct buffers, which use buffers that are hosted by the operating system. character set encoders and decoders Character set encoders and decoders enable you to convert raw data into characters and vice versa. The Charset class in the java.nio.charset package enables the encoding and decoding.

Question Match the feature of the java.nio package with its associated function. Options: 1. 2. 3. 4.

Asynchronous I/O Buffer Channel Character set encoder and decoder

Targets: A.Enables you to add nonblocking and selectable I/O to Java code B.Enables you to convert raw data into characters C.Enables you to track the position of data written to or read from a buffer D.Provides an endpoint for communication

Answer Asynchronous I/O enables you to add nonblocking and selectable I/O to Java code. Character set encoders and decoders enable you to convert raw data into characters. Buffers enable you to track the position of data written to or read from a buffer, and channels provide an endpoint for communication. When using asynchronous I/O, the main thread is able to select a stream that is ready to transmit or receive data. Unlike the java.io package, which uses byte arrays, java.nio is built around ByteBuffer objects that enable you to set buffers to read-only as well as to keep track of data in buffers. The Channel interface tests whether a channel is open with the isOpen method. The close method closes the channel. The java.nio.charset package allows for encoding and decoding of raw data and character sets.

2. Buffer methods Buffer is a direct subclass of Object and it resides in the java.nio package. Unlike arrays, Buffer stores its state internally. So you no longer need to control the state externally.

Note The Buffer class is abstract, so it's not possible to create an instance of it. ByteBuffer, or any other Buffer subclass, extends the abstract Buffer class. A buffer has the following three properties. •

position



capacity



limit

position The position property holds the position where the next element is to be read from or written to the buffer. A position property with a value of 2, for example, indicates that the next element should be written to or read from position 2. capacity The capacity property is the size of the buffer or the number of elements it can hold. For example, a capacity of 16 indicates the buffer can hold 16 elements.The capacity is set when the buffer is created. limit The limit property is the position of the first element in the buffer which should not be read or written to. The limit value must be between 0 and the buffer's capacity, and can be set at runtime by calling the limit method. There are several methods in the Buffer and ByteBuffer classes that can be used to modify the properties of a Buffer object: •

clear



mark



reset



put



get



flip



compact

clear The clear method sets the position property to zero and sets the limit property equal to the capacity property. It does this without affecting the data currently in the buffer. You use clear() to make the buffer ready to accept new data. The original data remains intact but may eventually be overwritten.

The syntax for the clear method is buffer_Object.clear(); mark The mark method is used to set a position in the buffer that you can return to in a future call. So you use mark() when you need to remember the position where data was written. You can then return to the position and overwrite the data if necessary. The syntax for the mark method is buffer_Object.mark(); reset You use the reset method to set the position attribute equal to the position of the mark. If you used the mark attribute to mark off a position to which data would be copied, using reset() is useful if you need to rewrite the data. The syntax for the reset method is buffer_Object.reset(); put The put method is used to write data to a ByteBuffer. When you write to the buffer, the position to which you write is always between the mark and the limit. The syntax for the put method is buffer_Object.put(byte b); get You use the get method to read bytes from a ByteBuffer. When you read from the buffer, the position from which you read is always between the mark and the limit. The syntax for the get method is buffer_Object.get(); flip You use the flip method to set the limit equal to the position. It then sets the position to zero. It is useful to use flip()after writing data to the buffer. You can then use get() to read from position zero to the limit. The syntax for the flip method is buffer_Object.flip();

compact You use the compact method to move all data to the head of the buffer. It then sets the position to where the data ends. Finally, it sets the limit equal to the capacity. The compact method is useful to free up space in the buffer. You can then use flip()to read from the buffer. The syntax for the compact method is buffer_Object.compact(); Consider the code that creates a simple file copier. First you need to declare a buffer variable and specify its size. import java.io.*; import java.nio.*; public class BufferFileCopier { public static void main (String [] args) throws IOException { long fileSize; ByteBuffer myBuffer; File workingLocation = new File("/testfiles"); File inFile = new File(workingLocation, "input.txt"); File outFile = new File(workingLocation, "output.txt"); FileReader inReader = new FileReader(inFile); FileWriter outWriter = new FileWriter(outFile); fileSize = inFile.length(); myBuffer = ByteBuffer.allocate((int) fileSize); Next you get the handles to the files you wish to copy and create File objects which you'll use to access the files. Then you determine how large the ByteBuffer needs to be and allocate a buffer of the required size. Now you want to read a file into the buffer called myBuffer, so that the bytes can be read from myBuffer and written to the output file. for(int i = 0; i < (int) fileSize; i++) { MISSING CODE(inReader.read() ); }

You type myBuffer.put to read the file into the buffer you have created. You have read the file into the buffer called myBuffer. for(int i = 0; i < (int) fileSize; i++) {

myBuffer.put(inReader.read() ); } Once you've read the file into the buffer, you need to rewind the buffer so that it can be read. And then you create the code to read the bytes from myBuffer and write them to the output file. myBuffer.rewind(); for(int i = 0; i < (int) fileSize; i++) { outWriter.write((char) myBuffer.get()); } inReader.close(); outWriter.close(); } }

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View the full code for the file copier.

Question Suppose you have a buffer 12 bytes long, with the position at 2 and the limit at 9. What are the values after using the clear method? Options: 1. 2. 3. 4.

position = 0, limit = 0, capacity = 0 position = 0, limit = 0, capacity = 12 position = 0, limit = 12, capacity = 12 position = 12, limit = 12, capacity = 12

Answer The clear method sets the position to zero and sets the limit to the capacity.

Option 1 is incorrect. The capacity value is unchanged when using the clear method on a buffer. Option 2 is incorrect. The clear method sets the limit equal to the capacity, which is 12. Option 3 is correct. You use the clear method to make the buffer ready to accept new data. The original data will be overwritten by any subsequent writes. Option 4 is incorrect. Although the clear method sets the limit equal to the capacity, it also sets the position to zero.

Question Suppose you have a buffer, 12 bytes long, with the position at 2 and the limit at 9. What are the values after using the flip method? Options: 1. 2. 3. 4.

position = 0, limit = 0, capacity = 0 position = 0, limit = 2, capacity = 12 position = 9, limit = 12, capacity = 12 position = 12, limit = 12, capacity = 12

Answer The flip method sets the limit to 2, the position to zero, and the capacity - 12 - remains unchanged. Option 1 is incorrect. The flip method sets the limit equal to the position. The capacity is unchanged. So the capacity value should be 12 and the limit should be 2. Option 2 is correct. The flip method sets the limit equal to the position. It then sets the position to zero. It is useful to use the flip method after reading data into the buffer. You can then use the get() method to read from position zero to the limit. Option 3 is incorrect. Firstly, the limit is set to the position, which is 2. Then, the position is set to 0. Option 4 is incorrect. Although the flip method leaves the capacity unchanged, it alters the position and limit. The position should be set to 0 and the limit should be 2.

3. Using channels Channels are to the java.nio package what streams are to the java.io package. They are used for communicating with files and devices. Channels implement the Channel interface. This interface queries whether a channel is open or closed and provides a close method to close the channel. Once closed, a channel cannot be reopened. The channel classes have many advantages over stream-based models:



cleaner inheritance hierarchy



common read/write operations



improved buffer integration



I/O interruptibility

cleaner inheritance hierarchy Classes implement channel interfaces, whereas API and custom streams are modelled around abstract classes. A class can implement many interfaces and still extend another class. common read/write operations A channel represents a two-way connection to a file, socket, or device. You can perform read and write operations using this single connection. improved buffer integration With channels, you can map files to memory buffers, and read and write directly to ByteBuffer objects. I/O interruptibility Apart from providing platform-specific optimizations, channels allow interruptible, nonblocking I/O and atomic read/write operations. Diverse channel types are designed to work smoothly with each other. The java.nio.channels package contains various classes that implement a variety of channel interfaces. •

FileChannel



Pipe.SinkChannel



Pipe.SourceChannel



SocketChannel

FileChannel The FileChannel subclass implements the ByteChannel, GatheringByteChannel, and ScatteringByteChannel interfaces, among others. You can use the FileChannel class when you want to read and write to a file. It also allows you to map a region of a large file to memory. You use the FileChannel class in a similar way to RandomAccessFile, except that the FileChannel class works with ByteBuffer. Pipe.SinkChannel The Pipe.SinkChannel nested class implements the WritableByteChannel and GatheringByteChannel interfaces. This class represents the writable end of a pipe. You write data to a sink channel and read data from a source channel. Pipe.SourceChannel The Pipe.SourceChannel subclass implements the ReadableByteChannel and ScatteringByteChannel interfaces. This channel represents the readable end of a pipe. You use the Pipe.SourceChannel subclass when you want to read data from a source channel. Data is read in the same order as it was written to the associated sink channel.

SocketChannel The SocketChannel subclass implements ByteChannel, ScatteringByteChannel and GatheringByteChannel interfaces, among others. The SocketChannel subclass represents nonblocking socket communication, asynchronous shutdown, and is safe to use in a multithreaded environment.

Question Match the Channel subclasses to their functions. Options:

1. 2. 3. 4.

FileChannel Pipe.SinkChannel Pipe.SourceChannel SocketChannel

Targets: A.Connects sockets for communication B.Manipulates a file by reading from and writing to it C.Reads data from a pipe D.Writes data to a pipe

Answer You use the SocketChannel subclass to connect sockets for communication. You use the FileChannel subclass to manipulate a file by reading and writing to it. The Pipe.SourceChannel subclass reads data from a pipe, whereas the Pipe.SinkChannel subclass writes data to a pipe. You use the FileChannel subclass in a similar way to the RandomAccessFile, except that the FileChannel class works with ByteBuffer. The Pipe.SinkChannel subclass implements the WritableByteChannel and GatheringByteChannel interfaces. The Pipe.SourceChannel subclass implements the ReadableByteChannel and ScatteringByteChannel interfaces. The SocketChannel subclass implements ByteChannel, ScatteringByteChannel, and GatheringByteChannel interfaces.

4. Using the FileChannel class There are three main reasons why you would want to use a FileChannel instead of a java.io file stream:



file locking



memory mapping



optimizing file transfer

file locking You use file locking when you want to prevent the possibility of simultaneous write or read access to a file. There are two types of locks - a shared lock and an exclusive lock. An exclusive lock prevents others placing a lock on a file while you have it locked. A shared lock allows several other shared locks to be placed on the file, but does not permit exclusive locks. memory mapping You use memory mapping when you want a file to be available through a single ByteBuffer object. The file is mapped into memory, which saves system resources when users attempt to access the file. optimizing file transfer Two FileChannel methods enable you to improve the performance of data transfer between two channels. The transferTo and transferFrom methods can be more efficient than a loop, which copies and reads bytes to the target channel. Consider the code that allows you to open a FileOutputStream in append mode. public class FileWriting { private FileChannel myReadWrite ; public FileWriting (String openFile) { try { // Opens a FileOutputStream in append mode myReadWrite = new FileOutputStream (openFile, true).getChannel() ; } catch (IOException e) { myReadWrite = null ; } } The FileChannel named myReadWrite is assigned to the FileOutputStream. The value true indicates that the file will be appended. You use getChannel to return the unique FileChannel associated with this FileOutputStream object. In append mode, the channel's position equals the file size.

Note The append mode is a file writing mode. The existing contents of a file are kept when that file is opened, and new contents are appended to the existing contents.

Suppose you now wish to set up a memory-mapped file. First you need to get the FileChannel, which is then stored in mappedFile. public void memoryMapAFile (String fileToMap ) { // Create a FileChannel and map a buffer to it try { MISSING CODE mappedFile = new RandomAccessFile (fileToMap, "rw").getChannel() ; You type FileChannel to call the method that returns a FileChannel. You have called the method that returns a FileChannel. public void memoryMapAFile (String fileToMap ) { // Create a FileChannel and map a buffer to it try { FileChannel mappedFile = new RandomAccessFile (fileToMap, "rw").getChannel() ; You use the map method to return a MappedByteBuffer. The MappedByteBuffer incorporates all the methods belonging to ByteBuffer with a few extras relating to mapping. The identifier READ_WRITE sets the FileChannel.Mapmode to allow read and write access. public void memoryMapAFile (String fileToMap ) { // Create a FileChannel and map a buffer to it try { FileChannel mappedFile = new RandomAccessFile (fileToMap, "rw").getChannel() ; MappedByteBuffer mappedBuff = mappedFile.map (FileChannel.MapMode.READ_WRITE, 0, mappedFile.size() ) ; } catch (IOException e) {} } Consider the code that creates a file copy in Java. public void bulkCopy (String fileIn, String fileOut ) { try { FileInputStream inFile = new FileInputStream (fileIn) ; FileOutputStream outFile = new FileOutputStream (fileOut, true) ; FileChannel in = inFile.getChannel () ; FileChannel out = outFile.getChannel () ;

in.transferTo (0, (int) in.size(), out ) ; in.close () ; out.close () ; } catch (IOException e) {} } You store references to the FileChannel objects that are associated with the input and output streams. You use the transferTo method to transfer the data from in to out. You pass the size of the FileChannel named in so that all the data from the beginning of the file to the end of the file can be copied. You can wrap a byte array in a ByteBuffer object and write its contents to a ByteBuffer. myBuff = ByteBuffer.wrap(myBytes) ; f.myReadWrite.write (myBuff) ; You use the force method when you want the changes made to a buffer's content to be written to the channel. // Print some diagnostics System.out.println ( "Bytes written: " + totalBytes ) ; System.out.println ( "File length now: " + f.myReadWrite.size () ) ; f.myReadWrite.force( true ) ; f.myReadWrite.close() ; You close the FileChannel with the close method.

Question Suppose you created a FileChannel subclass named fileNewFile. You want to open a FileOutputStream in append mode and retrieve its FileChannel. What code do you use to do this? Options:

1. fileNewFile = new FileOutputStream (openFile, false).getChannel() ;

2. fileNewFile = new FileOutputStream (openFile, true) ;

3. fileNewFile = new FileOutputStream (openFile, true).getChannel() ;

Answer

The code that creates a FileOutputStream and retrieves its FileChannel is fileNewFile = new FileOutputStream (openFile, true).getChannel() ; Option 1 is incorrect. You are required to open the FileOutputStream in append mode. To do this you need to pass the value true when constructing the FileOutputStream. Option 2 is incorrect. You need to use the getChannel() method in order to retrieve the FileChannel. Option 3 is correct. The unique FileChannel associated with the FileOutputStream is assigned to fileNewFile.

Question Suppose you want to transfer data from source to target, using a single FileChannel method invocation. Complete the code to do this.

public void copyTo (String fileIn, String fileOut ) { try { FileInputStream inFile = new FileInputStream (fileIn) ; FileOutputStream outFile = new FileOutputStream (fileOut, true) ; FileChannel source = inFile.getChannel () ; FileChannel target = outFile.getChannel () ; MISSING CODE (0, (int) source.size(), target ) ; source.close () ; target.close () ; } catch (IOException e) {} }

Answer To complete the code you type source.transferTo

Summary The java.nio package is an I/O package that complements the java.io package. NIO (New Input/Output) allows you to wholly and partially lock files, map files to memory, and read and write files using a common channel. Buffer is an abstract class in the java.nio package. Unlike arrays, Buffer stores its state internally, so you no longer need to manage the state externally, as you do for arrays. A buffer has various properties

including position, limit, and capacity. Channels are to the java.nio package what streams are to the java.io package. Channels hold various advantages over stream-based models. There are three reasons why you would want to choose a FileChannel over java.io streams - a FileChannel enables file locking, memory mapping, and optimizes file transfer.

Table of Contents

The Scanner class Learning objective

After completing this topic, you should be able to use a scanner to retrieve input from a specific source.

1. Features of the Scanner class J2SE 5.0 has a new Scanner class that enables you to easily read and parse strings and primitive types using regular expressions. Before the release of the scanner class, you had to write your own class to read and parse text from a file. The new Scanner class is part of the java.util package. A scanner reads formatted input from a specified source. You can specify that the Scanner reads input from a console, file, string, or any other source that implements the new Readable interface or ReadableByteChannel. You can use the Scanner class to retrieve specific information from a string. For example, you can retrieve the country in which a person lives from a string containing their address. And you can use the Scanner class to read a number from the keyboard and assign the value to a variable. You can also use the Scanner class in conjunction with the Formatter class to print data. The Scanner class simplifies the reading of formatted input data. It does this by using tokens that match with regular expressions. A token is a sequence of characters that you specify when creating a scanner, using appropriate delimiters. Delimiters are characters that are used to split a string into tokens. The default delimiter for tokens is whitespace. A regular expression is a string with some added pattern-matching logic performed on it. If you've ever searched for *.doc on your hard drive, for example, you've used a regular expression.

When performing regular expression matching, you can also test whether a string fits into a specific syntactic form - for example an email address. The regular expression that matches the token in the source code determines the way the data is parsed. The Scanner class has predefined methods for extracting expressions such as Strings, double, and int. You can also define your own expressions. You define your own expressions using the next method. The method takes either a Pattern object or a String and returns a String, which is the next token, if there is a match. The scanner process involves the following steps: •

the scanner locates a token using the specified delimiters



the scanner matches the token to a regular expression, if one was specified



the token is stored in the specified variable

Question Place the steps in the scanner process in the correct order.

Options Option

Description

A

Stores each token in the specified variable

B

Identify tokens

C

Match tokens to regular expressions

Answer

Correct ranking Option Description B

Identify tokens The scanner first identifies tokens in input using specified delimiters.

C

Match tokens to regular expressions Once it has identified tokens, the scanner matches these tokens to regular expressions, if any were specified.

A

Stores each token in the specified variable Once it has matched tokens to regular expressions, the scanner stores the token in the variable you have specified.

2. Creating a scanner To create a scanner instance, you invoke one of the Scanner class constructors to ensure the scanner is able to read input from the specified source. There are eight Scanner class constructors that you use to read input



input streams



formatted input streams



strings



files



formatted files



Readable objects



ReadableByteChannel interfaces



formatted ReadableByteChannel interfaces

input streams The input stream constructor creates a scanner that reads input from the stream specified by source. The syntax for the input stream constructor is Scanner(InputStream source) formatted input streams The formatted input stream constructor creates a scanner that reads input from the stream specified by the source parameter. The charsetName parameter specifies the encoding type used to convert the input stream to characters. The syntax for the formatted input stream constructor is Scanner(InputStream source, String charsetName) strings The string constructor creates a scanner that reads input from the string specified by the source parameter. The syntax for the string constructor is Scanner(String source) files The file constructor creates a scanner that reads input from the file specified in the source parameter. The syntax for the file constructor is Scanner(File source) formatted files The formatted file constructor creates a scanner that reads input from the file specified by the source parameter. The charsetName parameter specifies the encoding type used to convert the file contents to characters. The syntax for the formatted file constructor is Scanner(File source, String charsetName) Readable objects The Readable object constructor creates a scanner that reads input from the Readable object specified by the source parameter. The syntax for the Readable object constructor is

Scanner(Readable source) ReadableByteChannel interfaces The ReadableByteChannel constructor creates a scanner that reads input from the ReadableByteChannel interface specified in the source parameter. The syntax for the ReadableByteChannel constructor is Scanner(ReadableByteChannel source) formatted ReadableByteChannel interfaces The formatted ReadableByteChannel constructor creates a scanner that reads input from the ReadableByteChannel specified by the source parameter. The charsetName parameter specifies the encoding type used to convert the byte channel to characters. The syntax for the formatted ReadableByteChannel constructor is Scanner(ReadableByteChannel source, String charsetName) Suppose you want to create a scanner to read input from the console. You first declare the ScanConsole class. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); } } Then you create a new instance of the Scanner class - in - that invokes the input stream constructor. This causes the new scanner to initialize on System.in and scan the input stream from the console. Now suppose you want to read input from a file named Scanner.txt. To do this, you create a new instance of the Scanner class - file - that invokes the file constructor. And in the constructor, you specify the fileIn parameter to ensure that the scanner initializes on the file named Scanner.txt. import java.util.*; import java.io.*; public class ScanConsole { public static void main(String[] args) { FileReader fileIn = new FileReader( "Scanner.txt" );

Scanner file = new Scanner(fileIn); } }

Question You want to create the read instance of the Scanner class that reads input from a string defined as str. Which code do you use to do this? Options:

1. 2. 3. 4.

read Scanner = new Scanner(str); Scanner read = new Scanner(str); Scanner read = Scanner new(str); Scanner str = new Scanner(read);

Answer You instantiate the read instance of the Scanner class and initialize the Scanner on the str string, using the code Scanner read = new Scanner(str); Option 1 is incorrect. The new instance and the class are in the wrong order. You should instantiate the read instance of the Scanner class. Option 2 is correct. You instantiate the read instance of the Scanner class and initialize the new Scanner on the str string. Option 3 is incorrect. The Scanner class and the new keyword are in the wrong order. You should create a new instance of the Scanner class with a str parameter. Option 4 is incorrect. You need to instantiate a read instance rather than a str instance, and initialize the Scanner on the str string rather than on read.

3. Retrieving input To create a scanner to scan and read input, you •

determine whether there is pending input using the hasNext method



read the input using the next method



repeat this process until all input is read

To determine what type of input is specified, you call one of the hasNext methods. You specify the type of input you are searching for using the X element.

public boolean hasNextX()

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a complete list of the forms of the hasNext method. Three commonly used forms of the hasNext method search for boolean, integer, and string values. They are •

public boolean hasNextBoolean()



public boolean hasNextInt()



public boolean hasNext(String pattern)

public boolean hasNextBoolean() You use hasNextBoolean() to determine whether the next token in the input is a boolean value. The method returns true if there is a boolean value to read. public boolean hasNextInt() You use hasNextInt() to determine whether the next token in the input is an int value. The method returns true if there is an int value to read. public boolean hasNext(String pattern) You use the pattern argument to specify the string which the next token must match. The method returns true if there is a match. Once you determine what input type is specified, you use one of the next methods to read the input. public String nextX()

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a complete list of the forms of the hasNext method. Three commonly used forms of the next method scan for boolean, integer, and string values. They are •

public boolean nextBoolean()



public int nextInt()



public String next(String pattern)

public boolean nextBoolean() The nextBoolean method scans the next token of the input for a boolean value and returns that value. If the value cannot be converted into a valid boolean value, an InputMismatchException is thrown.

public int nextInt() The nextInt method scans the next token of the input for an int value. If the value cannot be converted into a valid int value, an InputMismatchException is thrown. public String next(String pattern) This method searches for a match for the specified string pattern and returns that token if there is a match. If the next token does not match pattern, a NoSuchElementException is thrown. Suppose you have created a scanner to read the System.in input stream from a console. You now want to write the code for scanning and reading the input. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); String str = " "; System.out.println ("Enter your first name and age, in the form name System.out.println("and press enter");

age ,");

// Scan the console for input, blocking if necessary if (in.hasNext()) str = "Your name is " + MISSING CODE; } } You create an if statement that calls the hasNext method to scan the input stream for the first delimiter. If there is data to be read, you want to call the next method of the in instance to read the input. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); String str = " "; System.out.println ("Enter your first name and age, in the form name System.out.println("and press enter"); // Scan the console for input, blocking if necessary if (in.hasNextInt()) str = "Your name is " + MISSING CODE;

age ,");

} } To do this, you type in.next(). The next method blocks the scanner while reading the input. During blocking, the method stops and waits for user input. When the user enters input, the formatted input is added to the str string. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); String str = " "; System.out.println ("Enter your first name and age, in the form name System.out.println("and press enter");

age ,");

// Scan the console for input, blocking if necessary if (in.hasNext()) str = "Your name is " + in.next(); } } You use the hasNextInt method to determine whether the next token is an integer value. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); String str = " "; System.out.println ("Enter your first name and age, in the form name System.out.println("and press enter");

age ,");

// Scan the console for input, blocking if necessary if (in.hasNext()) str = "Your name is " + in.next(); // Scan the console for specific input, blocking if necessary if (in.hasNextInt()) str += " and your age is " + in.nextInt();

} } If an integer value is found, you call the nextInt method to read the input and add the formatted input to a string. You use System.out.println to print the complete string onscreen. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); String str = " "; System.out.println ("Enter your first name and age, in the form name System.out.println("and press enter");

age ,");

// Scan the console for input, blocking if necessary if (in.hasNext()) str = "Your name is " + in.next(); // Scan the console for specific input, blocking if necessary if (in.hasNextInt()) str += " and your age is " + in.nextInt(); // Output the result System.out.println(str); } } When you compile and run the code, the output displays on screen.

Question Suppose you create the question instance of the Scanner class. You want to scan and read all boolean values from a specified input and add them to the answer string. You create an if statement to do this. Match each code segment to its correct location in the if statement. Options:

1. answer 2. hasNextBoolean() 3. nextBoolean()

Targets:

A.The code for MISSING CODE1 located after the word question. B.The code for MISSING CODE2 located before the += operator C.The code for MISSING CODE3 located after "The statement is " Answer After you declare the question instance, you type hasNextBoolean(). After you declare the method to scan the input for a boolean value, you type answer. And before you complete the if statement, you type nextBoolean(). The complete code is: if(question.hasNextBoolean()) answer += "The statement is " + nextBoolean(); You specify the answer string to add to the result of the nextBoolean method. You declare the hasNextBoolean method to scan the input for a boolean value. You declare the nextBoolean method to read the input and return a formatted boolean value.

Question Suppose you want to scan a specified file - File.txt - for the string "pat" using the findword instance of the Scanner class. Complete the code to do this.

public class ScanConsole { public static void main(String[] args) { FileReader fileIn = new FileReader( "File.txt" ); Scanner findword = new Scanner(fileIn); String str = " "; String pat = "the"; if(MISSING CODE) str = "The next word is " +findword.next(pat); } }

Answer

To call the findword instance's hasNext method and assign pat as the argument, you use the code findword.hasNext(pat)

4. Setting delimiters You can change the default delimiter that a scanner uses from whitespace to a character or pattern. To do this, you use the useDelimiter method. If a scanner must read input that is separated by commas, for example, you need to set the delimiter to a comma. There are two forms of the useDelimiter method - for string or pattern delimiters. The pattern element defines the regular expression that specifies the delimiter. Scanner useDelimiter(String pattern) Scanner useDelimiter(Pattern pattern) To determine the current delimiter set for a scanner, you use the delimiter method. Pattern delimiter() Suppose you want to change the delimiters used in the in instance of the Scanner class from the default whitespace to a semicolon followed by an optional number of space characters. You call the in instance's useDelimiter method, which takes a semi-colon, a space character, and an asterisk (* ) as arguments. The asterisk specifies that any number of characters preceding it may be contained in the delimiter. import java.util.*; public class ScanConsole { public static void main(String[] args) { // Declare and initialize a Scanner on System.in Scanner in = new Scanner(System.in); // Set delimiters to a semicolon, // followed by optional space character in.useDelimiter("; *"); } }

Question Suppose you want to change the delimiter that a scanner uses from the default whitespace to an optional number of spaces followed by a colon.

Which useDelimiter method does this? Options:

1. 2. 3. 4.

useDelimiter(": *"); useDelimiter(" *:"); useDelimiter(" *;"); useDelimiter(" :");

Answer To specify that the delimiter consists of an optional number of space characters followed by a colon, you use the code useDelimiter(" *:"); Option 1 is incorrect. The order of the arguments is wrong. This useDelimiter method specifies that any number of spaces may follow the colon. Option 2 is correct. You call the useDelimiter method with a space followed by an asterisk (*) and a colon as arguments. The asterisk specifies that any number of space characters may appear before the colon. Option 3 is incorrect. This useDelimiter method specifies that any number of space characters may precede a semi-colon. Option 4 is incorrect. This useDelimiter method specifies that the delimiter consists of one space followed by a colon. However, you require the scanner to accept any number of space characters.

Summary The new Scanner class simplifies reading and formatting of input data by matching tokens to regular expressions. Tokens are defined using delimiters. You can define regular expressions, or use predefined regular expressions. You can use the Scanner class to read input from the user console, a text file, or a String. There are eight Scanner class constructors you use to create scanners. You use one of the hasNext methods to determine whether there is specified input. These methods return true while the appropriate input is available. You then use one of the next methods to read the input. You use the useDelimiter method to change the delimiter from the default whitespace.

Table of Contents

Formatting output Learning objective

After completing this topic, you should be able to use the printf method and the Formatter class to format output for a given scenario.

1. The new printf method The Java 2 Standard Edition 5.0 kit now contains the printf convenience method. The printf method enables more control over the output format of its arguments than existing print methods.

Note A convenience method is an abridged version of a more complicated method and uses simplified signatures and default arguments. You use the printf method to create output to display onscreen. The printf method writes a string to an output string using a specified format string and arguments. The printf method is used with print streams, for example System.out and System.err. The System.out object - which belongs to the PrintStream class - now includes the printf method for formatting output strings. You can use the printf method with two object types: •

PrintStream



PrintWriter

PrintStream The PrintStream object writes a formatted string to bytes-oriented output. When you use the printf method of the PrintStream class, the invoking PrintStream is returned. PrintWriter The PrintWriter object writes a formatted string to character-oriented output. When you use a PrintWriter instance of the printf method, the invoking PrintWriter is returned. The PrintStream and PrintWriter objects share the same two forms of the printf method. The first form of the printf method of the PrintStream object defines the locale of a specified format string. public PrintStream printf(Locale l, String format, Object args) The second form of the printf method of the PrintStream object is similar to the first form of the printf method, but uses a default locale to format output. public PrintStream printf(String format, Object args) Here is the syntax of the printf method again: public PrintStream printf(Locale l, String format, Object args) This form of the printf method has three elements:



l



format



args

l You use the l element to specify the locale applied during formatting. If l is set to null, no localization is applied. format You use the format element to specify the format string used. args You use the args element to specify the arguments that are referenced by the format elements in the format string. If there are more arguments than format specifiers, the surplus arguments are ignored. To specify how outputted values are formatted, you use conversion types as arguments of the printf method. Two common conversion types are •

%n



%e or %E

%n The %n conversion type inserts a line break in the output. %e or %E The %e conversion type formats arguments of the printf method as floating-point decimal values using exponential notation. The arguments must be of type Double, BigDecimal, or Float. You can use format flags in conjunction with conversion types to specify how the formatted output is displayed onscreen. public PrintStream printf(Locale l, String format, Object args)

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a complete list of the conversion types and format flags of the printf method.

Question What are the functions of the printf method? Options: 1. To control the format of print stream output 2. To control the format of argument output

3. To create a formatted string for printing 4. To create output to display onscreen

Answer You use the printf method to control the format of argument output to a string that is displayed onscreen, and you use the printf method in conjunction with print streams. Option 1 is correct. You use the printf method in conjunction with print streams such as System.out and System.err. The PrintStream object writes a formatted string to bytes-oriented output. When you use the printf method of the PrintStream class, the invoking PrintStream is returned. Option 2 is correct. The printf method uses specified format strings and arguments to control how argument output is formatted. Option 3 is incorrect. You use methods of the Formatter class to create a formatted string for printing, not the printf method. Option 4 is correct. The printf method writes a string to an output string that can be displayed onscreen.

2. Using the printf method Suppose you want to display the date that output was printed onscreen using a default locale. You do this by calling the printf method of System.out. This writes the formatted string to a byteoriented PrintStream. import java.util.*; public class Printing { public static void main(String[] args) { Date datePrinted = Calendar.getInstance().getTime(); // Print a full date System.out.printf("Printed on the %tcMISSING CODE", datePrinted); } } You use the %t formatter conversion type to specify that the output is formatted as a date or time type. Then you use the %c formatter conversion type to specify that the output should have the form day month date hh:mm:ss timezone year. Now you want a line break to follow the formatted date and time. import java.util.*; public class Printing { public static void main(String[] args) { Date datePrinted = Calendar.getInstance().getTime();

// Print a full date System.out.printf("Printed on the %tcMISSING CODE", datePrinted); } } You type \n. The \n conversion type ensures that a line break is outputted. import java.util.*; public class Printing { public static void main(String[] args) { Date datePrinted = Calendar.getInstance().getTime(); // Print a full date System.out.printf("Printed on the %tc\n", datePrinted); } } Suppose you want to print the age of a person - for example Joe - onscreen. You use the %s formatter conversion type to format the output using String. This outputs the string "Joe's" onscreen. Then you use the %d formatter conversion type to format the byte output as a decimal value. import java.util.*; public class Printing { public static void main(String[] args) { Date datePrinted = Calendar.getInstance().getTime(); // Print a full date System.out.printf("Printed on the %tc\n", datePrinted); // Print a string and an integer System.out.printf("%s age is %d\n", "Joe's", 30); } } You can add further printf methods to the Printing class. For example, you can use the printf method to output a double or float value for weight. import java.util.*; public class Printing { public static void main(String[] args) { Date datePrinted = Calendar.getInstance().getTime();

// Print a full date System.out.printf("Printed on the %tc\n", datePrinted); // Print a string and an integer System.out.printf("%s age is %d\n", "Joe's", 30); // Print a double or float value System.out.printf("His weight is %3.2f lbs\n", 150.537); // Print a double using scientific notation System.out.printf("His height is %e inches\n", 180.28); } } And you can use the printf method to format a double value as a floating-point decimal value using exponential notation. This is also known as scientific notation. When you compile and run the code, the formatted output is displayed onscreen.

Question Suppose you want to display a person's height on screen. You need to format the argument output as a floatingpoint decimal value using exponential notation and follow the output with a line break. In the following exercise, you are required to match each code element of the printf method to its correct position in the following code: MISSING CODE1.printf("His height is MISSING CODE2 inches MISSING CODE3", 180.28); Options:

1. %e 2. \n 3. System.out Targets:

A.MISSING CODE1 B.MISSING CODE2 C.MISSING CODE3 Answer You use the printf method of the System.out object, specify the %e conversion type to format the output, and specify the \n conversion type to ensure a line break. You use the %e conversion type to format the argument output - the height - as a floating-point decimal value using exponential notation. The argument must be of type Double, BigDecimal, or Float.

You use the \n conversion type to ensure that the output is followed by a line break. You use System.out to write the formatted string to a byte-oriented PrintStream.

Question Suppose you are required to use the printf method to print the age of a pet - Max - onscreen. You want to format the output as a string. Complete the code to do this.

import java.util.*; public class Dog { public static void main(String[] args) { System.out.printf("MISSING CODE age is %d\n", "Max's", 30); } }

Answer You use the %s conversion type to print the age onscreen.

3. The Formatter class J2SE 5.0 includes the new Formatter class, which enables you to convert strings, numbers, and date and time information into almost any format. It also enables you to format common Java data types, including byte, Calendar, and BigDecimal. The Formatter class is part of the java.util package, which has methods that accommodate the varargs construct. The format method of the Formatter class is similar to the printf function used in C and C++. However, the Formatter class is stricter than printf. For example, Unlike printf, the Formatter class highlights an error if it recognizes an invalid flag. In this example, the format and the printf methods have a similar function. import java.util.*; public class MyFormatterA { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder();

// Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format a character, string, and decimal formatter.format("%c. You are %s %d\n", 'A', "visitor", numVisitor); //format() does the same job as printf System.out.printf("You are %s %d\n", "visitor", numVisitor);

System.out.println(formatter); } } The Formatter class provides the output "A. You are visitor 100", whereas the printf class provides the output "You are visitor 100". The Formatter class converts binary input data into formatted text, which it stores in a buffer. You retrieve the contents of the buffer by invoking the out method. You can also convert the buffer contents into a string by invoking the toString method. There are three commonly used constructors for the Formatter class. •

Formatter()



Formatter(Appendable bfr)



Formatter(String filename) throws FileNotFoundException

Formatter() The Formatter() syntax creates a general Formatter object that uses a StringBuilder object to store formatted output in the default location specified by the Java Virtual Machine (JVM). An example of code that uses this syntax is Formatter frmat = new Formatter(); Formatter(Appendable bfr) This syntax creates a Formatter object that stores formatted output in a buffer - bfr - that you specify. If the bfr value is null, the Formatter object writes formatted output to a StringBuilder object. An example of code that uses this syntax is StringBuilder buffer = new StringBuilder(); Formatter frmat = new Formatter(buffer); Formatter(String filename) throws FileNotFoundException

This syntax creates a Formatter object that writes formatted output to a specified file. If it can't create the specified file, it generates a FileNotFoundException error. If the file already exists, it overwrites its content. The Formatter object uses the default character set, and resides at the default location, that the Java Virtual Machine (JVM) specifies. An example of code that uses this syntax to write formatted output to a file named test.txt is Formatter frmat = new Formatter("test.txt"); In addition to specifying a buffer or file for Formatter output, you can use specific constructors to specify •

the location of a Formatter object



the character set it uses



the print or output stream it formats

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a list of Formatter constructors.

Question Suppose you want to create a Formatter object that writes output to a buffer named test, instead of to the default StringBuilder. Identify the code that does this. Options:

1. StringBuilder buffer = new StringBuilder(); Formatter frmat = new Formatter(Appendable test);

2. StringBuilder buffer = new StringBuilder(); Formatter test = new Formatter();

3. StringBuilder test = new StringBuilder(); Formatter frmat = new Formatter(Appendable test);

Answer To create a Formatter object that writes output to a specified buffer named test, you use the code StringBuilder test = new StringBuilder(); Formatter frmat = new Formatter(Appendable test);

Option 1 is incorrect. This code creates a buffer named buffer, but it already references a buffer called test in the Formatter constructor. Option 2 is incorrect. This code generates a general Formatter object named test. The test identifier should be the name of the buffer, rather than a reference to the Formatter object. Option 3 is correct. This code creates a buffer called test and defines a Formatter object that writes output to this buffer.

4. Formatter methods and specifiers In addition to different constructors, the Formatter class defines several methods.

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a complete list of Formatter methods. You use the format method to format arguments according to certain specifiers. The syntax for the commonly used format method contains a String parameter and an Object parameter. The String parameter typically contains a combination of text and format specifiers. Format specifiers determine how arguments display. You define them using a percent sign (%), followed by a single-character conversion specifier. For every format specifier, there must be a corresponding argument in the method's argument list. Formatter format(String fmatString, Object ... args) Common format specifiers include •

%c or %C



%s or %S



%d



%f



%e or %E



%g or %G



%a or %A

%c or %C

The %c format specifier returns a Unicode character. It can convert arguments of type char, byte, int, and short, as well as the generic wrapper types Character, Byte, Integer, and Short. The %C format specifier denotes that the result will be in uppercase. %s or %S The %s format specifier returns a string. It can convert arguments of any type. You use the %S format specifier to format the string in uppercase. %d The %d format specifier returns a decimal integer. It can convert arguments of type byte, short, int, long, and BigInteger, as well as the generic wrapper types Byte, Short, Integer, and Long. %f The %f format specifier returns a decimal floating point number. It can convert arguments of type float, double, and BigDecimal, and the generic wrappers Float and Double. %e or %E The %e format specifier returns a decimal number in scientific notation. It can convert arguments of type float, double, and BigDecimal, and the generic wrappers Float and Double. You use %E to display the result in uppercase. %g or %G The %g format specifier returns a number in scientific notation or as a decimal floating point. It can convert arguments of any numeric type, including int and float. The %G specifier denotes that the result must display in uppercase. %a or %A The %a format specifier returns a hexadecimal floating point number with a significant and an exponent. It can convert arguments of type float, double, and BigDecimal, and the generic wrappers Float and Double. You use %A to display the result in uppercase. You can combine format specifiers to provide detailed formatting instructions. In addition to other common format specifiers, the format method supports %n, %t or %T, %%, %o, %h, and %b format.

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View a complete list of Java format specifiers.

In the example, you specify the code to output the decimal number 9.837 in scientific notation. To ensure that the output displays on a new line, you use \n. You also need to enter the appropriate format specifier to perform the conversion. import java.util.*; public class MyFormatter { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format a character, string, and decimal formatter.format("%c. You are %s %d\n", 'A', "visitor", numVisitor); // Format a floating point in scientific notation formatter.format("The scientific notation for %f is MISSING CODE\n", d1, d1); System.out.println(formatter); } } You type %e to complete the conversion. You have now written the code to complete the conversion to scientific notation and you can now compile and run the code. import java.util.*; public class MyFormatter { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format a character, string, and decimal formatter.format("%c. You are %s %d\n", 'A', "visitor", numVisitor); // Format a floating point in scientific notation formatter.format("The scientific notation for %f

is %e\n", d1, d1); System.out.println(formatter); } } When you run the MyFormatter class, the formatted output is displayed onscreen. To output data in hexadecimal or octal format, you use the %a or the %o format specifiers, respectively. import java.util.*; public class MyFormatterB { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format numeric data in octal formatter.format("The octal format for %d is %o\n", numVisitor, numVisitor); // Format numeric data in hexadecimal formatter.format("The hex format for %f is %a\n", d1, d1); System.out.println(formatter); } } You can perform a hexadecimal conversion only on floating point numeric types such as float, BigDecimal, and double. You can apply an octal conversion only to integer types, such as short, int, and BigInteger. Running the code displays the number 100 in its octal form, and the floating point number in its hexadecimal form.

Note When formatting data to be output in hexadecimal format, the exponent is represented by p followed by a decimal string of the exponent. Suppose you want to output a character, a string, and a decimal on the same line. On the next line, you want to output a string that includes a conversion of a decimal number to a number in scientific notation.

import java.util.*; public class MyFormatter { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format a character, string, and decimal formatter.format("%c. You are %s %d\n", 'A', "visitor", numVisitor); To do this, you first need to create an instance of the Formatter class. Next, you use the format method to display the character A, the string You are visitor, and the number 100. You use the format specifiers %c, %s, and %d to do this. Consider the code that converts the number 100 to its octal version - 144. For each format specifier, there must be a corresponding argument. In this case, the argument - which is referenced twice - is the variable numVisitor. import java.util.*; public class MyFormatterB { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format numeric data in octal formatter.format("The octal format for %d is %o\n", numVisitor, numVisitor); // Format numeric data in hexadecimal formatter.format("The hex format for %f is %a\n", d1, d1); System.out.println(formatter); } }

To make the code more concise, you can use an argument index - n$, where n is the index of the relevant argument - immediately after the percent sign. Suppose you use the argument index in code that performs an octal conversion. You can reference it only once in the argument list to make the code more concise. import java.util.*; public class MyFormatterArg { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format numeric data in octal - with argument index formatter.format("The octal format for %1$d is %1$o\n", numVisitor); // Format numeric data in hexadecimal - with argument index formatter.format("The hex format for %1$f is %1$a\n", d1); System.out.println(formatter); } } You can use the out method to output the contents of a Formatter buffer. import java.util.*; public class MyFormatterArg { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format numeric data in octal - with argument index formatter.format("The octal format for %1$d is %1$o\n", numVisitor); // Format numeric data in hexadecimal - with argument index formatter.format("The hex format for %1$f is %1$a\n", d1); System.out.println(formatter);

} } Instead of using the out method, you can use the toString method to output the content of a Formatter buffer. import java.util.*; public class MyFormatterArg { public static void main(String[] args) { int numVisitor = 100; double d1 = 9.837; StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Format numeric data in octal - with argument index formatter.format("The octal format for %1$d is %1$o\n", numVisitor); // Format numeric data in hexadecimal - with argument index formatter.format("The hex format for %1$f is %1$a\n", d1); System.out.println(buffer.toString()); } }

Question Suppose you want to convert the decimal number 500 into its octal form. Identify the code that you can use to do this. Options:

1. formatter.format("The octal format for 500 is %o%n", 500); 2. formatter.format("The octal format for %d is %o\n", 500, 500); 3. formatter.format("The octal format for %f is %o\n", 500, 500); Answer You can convert the decimal number 500 into its octal form in output by using either of the following code lines: formatter.format("The octal format for 500 is %o%n", 500); formatter.format("The octal format for %d is %o\n", 500, 500);

Option 1 is correct. This code uses the %o format specifier to convert the decimal number 500 to the octal number 764. Option 2 is correct. This code uses two format specifiers - %d and %o - and the same number of arguments to produce the correct output. Option 3 is incorrect. This code compiles, but will generate a runtime error because you can convert only integer type numbers, such as short, int, and long, into octal numbers.

5. Formatting date and time information You use the %t or %T format specifier to output date and time information. The date and time specifier accepts arguments of type Calendar, Date, Long, or long. To represent date and time information in different ways, the %t or %T format specifiers use suffix characters. Commonly used suffixes include •

d



r



c



m



M



b



B



I

d The d suffix displays the current day of the month as two digits - 01 or 31, for example. r The r suffix displays time in the 12-hour format - hh:mm:ss - AM or PM. c The c suffix displays the standard date and time in the format hh:mm:ss time-zone year m The m suffix displays the current month as two digits - 01 to 12. M The uppercase M suffix displays the current minute as two digits - 00 to 59. b The b suffix displays the locale-specific, abbreviated month name - Jan or Dec, for example. B The uppercase B suffix displays the full locale-specific month name - for example January or December.

I The uppercase I suffix displays the current hour as two digits - 01 or 12, for example. The %t and %T format specifiers support a large number of additional date and time suffixes.

Supplement Selecting the link title opens the resource in a new browser window.

Launch window View the complete list of suffixes for the %t and %T format specifiers. Consider code that uses the %t format specifier with different suffixes to format date and time information in a variety of ways. import java.util.*; public class MyDateFormatter { public static void main(String[] args) { Date date = new Date(); Calendar cal = Calendar.getInstance(); StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Display 12-hour time format formatter.format("%tr\n", date); // Display complete date/time information formatter.format("%tc\n", cal); // Display hours/minutes only formatter.format("%1$tl:%1$tM\n", cal); // Display month name formatter.format("%1$tB %1$tb %1$tm\n", cal); System.out.println(formatter); } } The code first creates objects of type Date and Calendar. import java.util.*; public class MyDateFormatter { public static void main(String[] args) {

Date date = new Date(); Calendar cal = Calendar.getInstance(); StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Display 12-hour time format formatter.format("%tr\n", date); // Display complete date/time information formatter.format("%tc\n", cal); // Display hours/minutes only formatter.format("%1$tl:%1$tM\n", cal); // Display month name formatter.format("%1$tB %1$tb %1$tm\n", cal); System.out.println(formatter); } } It then creates a Formatter object that uses a buffer, with the US locale specified. Next, the code uses the r suffix with the %t format specifier to display the time in a 12-hour format. import java.util.*; public class MyDateFormatter { public static void main(String[] args) { Date date = new Date(); Calendar cal = Calendar.getInstance(); StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); // Display 12-hour time format formatter.format("%tr\n", date); // Display complete date/time information formatter.format("%tc\n", cal); // Display hours/minutes only formatter.format("%1$tl:%1$tM\n", cal); // Display month name formatter.format("%1$tB %1$tb %1$tm\n", cal);

System.out.println(formatter); } } It uses the c suffix to display the full date, the l suffix to display the current hour, and the M suffix to display the current minute. Finally, the code uses the B, b, and m suffixes to display the full name of the month, its abbreviation, and the month number. Now consider the output produced by running the MyDateFormatter code.

Question Assume that date and cal are objects of type Date and Calendar respectively. Suppose you want to format output to display the date and time information "5:10 Jan 30". Which lines of code will return the required output? Options:

1. 2. 3. 4.

formatter.format("%1$tl:%1$tM %1$tb %2$td.", cal, date); formatter.format("%tl:%1$tM %2$tb %3$td", cal, date); formatter.format("%tl:%tM %2$tb %1$td.", cal, date); formatter.format("%tl:tM %2$tb %1$td.", cal, date);

Answer To generate the output "5:10 Jan 30", you can use either of the following lines of code: formatter.format("%1$tl:%1$tM %1$tb %2$td.", cal, date); formatter.format("%tl:tM %2$tb %1$td.", cal, date); Option 1 is correct. This code uses the correct date and time suffixes in the correct sequence with the %t format specifier. The I suffix displays the hour as two digits, M displays the minutes as two digits, b displays the abbreviated month name, and d displays the day of the month. Option 2 is incorrect. This code will generate a runtime error because the 3$ argument index refers to a third argument that doesn't exist. Option 3 is correct. This code uses the correct date and time suffixes with the %t format specifier, and references the correct number of arguments from left to right. It also uses the correct argument index for the b and d suffixes, to enable more concise coding. Option 4 is incorrect. The output will be - 5:tM Jan 30. Each format specifier must be preceded by a percentage sign, otherwise it is interpreted as plain text.

6. Formatting numbers To control how numeric output displays, you can specify the width and precision of numbers, and use format flags with format specifiers. To format a table of numbers in which the columns line up, you can specify a minimum field width. You do this by specifying an integer between the percent symbol (%) and the conversion specifier in a format specifier. formatter.format("%15s", myNumber); To set a precision specifier for floating point data and strings, you enter a period followed by an integer between the % symbol and the conversion specifier in a format specifier. A precision specifier typically follows a minimum field width specifier. formatter.format("%10.6f", myNumber); The precision you specify for a string determines its maximum field length in characters. For example, %15s displays a string with a maximum width of 15 characters. If the string is longer than the maximum length, the characters are truncated. formatter.format("%15s", myNumber); Specifying precision for floating point numbers - %f, %e, or %g - specifies the number of decimal places to include in the numbers. For example, %10.6f displays a number of 10 characters with 6 decimal places - a precision of 4 (or 10 minus 6). formatter.format("%10.6f", myNumber); Suppose you want to output three columns of numbers in the following sequence - the number, its square, and then its cubed value. Each number must be a minimum of 15 characters wide. The square values must have a precision of 10. In the for loop that generates the columns, you need to enter the appropriate precision specifier. import java.util.*; public class MyFlagFormatter { public static void main(String[] args) { StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); buffer.setLength(0); printNums(formatter);

} static void printNums(Formatter formatter) { double d = -2.2834; for (short i=1; i <= 8; i++, d += d+i) { formatter.format("%15f %MISSING CODEf d, d*d, d*d*d); }

%15.2f%n",

System.out.println(formatter); } } You type 15.5 in the format specifier that determines the output for the square values column. You've now completed the code. import java.util.*; public class MyFlagFormatter { public static void main(String[] args) { StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); buffer.setLength(0); printNums(formatter); } static void printNums(Formatter formatter) { double d = -2.2834; for (short i=1; i <= 8; i++, d += d+i) { formatter.format("%15f %15.5f %15.2f%n", d, d*d, d*d*d); } System.out.println(formatter); } } So you compile it in the command window, using this command. javac MyFlagFormatter.java The output of the command includes the three required columns of numbers. C:\code>java MyFlagFormatter -2.283400 5.21392

-11.91

-2.566800 -2.133600 -0.267200 4.465600 14.931200 36.862400 81.724800

6.58846 4.55225 0.07140 19.94158 222.94073 1358.83653 6678.94294

-16.91 -9.71 -0.02 89.05 3328.77 50089.98 545835.28

C:\code> You can further control the format of output by using the following format flags after the percent symbol (%) in a format specification: •

0



+



(



space



-



,



#

0 The 0 flag pads the output with zeros instead of spaces. It works with all format specifiers, except %n. In the code example, the flag produces the output 00045. The code to output the string 00045 is formatter.format("%05d", 45); + The plus sign (+) flag outputs a plus sign in front of positive numbers. In the example, the flag produces the output +50. The code to output the string +50 is formatter.format("%+d", 50); ( The left parenthesis - ( - flag displays negative numbers between parentheses. The output created by the code example is (-50). The code to output the string (-50) is formatter.format ("%(d", -50); space The space flag outputs a space in front of positive numbers. You use this flag if you want negative and positive numbers to line up in a column. For example, the code sample outputs 50 and -50 aligned in a single

column. The code to output 50 and -50 aligned in a single column is formatter.format("% d", 50); formatter.format("% d", -50); The minus sign (-) flag forces left justification of numeric output. In the example, the flag produces this output: | 12.34| |12.34 | The code used to left justify the numeric output is formatter.format("|%8.2f|",12.34); formatter.format("|%-8.2f|", 12.34); , The comma (,) flag adds group separators to numeric data. For example, consider the code that produces the output 12,345.67. The code to output the string 12,345.67 is formatter.format("%,.2f", 12345.67); # You can use the number sign (#) flag with the %o, %f, %e, or %x format specifiers. With %o, the # flag prints the number with a leading zero. With %x, the # flag prints the number with an 0x prefix. With %f and %e, the flag forces a decimal point even if there is no decimal digit in a number. In the example, the code produces the output 0x1a. The code to output the string 0x1a is int myVal = 0x1a; fmt.format( "%#x", myVal ); System.out.println( myVal ); Suppose you want to modify code you've constructed so that - group separators and plus signs display in front of positive numbers - negative numbers display between parentheses and align to the left in columns import java.util.*; public class MyFlagFormatter { public static void main(String[] args) { StringBuilder buffer = new StringBuilder();

// Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); buffer.setLength(0); printNums(formatter); } static void printNums(Formatter formatter) { double d = -2.2834; for (short i=1; i <= 8; i++, d += d+i) { formatter.format("%15f %15.5f %15.2f%n", d, d*d, d*d*d); } System.out.println(formatter); } } To do this, you need to change only one line of code. To reformat the output as required, you add the minus sign, parenthesis, plus sign, and comma flags to the format specifiers, and then recompile the MyFlagFormatter class. import java.util.*; public class MyFlagFormatter { public static void main(String[] args) { StringBuilder buffer = new StringBuilder(); // Construct a Formatter object, passing in the US locale Formatter formatter = new Formatter(buffer, Locale.US); buffer.setLength(0); printNums(formatter); } static void printNums(Formatter formatter) { double d = -2.2834; for (short i=1; i <= 8; i++, d += d+i) { formatter.format("%,-(+15f %,-(+15.5f %,-(+15.2f%n", d, d*d, d*d*d); } System.out.println(formatter); } }

Note

The sequence in which you add the format flags is irrelevant. You run the MyFlagFormatter class in the command window. The output displays, with positive numbers preceded by plus signs and group separators, and negative numbers in parentheses and aligned to the left.

C:\code>java MyFlagFormatter (2.283400) +5.21392 (11.91) (2.566800) +6.58846 (16.91) (2.133600) +4.55225 (9.71) (0.267200) +0.07140 (0.02) +4.465600 +19.94158 +89.05 +14.931200 +222.94073 +3,328.77 +36.862400 +1,358.83653 +50,089.98 +81.724800 +6,678.94294 +545,835.28

C:\code>

Question Suppose you want to display this output, followed by a line break: |00098,765.432| Identify the line of code that will produce this output. Options:

1. 2. 3. 4.

formatter.format("|%0,13.3f%n|", 98765.432); formatter.format("|%0,13.3f|%n", 98765.432); formatter.format("%|0,13.3f|%n", 98765.432); formatter.format("|%0,13.3f|%n, 98765.432");

Answer To generate the output |00098,765.432| followed by a line break, you use the code formatter.format("|%0,13.3f|%n", 98765.432); Option 1 is incorrect. The second pipe (|) should appear before the newline format specifier. Option 2 is correct. This code produces the required output. The comma flag inserts group separators, the 0 flag fills the output with zeros instead of spaces, and the %n conversion type outputs a line break. Option 3 is incorrect. The pipe (|) character is not a formatting flag, but a normal text character. It cannot follow the percent sign.

Option 4 is incorrect. This line of code doesn't include an argument list. As a result, the compiler will treat everything inside the quote marks as string elements.

Summary The PrintStream and PrintWriter classes of the Java 2 Standard Edition 5.0 kit now contain the printf convenience method. The printf method writes a format string to be displayed onscreen. You use the arguments of the printf method to control how outputted values are formatted and printed. In J2SE 5.0, you use the Formatter class to convert binary data into formatted text. The class consists of a large number of constructors. The Formatter class manipulates output mostly via the format method, which takes two parameters - one of type String and the other of type Object. The Object parameter can accept variable-length arguments. The String parameter accepts various format specifiers that convert data into required formats. You use the Formatter class with the %t or %T format specifiers to format date and time information. These specifiers use suffixes to represent date and time information in various ways. You can specify the maximum width and precision of numbers, and use format flags with format specifiers, to determine how numeric output displays.

Related Documents

Jaa Io (7)
November 2019 4
7 - Serial Io
November 2019 11
Io
April 2020 29
Io
November 2019 42
Io
November 2019 42
Io
May 2020 27