Java Files and JavaStreams Files Data stored in variables and arrays is temporary it’s lost when a local variable goes out of scope or when the program terminates. For long-term retention of data, even after the programs that create the data terminate, computers use files. You use files every day for tasks such as writing a document or creating a spreadsheet. Computers store files on secondary storage devices, including hard disks, flash drives, DVDs and more. Data maintained in files is persistent data it exists beyond the duration of program execution. Files Data can be stored in text files and binary files. We can retrieve information about files and directories using classes Paths and Files and interfaces Path and DirectoryStream (all from package java.nio.file) Consider the mechanisms for writing data to and reading data from files. Read and Write Object with Files Working with text files allows you to quickly and easily start manipulating files. It’s difficult to read data from text files back into object form. Java (and many other object oriented lang.)provides ways to write objects to and read objects from files They are known as object serialization and deserialization. Files and Streams Java views each file as a sequential stream of bytes Java’s view of a file of n bytes Every operating system provides a mechanism to determine the end of a file such as an end-of-file marker or a count of the total bytes in the file that’s recorded in a system-maintained administrative data structure. Files and Streams A Java program processing a stream of bytes simply receives an indication from the operating system when it reaches the end of the stream The program does not need to know how the underlying platform represents files or streams. in some cases, the end-of-file indication occurs as an exception. in others, the indication is a return value from a method invoked on a stream-processing object. Java’s NIO APIs also include classes and interfaces implement so-called channel-based architecture for high-performance I/O. Byte-Based and Character-Based Streams File streams can be used to input and output data as bytes or characters. Byte-based streams output and input data in its binary format a char is two bytes, an int is four bytes, a double is eight bytes, etc. Character-based streams output and input data as a sequence of characters in which every character is two bytes the number of bytes for a given value depends on the number of characters in that value. For example, the value 2000000000 requires 20 bytes (10 characters at two bytes per character) but the value 7 requires only two bytes (1 character at two bytes per character). Byte-Based and Character-Based Streams Files created using byte-based streams are referred to as binary files Binary files are read by programs that understand the file’s specific content and its ordering Files created using character-based streams are referred to as text files. Text files can be read by text editors A numeric value in a binary file can be used in calculations The character 5 is simply a character that can be used in a string of text, as in "Sarah Miller is 15 years old". Standard Input, Standard Output and Standard Error Streams A Java program opens a file by creating an object and associating a stream of bytes or characters with it. The object’s constructor interacts with the operating system to open the file. Java can also associate streams with different devices. When a Java program begins executing, it creates three stream objects that are associated with devices System.in, System.out and System.err. Standard Input, Standard Output and Standard Error Streams The System.in (standard input stream) object normally enables a program to input bytes from the keyboard. Object System.out (the standard output stream object) normally enables a program to output character data to the screen. Object System.err (the standard error stream object) normally enables a program to output character-based error messages to the screen. Each stream can be redirected. For System.in, this capability enables the program to read bytes from a different source. For System.out and System.err, it enables the output to be sent to a different location, such as a file on disk. Class System provides methods setIn, setOut and setErr to redirected the standard input, output and error streams. The java.io and java.nio Packages Java programs perform stream-based processing with classes and interfaces from package java.io and the subpackages of java.nio Java’s New I/O APIs that were first introduced in Java SE 6 There are also other packages throughout the Java APIs containing classes and interfaces based on those in the java.io and java.nio packages. Character-based input and output Character-based input and output can be performed with classes Scanner and Formatter. Scanner also can read data from a file. Class Formatter enables formatted data to be output to any text-based stream in a manner similar to method System.out.printf. Using NIO Classes and Interfaces to Get File and Directory Information Interfaces Path and DirectoryStream and classes Paths and Files (all from package java.nio.file) are useful for retrieving information about files and directories on disk: Path interface—Objects of classes that implement this interface represent the location of a file or directory. Path objects do not open files or provide any fileprocessing capabilities. Paths class—Provides static methods used to get a Path object representing a file or directory location. Using NIO Classes and Interfaces to Get File and Directory Information Files class:Provides static methods for common file and directory manipulations such as copying files creating and deleting files and directories getting information about files and directories reading the contents of files getting objects that allow you to manipulate the contents of files and directories DirectoryStream interface: Objects of classes that implement this interface enable a program to iterate through the contents of a directory. Creating Path Objects We use class static method get of class Paths to convert a String representing a file’s or directory’s location into a Path object. We can then use the methods of interface Path and class Files to determine information about the specified file or directory. For complete lists of their methods http://docs.oracle.com/javase/7/docs/api/java/nio/file/Path.html http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html Absolute vs.Relative Paths A file or directory’s path specifies its location on disk. The path includes some or all of the directories leading to the file or directory. An absolute path contains all directories, starting with the root directory, that lead to a specific file or directory. Every file or directory on a particular disk drive has the same root directory in its path. A relative path is “relative” to another directory Relative path is defined as path related to the present working directory If the file is in the same location as you are in you can just write it’s file name – extension included. A path relative to the directory in which the application began executing. Relative URLs are more convenient because they are shorter and often more portable <a href="foobar.html"> The real world </a> Getting Path Objects from URIs An overloaded version of Files static method get uses a URI object to locate the file or directory. A Uniform Resource Identifier (URI) is a more general form of the Uniform Resource Locators (URLs) that are used to locate websites. the URL http://www.deitel.com/ is the URL for the Deitel & Associates website. URIs for locating files vary across operating systems. On Windows platforms, the URI file://C:/data.txt identifies the file data.txt stored in the root directory of the C: drive. On UNIX/Linux platforms, the URI file:/home/student/data.txt identifies the file data.txt stored in the home directory of the user student. Example: Getting File and Directory Information The following program prompts the user i)to enter a file or directory name ii)uses classes Paths, Path, Files and DirectoryStream to output information about that file or directory. Example: Getting File and Directory Information // File class used to obtain file and directory information. import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; public class FileAndDirectoryInfo { public static void main(String[] args) throws IOException { Scanner input = new Scanner(System.in); /*The program begins by prompting the user for a file or directory // create Path object based on user input System.out.println("Enter file or directory name:" Path path =Paths.get(input.nextLine()); /* inputs the filename or directory name and passes it to Paths static method get, which converts the String to a Path*/ /output: Enter file or directory name: c:\examples\8May if (Files.exists(path)) // if path exists, output info about it { /* invokes Files static method exists, which receives a Path and determines whether it exists (either as a file or as a directory) on disk. // display file (or directory) information System.out.printf("%n%s exists%n", path.getFileName()) ; /*Path method getFileName) gets the String name of the file or directory without any location information*/ //output: 8May exists System.out.printf("%s a directory%n", Files.isDirectory(path) ? "Is" : "Is not"); /* Files static method isDirectory receives a Path and returns a boolean indicating whether that Path represents a directory on disk */ //output: Is a directory System.out.printf("%s an absolute path%n", path.isAbsolute() ? "Is" : "Is not"); /* Path method isAbsolute returns a boolean indicating whether that Path represents an absolute path to a file or directory. //output:Is an absolute path System.out.printf("Last modified: %s%n", Files.getLastModifiedTime(path)); /*Files static method getLastModifiedTime (receives a Path and returns a FileTime (package java.nio.file.attribute) indicating when the file was last modified. The program outputs the FileTime’s default String representation.*/ //output:Last modified: 2015-05-08T19:50:00.838256Z System.out.printf("Size:%s%n",Files.size(path)); /*Files static method size receives a Path and returns a long representing the number of bytes in the file or directory. For directories, the value returned is platform specific.*/ //output:Size: 4096 System.out.printf("Path: %s%n", path); /*Path method toString (called implicitly at this line) returns a String representing the Path.*/ //output: Path: c:\examples\8May System.out.printf("Absolute path: %s%n", path.toAbsolutePath()); /*Path method toAbsolutePath converts the Path on which it’s called to an absolute path*/ /output:Absolute path: c:\examples\8May if (Files.isDirectory(path)) // output directory listing { System.out.printf("%nDirectory contents:%n"); // object for iterating through a directory's contents DirectoryStream <Path> directoryStream =Files.newDirectoryStream(path); /*If the Path represents a directory , we use Files static method newDirectoryStream to get a DirectoryStream<Path> containing Path objects for the directory’s contents*/ for (Path p : directoryStream) System.out.println(p); /*display the String representation of each Path in the DirectoryStream<Path>. Note that DirectoryStream is a generic type like ArrayList */ } } //output:Directory contents: C:\examples\8May\bir C:\examples\8May\iki C:\examples\8May\SerializationApps C:\examples\8May\TextFileApps else // not file or directory, output error message { System.out.printf("%s does not exist%n", path); /*If the name does not exist, a message is displayed containing the Path’s String representation followed by “does not exist.” */ } } // end main } // end class FileAndDirectoryInfo Output of an Another Run of the Example Enter file or directory name: C:\examples\8May\bir\FileAndDirectoryInfo.java FileAndDirectoryInfo.java exists Is not a directory Is an absolute path Last modified: 2015-05-08T19:59:01.848255Z Size: 2952 Path: C:\examples\8May\bir\FileAndDirectoryInfo.java Absolute path: C:\examples\8may\bir\FileAndDirectoryInfo.java Separator Characters A separator character is used to separate directories and files in a path. On a Windows computer, the separator character is a backslash (\). On a Linux or Mac OS X system, it’s a forward slash (/). Java processes both characters identically in a path name. For example, if we were to use the path c:\Program Files\Java\jdk1.6.0_11\demo/jfc Sequential-Access Text Files We create and manipulate sequential-access files in which records are stored in order by the record-key field. We begin with text files, enabling the reader to quickly create and edit human-readable files. We discuss creating, writing data to, reading data from and updating sequential-access text files. Creating a Sequential-Access Text File Java imposes no structure on a file Notions such as records do not exist as part of the Java language. Therefore, you must structure files to meet the requirements of your applications. In the following example, we see how to impose a keyed record structure on a file. Example of keyed record structure on a file The program creates a simple sequentialaccess file It might be used in an accounts receivable system to keep track of the amounts owed to a company by its credit clients. For each client, the program obtains from the user an account number and the client’s name and balance i.e., the amount the client owes the company for goods and services received Each client’s data constitutes a “record” for that client. Example of keyed record structure on a file This application uses the account number as the record key the file’s records will be created and maintained in accountnumber order. The program assumes that the user enters the records in account-number order In a comprehensive accounts receivable system (based on sequential-access files), a sorting capability would be provided so that the user could enter the records in any order The records would then be sorted and written to the file. Example // Writing data to a sequential text file with class Formatter. import java.io.FileNotFoundException; import java.lang.SecurityException; import java.util.Formatter; import java.util.FormatterClosedException; import java.util.NoSuchElementException; import java.util.Scanner; public class CreateTextFile /*Class CreateTextFile uses a Formatter to output formatted Strings, using the same formatting capabilities as method System.out.printf. */ { private static Formatter output; /* Formatter object can output to various locations, such as to a command window or to a file*/ // outputs text to a file public static void main(String[] args) { openFile(); addRecords(); closeFile(); } // open file clients.txt public static void openFile() { /*The Formatter object is instantiated in method openFile try { output = new Formatter("clients.txt"); // open the file /*The constructor takes one argument—a String containing the name of the file, including its path. This is RELATIVE PATH */ /*If a path is not specified, as is the case here, the JVM assumes that the file is in the directory from which the program was executed*/ /*If the file does not exist, it will be created. If an existing file is opened, its contents are truncated—all the data in the file is discarded*/ } catch (SecurityException securityException) { System.err.println("Write permission denied. Terminating."); System.exit(1); // terminate the program } catch (FileNotFoundException fileNotFoundException) { System.err.println("Error opening file. Terminating."); System.exit(1); // terminate the program } } /*If no exception occurs, the file is open for writing and the resulting Formatter object can be used to write data to the file.*/ The Usage of Exceptions The first catch lines handle the SecurityException it occurs if the user does not have permission to write data to the file The second cach lines handle the FileNotFoundException, it occurs if the file does not exist and a new file cannot be created. This exception may also occur if there’s an error opening the file. In both exception handlers we call static method System.exit and pass the value 1. This method terminates the application. An argument of 0 to method exit indicates successful program termination The Usage of Exceptions A nonzero value, such as 1 in this example, normally indicates that an error has occurred. This value is passed to the command window that executed the program. The argument is useful if the program is executed from a batch file on Windows systems or a shell script on UNIX/Linux/Mac OS X systems. Batch files and shell scripts offer a convenient way of executing several programs in sequence. When the first program ends, the next program begins execution. It’s possible to use the argument to method exit in a batch file or shell script to determine whether other programs should execute. /*Method addRecords ( prompts the user to enter the various fields for each record or the end-of-file key sequence when data entry is complete .*/ public static void addRecords() { Scanner input = new Scanner(System.in); //the following line prompt the user for input System.out.printf("%s%n%s%n? ", "Enter account number, first name, last name and balance.", "Enter end-of-file indicator to end input."); while (input.hasNext()) { // loop until end-of-file indicator /*uses Scanner method hasNext to determine whether the end-of-file key combination has been entered. The loop executes until hasNext encounters end-of-file.*/ try { // output new record to file; assumes valid input output.format("%d %s %s %.2f%n", input.nextInt(), input.next(), input.next(), input.nextDouble()); /*use a Scanner to read data from the user, then output the data as a record using the Formatter The record’s information is output using method format, which can perform identical formatting to the System.out.printf */ } The method format Method format outputs a formatted String to the output destination of the Formatter object—the file clients.txt. The format string "%d %s %s %.2f%n" indicates that the current record will be stored as an integer (the account number) followed by a String (the first name), another String (the last name) and a floating-point value (the balance). Each piece of information is separated from the next by a space, and the double value (the balance) is output with two digits to the right of the decimal point (as indicated by the .2 in %.2f). catch (FormatterClosedException formatterClosedException) { System.err.println("Error writing to file. Terminating."); break; } /*Each Scanner input method throws a NoSuchElementException if the data is in the wrong format (e.g., a String when an int is expected) or if there’s no more data to input.*/ { } catch (NoSuchElementException elementException) System.err.println("Invalid input. Please try again."); input.nextLine(); // discard input so user can try again /* When the following lines execute, if the Formatter object is closed, a FormatterClosedException will be thrown */This exception is handled in last two lines of close file method (right brackets) System.out.print("? "); } // end while } // end method addRecords /*method closeFile, which closes the Formatter and the underlying output file. closes the object by simply calling method close*/ public static void closeFile() { if (output != null) output.close(); } } // end class CreateTextFile System.out.print("? "); What does it do? System.out.print("? "); code line is run after every invocation process of the method format; in other words after each execution of the output.format(…,…,….,……) line. Since the specification of the method format (last declaration is %n) , ? is seen at the beginning of each output data. Question: Is the first ? is written from the printf function that is written after Scanner object? Writing data to a sequential text file with class Formatter Enter account number, first name, last name and balance. Enter end-of-file indicator to end input. ? 100 Bob Blue 24.98 ? 200 Steve Green -345.67 ? 300 Pam White 0.00 ? 400 Sam Red -42.16 ? 500 Sue Yellow 224.62 ? ^Z (end-of file key combination in Windows) The key combinations for entering end-of-file for various computer systems The sample data for this application The user enters information for five accounts, then enters end-of-file to signal that data entry is complete. The sample output does not show how the data records actually appear in the file. Reading Data from a Sequential-Access Text File The next example shows how to read data sequentially from a text file. We demonstrate how class Scanner can be used to input data from a file rather than the keyboard. The next application reads records from the file "clients.txt" created by the previous application and displays the record contents. a Scanner will be used to retrieve input from the file Reading a text file & Displaying each record import java.io.IOException; import java.lang.IllegalStateException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.NoSuchElementException; import java.util.Scanner; public class ReadTextFile { private static Scanner input; public static void main(String[] args) { openFile(); readRecords(); closeFile(); } // open file clients.txt public static void openFile() { try { input = new Scanner(Paths.get("clients.txt")); } catch (IOException ioException) { System.err.println("Error opening file. Terminating."); System.exit(1); } } //read record from file public static void readRecords() { System.out.printf("%-10s%-12s%-12s%10s%n", "Account", "First Name", "Last Name", "Balance"); try { while (input.hasNext()) // while there is more to read { // display record contents System.out.printf("%-10d%-12s%-12s%10.2f%n", input.nextInt(), input.next(), input.next(), input.nextDouble()); } } catch (NoSuchElementException elementException) { System.err.println("File improperly formed. Terminating."); } catch (IllegalStateException stateException) { System.err.println("Error reading from file. Terminating."); } } // end method readRecords // close file and terminate application public static void closeFile() { if (input != null) input.close(); } } // end class ReadTextFile Sequential File Reading Using a Scanner Account 100 200 300 400 500 . First Name Last Name Balance Bob Blue 24.98 Steve Green -345.67 Pam White 0.00 Sam Red -42.16 Sue Yellow 224.62 Explanation Step by Step Method openFile opens the file for reading by instantiating a Scanner object We pass a Path object to the constructor it specifies that the Scanner object will read from the file "clients.txt" located in the directory from which the application executes. (relative path) If the file cannot be found, an IOException occurs. Explanation Step by Step Method readRecords reads and displays records from the file. Headers are displayed for the columns in the application’s output. Data is read and displayed from the file until the endof-file marker is reached until the method hasNext will return false Scanner methods nextInt, next and nextDouble are used to input an int (the account number), two Strings (the first and last names) and a double value (the balance). Each record is one line of data in the file. Explanation Step by Step If the information in the file is not properly formed ,a NoSuchElementException occurs when the record is input. If the Scanner was closed before the data was input, an IllegalStateException occurs The account number, first name and last name are left justified, as the format string while the balance is right justified and output with two digits of precision. Each iteration of the loop inputs one line of text from the text file it represents one record. We define method closeFile, which closes the Scanner. Explanation Step by Step When the data was output to disk, certain information was lost, such as the type of each value. For instance, if the value "3" is read from a file, there’s no way to tell whether it came from an int, a String or a double. We have only data, not type information, on a disk.