Friday, February 24, 2017

Reading a file in Java is not simple, it requires lots of boiler plate code, as we have seen in our earlier example of reading text files. Various things had to wrapped e.g. a FileInputStream inside a BufferedReader, loops with weird terminating conditions had to be specified and so forth. From JDK 7 onward, you can do a lot better. It provides lots of useful classes e.g. Files and Paths to deal with file and their actual path. In this article, we will see how we can read a file in just one line. Of course, your production code won't be like that, especially if you are reading a few gigabytes into memory and want to pay attention to the character set, if you don't specify, by platform's default character encoding will be used. In short, you will need a little more code, but for quick and dirty file reading this should do the trick. By the way, It wouldn't be a one-liner if it had exception handling. In a production application you would need to deal with the fact that the file isn't there e.g. displaying an error for the user, posting metrics, logging the error etc, but it certainly be lot less boiler code than it used to be.

Java 8 made life even easier with its new set of methods which take advantage of stream API. Some new methods are also added in Files class which always use UTF-8 instead of random platform encoding, for example Files.readAllLines(). This method read all lines from a file. Bytes from the file are decoded into characters using the UTF-8 charset. It's equivalent to Java 7's Files.readAllLines(path, StandardCharsets.UTF_8). BTW, Always use explicitly character encoding while converting byte array to String. Always explicitly set your charsets, even if the file you are reading contains only English at that moment.

Reading file in one line Java 7 and 8

Here is our sample Java program to demonstrate how you can read a complete file in just one line of code. Our example uses new Files class which was introduced in JDK 1.7, the java.nio.file.Files class contains lots of utility method to deal with files in Java e.g. checking whether a file is hidden, or to check if a file is read only. You can use Files.readAllBytes(Path) to read a complete file in memory. This method returns a byte array, which can be passed to String constructor to create String out of it. This method also ensures that file is properly closed when all bytes are read, or an IO error or other unchecked exception has occurred, which means no need to close file in finally block, in short no boiler plate coding. BTW, this method is not suitable to read large files, because you might run out of memory if enough space is not available in heap. You should also explicitly provide character encoding while converting byte array to String to avoid any surprise or parsing error.

If you want to read file as String then you can also use another method called readAllLines(Path path, Charset cs), this method is similar to previous one i.e. it also close files after reading or in case of error but instead of returning byte array, returns a list of String, which is converted from bytes using specified character set. Further to this, Java 8 added another overloaded version of this method which doesn't require Charset and uses UTF-8 to convert bytes to String.

Goodies not ends here, if you want to read a file line by line, you can use Files.lines() method, which returns a Stream of String read from file, where bytes are converted to character using UTF-8 character encoding. By using forEach() method, you can print all lines of file into console by using just one line of Java code, as shown in third code snippet.

If you are using Java 8, you can also use Stream API to write more concise and performance code, as shown in following example. In this example, lines() method returns a Stream of String, which is converted from bytes read from file to character using UTF-8 character encoding. It's equivalent to Java 7, Files.lines(path, StandardCharsets.UTF_8) method call.

That's all about how to read a text or binary file in one line in Java 7. As you can, new File API of JDK 7 and new Stream API from Java 8 has made file reading quite smooth in Java. It reduced the boiler plate code completely, resulting in much cleaner and concise code. By the way, if you are writing production code for file reading then don't forget to pay attention to following points :

File could be large to fit in memory, so carefully examine the size before reading it and handle the situation when you cannot read file.

Log why you can't read file or any error you encountered while reading file.

P.S. : If you want to learn more about new features in Java 8 then please see the tutorial What's New in Java 8. It explains about all important features of Java 8 e.g. lambda expressions, streams, functional inteface, Optionals, new date and time API and other miscelleneous changes.

That's really weired for a utility method to not complete its job. Files.lines() should have done the cleanup by itself instead of reliaing on clients to do the job. Its very easy to forget about those only to find that your programming is running out of file descriptors.

@Anonymous 1 and 2nd, thanks for your comments but I didn't found any mention of using Files.lines and closing IO streams. Yes, it does throw IOException if any IO error happens while opening file, but no mention about specially closing it. May be you can point to the right reference.

Since Files.lines() is only available in Java 8 and main reason of using it to take advantage of lazily loaded stream API, I am expecting them to closing the file once it read.

Quick question about ReadAllBytes() - I converted a file into byte array and stored in the database(SQL Server). I need to retrieve the byte array from database and decrypt back to the file. Is there a method for that? Or how do I go about doing that? Any help is appreciated.

@Anonymous, you need to use BLOB (Binary Large object) type of SQL Server to store and retrieve that data (byte array) using JDBC. Once you get the byte array in your Java code, you can easily convert that into text. You can even use CLOB (Character large Object) if you are storing text data.

Hello @Anonymous, once you got the byte array in Java code, you can use String constructor new String(byte[], String characterEncoding) to convert it to String or text. You can see this example for more details.

If you have trouble reading varbinary data from SQL Server then you can use getBlob(int columnIndex) method of ResultSet.