Trail: Essential Classes
Lesson: Basic I/O
Section: File I/O (featuring NIO.2)
Reading, Writing, and Creating Files
Home Page > Essential Classes > Basic I/O
Reading, Writing, and Creating Files

This page discusses the details of reading, writing, creating and opening files. Three of the methods used to create a new file allow you to specify an optional set of initial attributes for the file. For example, on a file system that supports the POSIX set of standards (such as UNIX), you can specify a file owner, group owner, or file permissions at the time the file is created. It is best if you first familiarize yourself with file attributes in the Managing Metadata before reading this lesson.

File I/O

The java.nio.file package supports stream I/O using regular input stream and output stream API for reading and writing. This is fully interoperable with the java.io package. The following sections cover this form of I/O:

The java.nio.file package also supports channel I/O, which moves data in buffers, bypassing some of the layers that can bottleneck stream I/O. The channel I/O methods are covered in the following section:

The other topic covered in this page is:

Creating Files

You can create an empty file with an initial set of attributes using the createFile(FileAttribute<?>) method. For example, if, at the time of creation, you want a file to have particular set of file permissions, the createFile method is an easy way to accomplish this. If you do not specify any attributes, the file is created with default attributes. If the file already exists, createFile throws an exception.

In a single atomic operation, the createFile method checks for the existence of the file and creates that file with the specified attributes — this makes the process more secure against malicious code.

The following code snippet creates a file using default attributes:

Path file = ...;
try {
    file.createFile();   //Create the empty file with default permissions, etc.
} catch (FileAlreadyExists x) {
    System.err.format("file named %s already exists%n", file);
} catch (IOException x) {
    //Some other sort of failure, such as permissions.
    System.err.format("createFile error: %s%n", x);
}

The POSIX File Permissions section has an example that uses createFile(FileAttribute<?>) to create a file with pre-set permissions.

You can also create a new file using the newOutputStream methods, described in the Creating and Writing a File using Stream I/O section. If you open a new output stream and close it immediately, it creates an empty file.

Reading a File using Stream I/O

To open a file for reading, you can use the newInputStream method. This method returns an unbuffered input stream for reading bytes from the file.

Path file = ...;
InputStream in = null;
try {
    in = file.newInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException x) {
    System.err.println(x);
} finally {
    if (in != null) in.close();
}

Creating and Writing a File using Stream I/O

You can create a file, append to a file, or write to a file using one of the newOutputStream methods:

These methods open or create a file for writing bytes and return an unbuffered output stream. Both methods take a list of OpenOption options. The second method allows you to specify initial file attributes, similar to the createFile(FileAttribute<?>) method. The first method takes the options as a varargs argument and the second method takes the options as a Set. This may seem confusing, but a method can only accept a single varargs argument.

The following StandardOpenOptions are supported:

  • WRITE — Opens the the file for write access.
  • APPEND — Appends the new data to the end of the file — used in conjunction with the WRITE or CREATE options.
  • TRUNCATE_EXISTING — Truncates the file to zero bytes — used in conjunction with the WRITE option.
  • CREATE_NEW — Creates a new file and throws an exception if the file already exists.
  • CREATE — Opens the file if it exists or creates a new file if it does not.
  • DELETE_ON_CLOSE — Deletes the file when the stream is closed — very useful for temp files.
  • SPARSE — Hints that a newly created file will be sparse. This advanced option is honored on some file systems, such as NTFS, where large files with data "gaps" can be stored in a more efficient manner where those empty gaps do not consume disk space.
  • SYNC — Keeps the file (both content and metadata) in sync with the underlying storage device.
  • DSYNC — Keeps the file content in sync with the underlying storage device.

If no open options are specified, and the file does not exist, a new file is created. If the file exists, it is truncated. This is equivalent to invoking the method with the CREATE and TRUNCATE_EXISTING options.

The following code snippet opens a log file. If the file does not exist, it is created; if the file exists, it is opened for appending:

import static java.nio.file.StandardOpenOption.*;

Path logfile = ...;

//Convert the string to a byte array.
String s = ...;
byte data[] = s.getBytes();

OutputStream out = null;
try {
    out = new BufferedOutputStream(logfile.newOutputStream(CREATE, APPEND));
    ...
    out.write(data, 0, data.length);
} catch (IOException x) {
    System.err.println(x);
} finally {
    if (out != null) {
        out.flush();
        out.close();
    }
}

Reading and Writing Files using Channel I/O

Where stream I/O reads a character at a time, channel I/O reads a buffer at a time. The ByteChannel interface provides basic read and write functionality — a SeekableByteChannel is a ByteChannel that has the ability to maintain a position in the channel, and to change that position. A SeekableByteChannel also supports truncating the file associated with the channel and querying the file for its size.

The ability to move to different points in the file and then read from or write to that location makes random access of a file possible. See Random Access Files for more information.

There are two methods for reading and writing channel I/O. As you can see, the method signatures for the newByteChannel methods are almost identical to the newOutputStream methods and they are invoked in a similar manner.


Note: The newByteChannel methods return an instance of a SeekableByteChannel. In the case of the default file system, you can cast this seekable byte channel to a FileChannel providing access to more advanced features such mapping a region of the file directly into memory for faster access, locking a region of the file so other processes cannot access it, or reading and writing bytes from an absolute position without affecting the channel's current position.

Both newByteChannel methods allow you to specify a list of OpenOption options. The same open options used by the newOutputStream methods are supported plus one additional option: READ is required because the SeekableByteChannel supports both reading and writing.

Specifying READ opens the channel for reading. Specifying WRITE or APPEND opens the channel for writing. If none of these is specified, the channel is opened for reading.

The following code reads a file and prints it to standard out:

SeekableByteChannel sbc = null;
try {
    sbc = file.newByteChannel();  //Defaults to READ
    ByteBuffer buf = ByteBuffer.allocate(10);

    //Read the bytes with the proper encoding for this platform.
    //If you skip this step, you might see something that looks like Chinese
    //characters when you expect Latin-style characters.
    String encoding = System.getProperty("file.encoding");
    while (sbc.read(buf) > 0) {
        buf.rewind();
        System.out.print(Charset.forName(encoding).decode(buf));
        buf.flip();
    }
} catch (IOException x) {
    System.out.println("caught exception: " + x);
} finally {
    if (sbc != null) sbc.close();
}

The following code snippet, written for UNIX or other POSIX file systems, creates a log file with a specific set of file permissions. This code creates a log file or appends to the logfile if it already exists. The log file is created with read/write permissions for owner and read only permissions for group.

import static java.nio.file.StandardCopyOption.*;

//Create the set of options for appending to the file.
Set<OpenOptions> options = new HashSet<OpenOption>();
options.add(APPEND);
options.add(CREATE);

//Create the custom permissions attribute.
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);

//Convert the string to a ByetBuffer.
String s = ...;
byte data[] = s.getBytes();
ByteBuffer bb = ByteBuffer.wrap(data);

SeekableByteChannel sbc = null;
try {
    sbc = file.newByteChannel(options, attr);
    sbc.write(bb);
} catch (IOException x) {
    System.out.println("exception thrown: " + x);
} finally {
    if (sbc != null) sbc.close();
}
Previous page: Managing Metadata (File and File Store Attributes)
Next page: Random Access Files