About Yifan Peng

Yifan is a senior CIS PhD student in University of Delaware. His main researches include relation extraction, sentence simplification, text mining and natural language processing. He does not consider himself of a Java geek, but all his projects are written in Java.

Java.io in nutshell: 22 case studies

This post attempts to cover a comprehensive set of operations in java.io. Compared with other books and blogs related to this topic, my motivation is to show “how-to” through case studies. As once a Java student, I realize the most effective way of learning a new program language is through examples: copy and paste a piece of codes, run it to see results, then try to modify and re-run it step-by-step. Therefore, I assume this post will be helpful.

It is worth noting that this post will not touch anything related to java.nio because I think it is quite a different topic.
 
 

Table of Contents

Case 0: Create a new file

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    try {
      f.createNewFile();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Output: a new and empty file is created in the working directory, iff there is no “helloword.txt” before.

Case 1: Two constants in File

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    System.out.println(File.separator);
    System.out.println(File.pathSeparator);
  }
}

Output:

/
:

I get the above output because I am working on Linux. If you use Windows, the output should be \ and ;. As can be seen, for the purpose of portability and robustness, it should always be recommended to use these two constants.

Case 2: Delete a file

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    if (f.exists()) {
      if (!f.delete()) {
        System.out.println("the file cannot be deleted.");
      }
    } else {
      System.out.println("the file does not exist.");
    }
  }
}

Case 3: Create a directory

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("hello");
    f.mkdir();
  }
}

Case 4: List files and directories in a given directory

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File(".");
    for (String str : f.list()) {
      System.out.println(str);
    }
  }
}

Output: I am using Eclipse

.settings
.classpath
.project
src
bin

File.list() returns an array of strings. If you prefer an array of Files, please use File.listFiles():

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File(".");
    for (File subFile : f.listFiles()) {
      System.out.println(subFile.getName());
    }
  }
}

Case 5: Tests whether a file is a file

import java.io.File;
public class FileOperationTest {
  public static void main(String[] args) {
    File f = new File("helloworld.txt");
    if (f.isFile()) {
      System.out.println("YES");
    } else {
      System.out.println("NO");
    }
  }
}

Combined with File.listFiles(), we can list all files in the given directory and its sub-directories.

import java.io.File;

public class FileOperationTest {

  public static void main(String[] args) {
    File f = new File(".");
    listFiles(f);
  }

  private static void listFiles(File f) {
    if (f.isFile()) {
      System.out.println(f.getName());
      return;
    }
    for (File subFile : f.listFiles()) {
      listFiles(subFile);
    }
  }
}

Output: compared with Case 4 to see the difference

org.eclipse.jdt.core.prefs
.classpath
.project
FileOperationTest.java
FileOperationTest.class

Case 6: Write to a RandomAccessFile

import java.io.IOException;
import java.io.RandomAccessFile;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    RandomAccessFile file = new RandomAccessFile(
        "helloworld.txt", "rw");
    file.writeBytes("hello world!");
    file.writeChar('A');
    file.writeInt(1);
    file.writeBoolean(true);
    file.writeFloat(1.0f);
    file.writeDouble(1.0);
    file.close();
  }
}

If you open the file using text editor, you will find garbled code except the first hello world!A (kindly note the char A at the end of “hello world!”). This is because RandomAccessFile only writes an array of bytes in the file.

Case 7: Write bytes to a file

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt");
    String str = "hello world!";
    out.write(str.getBytes());
    out.close();
  }
}

This time you can see “hello world!” in the file. Of course you can write to an OutputStream byte by byte, but it is less effective:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt");
    String str = "hello world!";
    for (byte b : str.getBytes()) {
      out.write(b);
    }
    out.close();
  }
}

Case 8: Append bytes to a file

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    OutputStream out = new FileOutputStream(
        "helloworld.txt", true);
    String str = "hello world!";
    out.write(str.getBytes());
    out.close();
  }
}

Output: hello world!hello world!

Case 9: Read bytes from a file

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    InputStream in = new FileInputStream("helloworld.txt");
    byte[] bs = new byte[1024];
    int len = -1;
    while ((len = in.read(bs)) != -1) {
      System.out.println(new String(bs, 0, len));
    }
    in.close();
  }
}

The InputStream.read() will return -1 if it reaches the end of the file. Otherwise, it will return the total number of bytes read into the buffer.

Case 10: Copy files

Simply combine Case 7 and 9, we will get the copy function.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Copy {

  public static void main(String[] args)
      throws IOException {
    if (args.length != 2) {
      System.out.println("java Copy SOURCE DEST");
      System.exit(1);
    }
    InputStream input = new FileInputStream(args[0]);
    OutputStream output = new FileOutputStream(args[1]);
    int len = 0;
    byte bs[] = new byte[1024];
    while ((len = input.read(bs)) != -1) {
      output.write(bs, 0, len);
    }
    input.close();
    output.close();
  }
}

Case 11: Write chars to a file

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    Writer out = new FileWriter("helloworld.txt");
    String str = "hello world!";
    out.write(str);
    out.close();
  }
}

For the above case, you will get the same result as in Case 7. So what is the difference? FileWriter is meant for writing streams of characters. It will use the default character encoding and the default byte-buffer size. In other words, it is a wrapper class of FileOutputStream for convenience. Therefore, to specify these values yourself, consider using a FileOutputStream.

Case 12: Read chars from a file

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    Reader in = new FileReader("helloworld.txt");
    char cs[] = new char[1024];
    int len = -1;
    while ((len = in.read(cs)) != -1) {
      System.out.println(new String(cs, 0, len));
    }
    in.close();
  }
}

Whether to use streams of bytes or characters? It really depends. Both have buffers. InputStream/OutputStream provide more flexibility, but will make your “simple” program complex. On the other hand, FileWriter/FileReader give a neat solution but you lose the control.

Case 13: Convert from OutputStream to FileWriter

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    Writer out = new OutputStreamWriter(
        new FileOutputStream("helloworld.txt"));
    out.write("hello world!");
    out.close();
  }
}

Instead of using default character encoding, you can specify the charset. For example,

Writer out = new OutputStreamWriter(
        new FileOutputStream("helloworld.txt"), "utf8");

Case 14: Convert from InputStream to FileReader

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    Reader in = new InputStreamReader(
        new FileInputStream("helloworld.txt"));
    char cs[] = new char[1024];
    int len = -1;
    while ((len = in.read(cs)) != -1) {
      System.out.println(new String(cs, 0, len));
    }
    in.close();
  }
}

Case 15: Use pipeline

The following code creates two thread, a Producer which writes something into the pipeline at one end and a Consumer which reads it from the pipeline at the other end. To create a pipeline, we need to create PipedInputStream and PipedOutputStream seperately, and connect them using output.connect(input) or via their constructors. In this program, I intentionally start the Consumer thread first and ask the whole program to sleep 1 second before starting the Producer thread. This will show the pipeline DOES work. It is worthy noting that, I close the pipeline in the Producer because “A thread that writes to a stream should always close the OutputStream before terminating.” If we remove the out.close() line, an IOException will be thrown

java.io.IOException: Write end dead
    at java.io.PipedInputStream.read(PipedInputStream.java:311)
    at java.io.PipedInputStream.read(PipedInputStream.java:378)
    at java.io.InputStream.read(InputStream.java:101)
    at foo.Consumer.run(FileOperationTest.java:58)
    at java.lang.Thread.run(Thread.java:701)
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException, InterruptedException {

    PipedInputStream input = new PipedInputStream();
    PipedOutputStream output = new PipedOutputStream();
    output.connect(input);

    Producer producer = new Producer(output);
    Consumer consumer = new Consumer(input);
    new Thread(consumer).start();
    Thread.sleep(1000);
    new Thread(producer).start();
  }
}

class Producer implements Runnable {

  private final OutputStream out;

  public Producer(OutputStream out) {
    this.out = out;
  }

  @Override
  public void run() {
    String str = "hello world!";
    try {
      out.write(str.getBytes());
      out.flush();
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

class Consumer implements Runnable {

  private final InputStream in;

  public Consumer(InputStream in) {
    this.in = in;
  }

  @Override
  public void run() {
    byte[] bs = new byte[1024];
    int len = -1;
    try {
      while ((len = in.read(bs)) != -1) {
        System.out.println(new String(bs, 0, len));
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Case 16: Write formatted string to a file

PrintStream adds functionality to print representations of various data values conveniently. The format string syntax is almost same as C.

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    PrintStream print = new PrintStream(
        new FileOutputStream("helloworld.txt"));
    print.printf("%s %s!", "hello", "world");
    print.close();
  }
}

Case 17: Redirect the “standard” IO

In Java, both standard and error output are PrintStreams. The standard input is InputStream. Therefore we can reassign them freely. The following code redirect the standard output to error output.

public class FileOperationTest {
  public static void main(String[] args) {
    System.out.println("hello world!");
    System.setOut(System.err);
    System.out.println("hello world!");
  }
}

Output: In Eclipse, red text means error message

hello world!hello world!

Case 18: Read a file line by line

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    BufferedReader reader = new BufferedReader(
        new FileReader("helloworld.txt"));
    String str = null;
    while ((str = reader.readLine()) != null) {
      System.out.println(str);
    }
    reader.close();
  }
}

Case 19: Compress to a zip file

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(
        "helloworld.zip"));
    String str = "hello world!";
    for (int i = 0; i < 3; i++) {
      zipOut.putNextEntry(new ZipEntry("helloworld" + i + ".txt"));
      zipOut.write(str.getBytes());
      zipOut.closeEntry();
    }
    zipOut.close();
  }
}

The above code creates a zip file and puts three files, named “helloworld0.txt”, “helloworld1.txt”, and “helloworld2.txt”, each of which contains the content “hello world!”.

Case 20: Extract from a zip file

import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    ZipInputStream zipIn = new ZipInputStream(new FileInputStream(
        "helloworld.zip"));
    ZipEntry entry = null;
    byte bs[] = new byte[1024];
    while ((entry = zipIn.getNextEntry()) != null) {
      // get file name
      System.out.printf("file: %s content: ", entry.getName());
      int len = -1;
      // read current entry to the buffer
      while((len=zipIn.read(bs)) != -1) {
        System.out.print(new String(bs, 0, len));
      }
      System.out.println();
    }
    zipIn.close();
  }
}

Output:

file: helloworld0.txt content: hello world!
file: helloworld1.txt content: hello world!
file: helloworld2.txt content: hello world!

Case 21: Push back bytes

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    PushbackInputStream push = new PushbackInputStream(
        new ByteArrayInputStream("hello, world!".getBytes()));
    int temp = 0;
    while ((temp = push.read()) != -1) {
      if (temp == ',') {
        push.unread('.');
      }
      System.out.print((char) temp);
    }
  }
}

The above code pushes a dot after reading a comma, therefore the output is

hello,. world!

However, if you try to push more characters back such as push.unread("(...)".getBytes());, you will get an IOException: Push back buffer is full. This is because the default size of the pushback buffer is 1. To specify it with larger capacity, use the constructor PushbackInputStream(InputStream in, int size), such as

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

public class FileOperationTest {

  public static void main(String[] args)
      throws IOException {
    PushbackInputStream push = new PushbackInputStream(
        new ByteArrayInputStream("hello, world!".getBytes()), 10);
    int temp = 0;
    while ((temp = push.read()) != -1) {
      if (temp == ',') {
        push.unread("(...)".getBytes());
      }
      System.out.print((char) temp);
    }
  }
}

Output:

hello,(...) world!

 

Reference: Java.io in nutshell: 22 case studies from our JCG partner Yifan Peng at the PGuru blog.
Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

3 Responses to "Java.io in nutshell: 22 case studies"

  1. David says:

    I’m afraid to say that the guava library (especially com.google.common.io.Files) makes many of these redundant…

  2. Harald says:

    Nice collection of cases. But may I suggest to improve the examples to not lure beginners into thinking that a conversion between bytes and String is ok without excplicit encoding? This applies to String.getBytes(), as well as for all the constructors of Reader or Writer you use.

    Just yesterday we had to waste three hours for an encoding bug hunt where a Reader was opened without explicit encoding. It worked on Linux but not on Windows, because the default encoding is different.

    One further comment regarding IOExceptions. You define your main() methods as throwing an IOException. This gets you around demonstrating all the issues of properly handling IOExceptions in production code. At least a warning for beginners would be fair, that in real code, it is an important aspect to think about how to handle all the possible IOExceptions.

    In your pipeline example, the close() calls are missing, as well as the non-trivial issue of how to wait for the threads to finish. Rather than doing this by hand, some classes from java.util.concurrent should probably be used.

Leave a Reply


seven + 2 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books