700 likes | 854 Views
JAVA Programcılığı 4.2.1. Ali R+ SARAL. Ders Planı 4.2.1. Chapter 10: Writing Files File I/O Basics File Input and Output Channels Buffers Writing to a File. File I/O Basics.
E N D
JAVA Programcılığı 4.2.1 Ali R+ SARAL
Ders Planı 4.2.1 • Chapter 10: Writing Files • File I/O Basics • File Input and Output • Channels • Buffers • Writing to a File
File I/O Basics • Once you have written data to a file, what you have is just a linear sequence of bytes. The bytes in a file are referenced by their offset from the beginning, so the first byte is byte 0, the next byte is byte 1, the third byte is byte 2, and so on through to the end of the file. • If there are n bytes in a file, the last byte will be at offset n-1. • There is no specific information in the file about how the data originated or what it represents unless you explicitly put it there. • You can access an existing file to read it or write it in two different ways, described as sequential accessor random access. The latter is sometimes referred to as direct access.
File Input and Output • You saw a file stream object that encapsulates the physical file that you are working with. • You saw how to create FileOutputStream objects at the end of the previous chapter, and you use these for files to which you want to write. • In the next chapter, you will be using FileInputStream objects for files that you want to read. • One or more buffer objects in which you put the data to be written to a file, or from which you get the data that has been read. You’ll learn about buffer objects in the next section. • A channel object that provides the connection to the file and does the reading or writing of the data using one or more buffer objects. You’ll see how to obtain a channel from a file stream object later in this chapter.
File Input and Output • The process for writing and reading files is basically quite simple. To write to a file, you load data into one or more buffers that you have created • and then call a method for the channel object to write the data to the file that is encapsulated by the file stream. • To read from a file, you call a method for the channel object to read data from the file into one or more buffers, and then retrieve the data from the buffers. • You will be using four classes defined in the java.io package when you are working with files. The FileInputStream and FileOutputStream classes define objects that provide access to a file for reading or writing, respectively. • You use an object of type RandomAccessFile when you want to access a file randomly, or when you want to use a single channel to both read from and write to a file.
Channels • Channels were introduced in the 1.4 release of Java to provide a faster capability for input and output operations with files, network sockets, and piped I/O operations between programs than the methods provided by the stream classes. • …The channel mechanism can take advantage of buffering and other capabilities of the underlying operating system • and therefore is considerably more efficient than using the operations provided directly within the file stream classes.
Channels • A File object encapsulates a path to a file or a directory, and such an object encapsulating a file path can be used to construct a file stream object. • A FileInputStream object encapsulates a file that can be read by a channel. A FileOutputstream object encapsulates a file that can be written by a channel. • A RandomAccessFile object can encapsulate a file that can be both read from and written to by a channel. • A buffer just holds data in memory. You load the data that you want to write to a file into a buffer using the buffer’s put() methods. You use a buffer’s get() methods to retrieve data that has been read from a file. • You obtain a FileChannel object from a file stream object or a RandomAccessFile object. • You use a FileChannel object to read and/or write a file using the read() and write() methods for the FileChannel object, with a buffer or buffers as the source or destination of the data.
Channel Operations • A series of channel interfaces exists, each of which declares a set of one or more related operations that a channel may perform. • They all extend a common interface, Channel, which declares two methods: • The close() method, which closes a channel • The isOpen() method, which tests the state of the channel, returning true if it is open and • false otherwise
Channel Operations • Closeablevoid close() Closes the source or destination and releases any resources associated with it. • Channel void close() Closes the channel • boolean isOpen() Returns true if the channel is open and false otherwise. • ReadableByteChannel int read(ByteBuffer input) Reads bytes from a channel into the buffer specified by the argument and returns the number of bytes read, or -1 if the end of the stream is reached. • WritableByteChannel int write(ByteBuffer output) Writes bytes from the buffer specified by the argument to the channel and returns the number of bytes written. • ByteChannel This interface just inherits methods from the ReadableByteChannel and WritableByteChannel interfaces. No additional methods are declared. • ScatteringByteChannel int read(ByteBuffer[] inputs) Reads bytes from the channel into the array of buffers specified by the argument and returns the number of bytes read or -1 if the end of the stream is reached. • GatheringByteChannel int write(ByteBuffer[] outputs) Writes bytes from the array of buffers specified by the argument to the channel, and returns the number of bytes written.
File Channels • A FileChannel object defines a channel for a physical file and provides an efficient mechanism for reading, writing, and manipulating the file. File aFile = new File(“C:/Beg Java Stuff/myFile.text”); FileOutputStream outputFile = null; // Place to store an output stream reference try { // Create the stream opened to write outputFile = new FileOutputStream(aFile); } catch (FileNotFoundException e) { e.printStackTrace(System.err); System.exit(1); } // Get the channel for the file FileChannel outputChannel = outputFile.getChannel();
Buffers • All the classes that define buffers have the abstract Buffer class as a base. The Buffer class therefore defines the fundamental characteristics common to all buffers. • ByteBuffer A buffer that stores values of type byte. • CharBuffer A buffer that stores only values of type char • ShortBuffer A buffer that stores only values of type short • IntBuffer A buffer that stores only values of type int • LongBuffer A buffer that stores only values of type long • FloatBuffer A buffer that stores only values of type float • DoubleBuffer A buffer that stores only values of type double
Buffer Capacity • Each type of buffer stores elements of a specific kind— • a ByteBuffer object holds bytes, • a LongBuffer object holds integers of type long, and so on for the other buffer types. • The capacity of a buffer is the maximum number of values it can contain, not the number of bytes—
Buffer Position and Limit • A buffer also has a limitand a position, both of which affect data transfer operations to or from the buffer. • In the case of a ByteBuffer, the position and limit control read and write operations executed by a channel using the buffer.
Setting the Position and Limit position(int newPosition) limit(int newLimit) buf.limit(512).position(256); buf.position(0).limit(newLimit).position(newPosition); if(newPosition >= 0 && newLimit > newPosition) { buf.position(0).limit(newLimit).position(newPosition); } else { System.out.printn(“Illegal position:limit settings.” + “Position: “ + newPosition + “ Limit: “+ newLimit); } if (buf.hasRemaining()) { // If limit-position is >0 System.out.println(“We have space in the buffer!”); }
Creating Buffers ByteBuffer buf = ByteBuffer.allocate(1024); // Buffer of 1024 bytes capacity // Buffer stores 100 float values FloatBuffer floatBuf = FloatBuffer.allocate(100);
View Buffers • A view buffer therefore has two primary uses: • for loading data items that are not of type byte into a ByteBuffer prior to writing it to a file, • and accessing data that has been read from a file as values that are other than type byte. ByteBuffer buf = ByteBuffer.allocate(1024); // Buffer of 1024 bytes capacity IntBuffer intBuf = buf.asIntBuffer(); // Now create a view buffer
View Buffers • The content of the view buffer, intBuf, that you create here will start at the byte buffer’s current position, which in this case is zero since it is newly created. • The remaining bytes in buf will effectively be shared with the view buffer. • At least, the maximum number of them that is a multiple of 4 will be, since intBuf stores elements of type int that require 4 bytes each. • The view buffer will have an initial position of 0, and a capacity and limit of 256. This is because 256 elements of type int completely fill the 1024 bytes remaining in buf.
View Buffers • Method Description • asCharBuffer() Returns a reference to a view buffer of type CharBuffer • asShortBuffer() Returns a reference to a view buffer of type ShortBuffer • asIntBuffer() Returns a reference to a view buffer of type IntBuffer • asLongBuffer() Returns a reference to a view buffer of type LongBuffer • asFloatBuffer() Returns a reference to a view buffer of type FloatBuffer • asDoubleBuffer() Returns a reference to a view buffer of type DoubleBuffer • asReadOnlyBuffer() Returns a reference to a read-only view buffer of type ByteBuffer
Duplicating and Slicing Buffers • You can duplicate any of the buffers I have discussed by calling the duplicate() method for a buffer. • The method returns a reference to a buffer with the same type as the original, and which shares the contents and memory of the original buffer. • You can also slice any of the buffers you have seen. Calling the slice() method for a buffer • will return a reference to a new buffer object of the same type as the original that shares the elements that remain in the original buffer.
Creating Buffers by Wrapping Arrays • You can also create a buffer by wrapping an existing array of the same type as the buffer elements by • calling one of the static wrap() methods that are inherited from the Buffer class. This method creates a • buffer that already contains the data in the array. String saying = “Handsome is as handsome does.”; byte[] array = saying.getBytes(); // Get string as byte array ByteBuffer buf = ByteBuffer.wrap(array); ByteBuffer buf = ByteBuffer.wrap(saying.getBytes());
Creating Buffers by Wrapping Arrays String saying = “Handsome is as handsome does.”; byte[] array = saying.getBytes(); // Get string as byte array ByteBuffer buf = ByteBuffer.wrap(array, 9, 14); long[] numbers = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}; LongBuffer numBuf = LongBuffer.wrap(numbers); • for the buffer created by the previous code fragment, you could obtain a reference to the original array like this: long[] data = numBuf.array();
Creating Buffers by Wrapping Arrays if(numBuf.hasArray()) { long[] data = numBuf.array(); // Modify the data array directly to alter the buffer... } else { // Modify the buffer using put() methods for the buffer object... }
Wrapping Strings • You can create buffers of type CharBuffer by wrapping an object of any type that implements the CharSequence interface, • so this enables you to wrap objects of type String, type StringBuilder, and type StringBuffer. String wisdom = “Many a mickle makes a muckle.”; CharBuffer charBuf = CharBuffer.wrap(wisdom); String wisdom = “Many a mickle makes a muckle.”; CharBuffer charBuf = CharBuffer.wrap(wisdom.toCharArray()); String wisdom = “Many a mickle makes a muckle.”; CharBuffer charBuf = CharBuffer.wrap(new StringBuffer(wisdom));
Marking a Buffer • You use the mark property for a buffer to record a particular index position in the buffer that you want to be able to return to later. buf.mark(); // Mark the current position buf.limit(512).position(256).mark(); buf.reset(); // Reset position to last marked
Buffer Data Transfers • A relative put or get operation transfers one or more values starting at the buffer’s current position. • In this case the position is automatically incremented by the number of values transferred. • In an absolute put or get operation, you explicitly specify an index for the position in the buffer where the data transfer is to begin.
Transferring Data into a Buffer • Method Description • put(byte b) Transfers the byte specified by the argument to the buffer at the current position and increments the position by 1. An exception of type BufferOverflowException will be thrown if the buffer’s position is not less than its limit. • put(int index, byte b) Transfers the byte specified by the second argument to the buffer at the index position specified by the first argument. The buffer position is unchanged. An exception of type IndexOutOfBoundsException will be thrown … • put(byte[] array) Transfers all the elements of array to this buffer starting at the current position. The position will be incremented by the length of the array. An exception of type BufferOverflow Exception will be thrown if … • put(byte[] array, int offset, int length) Transfers bytes from array[offset] to array[offset+length-1] inclusive to the buffer. If there is insufficient space for them in the buffer, an exception of type BufferOverflowException will be thrown. • put(ByteBuffer src) Transfers the bytes remaining in src to the buffer. This will be src.remaining() elements from the buffer src from its position index to limit-1.
Transferring Data into a Buffer putDouble(double value) putDouble(int index, double value) String text = “Value of e”; ByteBuffer buf = ByteBuffer.allocate(text.length()+ sizeof(Math.E)); buf.put(text.getBytes()).putDouble(Math.E);
Transferring Data into a Buffer char[] array = text.toCharArray(); // Create char[] array from the string ByteBuffer buf = ByteBuffer.allocate(50); // Buffer of 50 bytes capacity // Now use a loop to transfer array elements one at a time for (char ch; array) { buf.putChar(ch); } buf.putDouble(Math.E); // Transfer the binary double value
Using View Buffers String text = “Value of e”; ByteBuffer buf = ByteBuffer.allocate(50); // The original byte buffer CharBuffer charBuf = buf.asCharBuffer(); // Create view buffer charBuf.put(text); // Transfer string via view buffer // Update byte buffer position by the number of bytes we have transferred buf.position(buf.position() + 2*charBuf.position()); buf.putDouble(Math.E); // Transfer binary double value
Preparing a Buffer for Output to a File ByteBuffer buf = ByteBuffer.allocate(80); DoubleBuffer doubleBuf = buf.asDoubleBuffer(); double[] data = { 1.0, 1.414, 1.732, 2.0, 2.236, 2.449 }; doubleBuf.put(data); // Transfer the array elements to the buffer buf.Position(8*doubleBuf.Position()); // Limit to current position and position to 0 buf.limit(buf.position()).position(0); // Limit to current position and position to 0 buf.flip();
Writing to a File • A channel write() operation can throw any of five different exceptions: • NonWritableChannelException Thrown if the channel was not opened for writing. • ClosedChannelException Thrown if the channel is closed. Calling the close() method for the file channel will close the channel, as will calling the close() method for the file stream. • AsynchronousCloseException Thrown if another thread closes the channel while the write operation is in progress. • ClosedByInterruptException Thrown if another thread interrupts the current thread while the write operation is in progress. • IOException Thrown if some other I/O error occurs.
Writing to a File File aFile = new File(“C:/Beg Java Stuff/myFile.text”); FileOutputStream outputFile = null; // Place to store an output stream reference try { // Create the stream opened to write outputFile = new FileOutputStream(aFile); } catch (FileNotFoundException e) { e.printStackTrace(System.err); } // Get the channel for the file FileChannel outputChannel = outputFile.getChannel(); try { outputChannel.write(buf); // Write the buffer contents to the file } catch (IOException e) { e.printStackTrace(System.err); } try { outputChannel.force(); // Force data transfer to the file } catch (IOException e) { e.printStackTrace(System.err); }
File Position • write(ByteBuffer buf, long position) This writes the contents of the buffer, buf, to the file at the position specified by the second argument, and not the file position recorded by the channel. • Bytes from the buffer are written starting at the buffer’s current position, and buf.remaining() bytes will be written. This does not update the channel’s file position. • This method can throw any of the following exceptions: • IllegalArgumentException Thrown if you specify a negative value for the file position • NonWritableChannelException Thrown if the file was not opened for writing • ClosedChannelException Thrown if the channel is closed • AsynchronousCloseException Thrown if another thread closes the channel while the write operation is in progress • ClosedByInterruptException Thrown if another thread interrupts the current thread while the write operation is in progress • IOException Thrown if any other I/O error occurs
Try It Out Using a Channel to Write a String to a File import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class WriteAString { public static void main(String[] args) { String phrase = new String(“Garbage in, garbage out\n”); String dirname = “C:/Beg Java Stuff”; // Directory name String filename = “charData.txt”; // File name File dir = new File(dirname); // File object for directory // Now check out the directory if (!dir.exists()){ // If directory does not exist if (!dir.mkdir()){ // ...create it System.out.println(“Cannot create directory: “ + dirname); System.exit(1); } } else if (!dir.isDirectory()) { System.err.println(dirname + “ is not a directory”); System.exit(1); }
Try It Out Using a Channel to Write a String to a File // Create the filestream File aFile = new File(dir, filename); // File object for the file path FileOutputStream outputFile = null; // Place to store the stream reference try { outputFile = new FileOutputStream(aFile, true); System.out.println(“File stream created successfully.”); } catch (FileNotFoundException e) { e.printStackTrace(System.err); } // Create the file output stream channel and the buffer FileChannel outChannel = outputFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println(“New buffer: position = “ + buf.position() + “\tLimit = “ + buf.limit() + “\tcapacity = “ + buf.capacity()); // Load the data into the buffer for (char ch : phrase.toCharArray()) { buf.putChar(ch); } System.out.println(“Buffer after loading: pos = “+ buf.position()+ “\tLimit = “+ buf.limit()+“\tcapacity = “+ buf.capacity()); buf.flip(); // Flip the buffer ready for file write System.out.println(“Buffer after flip: position = “ + buf.position() + “\tLimit = “ + buf.limit() +“\tcapacity = “+ buf.capacity()); // Write the file try { outChannel.write(buf); // Write the buffer to the file channel outputFile.close(); // Close the O/P stream & the channel System.out.println(“Buffer contents written to file.”); } catch (IOException e) { e.printStackTrace(System.err); } System.exit(0); } }
Using a View Buffer to Load Data into a Byte Buffer ByteBuffer buf = ByteBuffer.allocate(1024); CharBuffer charBuf = buf.asCharBuffer(); charBuf.put(phrase); // Transfer string to buffer buf.limit(2*charBuf.position()); // Update byte buffer limit // Create the file output stream channel FileChannel outChannel = outputFile.getChannel(); // Write the file try { outChannel.write(buf); // Write the buffer to the file channel outputFile.close(); // Close the output stream & the channel } catch(IOException e) { e.printStackTrace(System.err); }
Try It Out Writing a String as Bytes import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;
Try It Out Writing a String as Bytes public class WriteAStringAsBytes { public static void main(String[] args) { String phrase = new String(“Garbage in, garbage out\n”); String dirname = “C:/Beg Java Stuff”; // Directory name String filename = “byteData.txt”; File aFile = new File(dirname, filename); // Create the file output stream FileOutputStream file = null; try { file = new FileOutputStream(aFile, true); } catch (FileNotFoundException e) { e.printStackTrace(System.err); } FileChannel outChannel = file.getChannel(); ByteBuffer buf = ByteBuffer.allocate(phrase.length()); byte[] bytes = phrase.getBytes(); buf.put(bytes); buf.flip(); try { outChannel.write(buf); file.close(); // Close the output stream & the channel } catch (IOException e) { e.printStackTrace(System.err); } } }
Try It Out Writing Multiple Strings to a File import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class WriteProverbs { public static void main(String[] args) { String dirName = “c:/Beg Java Stuff”; // Directory for the output file String fileName = “Proverbs.txt”; // Name of the output file String[] sayings = { “Indecision maximizes flexibility.”, “Only the mediocre are always at their best.”, “A little knowledge is a dangerous thing.”, “Many a mickle makes a muckle.”, “Who begins too much achieves little.”, “Who knows most says least.”, “A wise man sits on the hole in his carpet.” }; File aFile = new File(dirName, fileName);
Try It Out Writing Multiple Strings to a File FileOutputStream outputFile = null; try { outputFile = new FileOutputStream(aFile, true); } catch (FileNotFoundException e) { e.printStackTrace(System.err); System.exit(1); } FileChannel outChannel = outputFile.getChannel(); // Create a buffer to accommodate the longest string + its length value int maxLength = 0; for (String saying : sayings) { if(maxLength < saying.length()) maxLength = saying.length(); } ByteBuffer buf = ByteBuffer.allocate(2*maxLength + 4); // Write the file try { for (String saying : sayings) { buf.putInt(saying.length()).asCharBuffer().put(saying); buf.position(buf.position() + 2*saying.length()).flip(); outChannel.write(buf); // Write the buffer to the file channel buf.clear(); } outputFile.close(); // Close the output stream & the channel System.out.println(“Proverbs written to file.”); } catch (IOException e) { e.printStackTrace(System.err); System.exit(1); } System.exit(0); } }
Using a Formatter Object to Load a Buffer ByteBuffer buf = ByteBuffer.allocate(1024); // Byte buffer CharBuffer charBuf = buf.asCharBuffer(); // View buffer Formatter formatter = new Formatter(charBuf); // Formatter to write view buffer
Try It Out Using a Formatter Object to Load a Buffer (missing) import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.util.Formatter; public class UsingAFormatter { public static void main(String[] args) { String[] phrases = { “Rome wasn’t burned in a day.”, “It’s a bold mouse that sits in the cat’s ear.”, “An ounce of practice is worth a pound of instruction.” }; String dirname = “C:/Beg Java Stuff”; // Directory name String filename = “Phrases.txt”; // File name File dir = new File(dirname); // File object for directory // Now check out the directory if (!dir.exists()){ // If directory does not exist if (!dir.mkdir()){ // ...create it System.out.println(“Cannot create directory: “ + dirname); System.exit(1); } } else if (!dir.isDirectory()) { System.err.println(dirname + “ is not a directory”); System.exit(1); }
Try It Out Using a Formatter Object to Load a Buffer // Create the filestream File aFile = new File(dir, filename); // File object for the file path FileOutputStream outputFile = null; // Place to store the stream reference try { outputFile = new FileOutputStream(aFile, true); System.out.println(“File stream created successfully.”); } catch (FileNotFoundException e) { e.printStackTrace(System.err); } // Create the file output stream channel FileChannel outChannel = outputFile.getChannel(); // Create byte buffer to hold data to be written ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println(“\nByte buffer:”); System.out.printf(“position = %2d Limit = %4d capacity = %4d%n”, buf.position(), buf.limit(), buf.capacity()); // Create a view buffer CharBuffer charBuf = buf.asCharBuffer(); System.out.println(“Char view buffer:”); System.out.printf(“position = %2d Limit = %4d capacity = %4d%n”, charBuf.position(),charBuf.limit(),charBuf.capacity()); Formatter formatter = new Formatter(charBuf);
Try It Out Using a Formatter Object to Load a Buffer // Write to the view buffer using a formatter int number = 0; // Proverb number for(String phrase : phrases) { formatter.format(“%nProverb%3d: %s”, ++number, phrase); System.out.println(“\nView buffer after loading:”); System.out.printf(“position = %2d Limit = %4d capacity = %4d%n”, charBuf.position(), charBuf.limit(),charBuf.capacity()); charBuf.flip(); // Flip the view buffer System.out.println(“View buffer after flip:”); System.out.printf(“position = %2d Limit = %4d length = %4d%n”, charBuf.position(),charBuf.limit(),charBuf.length()); buf.limit(2*charBuf.length()); // Set byte buffer limit System.out.println(“Byte buffer after limit update:”); System.out.printf(“position = %2d Limit = %4d length = %4d%n”, buf.position(),buf.limit(), buf.remaining()); // Write the file try { outChannel.write(buf); // Write the buffer to the file channel System.out.println(“Buffer contents written to file.”); buf.clear(); charBuf.clear(); } catch (IOException e) { e.printStackTrace(System.err); System.exit(1); } } try { outputFile.close(); // Close the O/P stream & the channel } catch (IOException e) { e.printStackTrace(System.err); } } }