4
\$\begingroup\$

I am quite new to Java, and I am trying to read a file into a string (or should I use byte arrays for this?). File can be anything, such as a text file or executable file etc. I will compress what I read and write it to another file.

I am thinking of using this code:

public static String readFile(File f) {
    StringBuilder sb = new StringBuilder();
    try (BufferedReader br = new BufferedReader(new FileReader(f))) {
        String sCurrentLine;
        while ((sCurrentLine = br.readLine()) != null) {
            sb.append(sCurrentLine);
        }

    } catch (IOException e) {
        System.err.println("I/O Exception:" + e.getMessage());
        return null;
    }
    return sb.toString();
}

Does this look good to you? I was reading "Reading and writing text files" and I was a little bit confused over all the different ways one can use to read files.

\$\endgroup\$
  • \$\begingroup\$ I would suggest that you read this : stackoverflow.com/questions/3402735/… \$\endgroup\$ – konijn Aug 8 '13 at 17:42
  • 4
    \$\begingroup\$ Strings are for character data only. Since the file might be an executable, that means it's not always going to be valid character data, so yes, you should use byte arrays instead of strings. \$\endgroup\$ – Matt Ball Aug 8 '13 at 19:33
2
\$\begingroup\$

Why reinvent the wheel?

Use commons-io's FileUtils.readFileToByteArray(File)(javadoc)

\$\endgroup\$
  • 1
    \$\begingroup\$ In this day and age, you'd most probably want to use Guava instead of Apache Commons. (With Guava it's equally easy to read a file into a string or byte array.) \$\endgroup\$ – Jonik Nov 13 '13 at 20:56
2
\$\begingroup\$

Old question, two new answers so far each with some concerns....

Firstly, the original code has a significant bug, which should be pointed out:

    String sCurrentLine;
    while ((sCurrentLine = br.readLine()) != null) {
        sb.append(sCurrentLine);
    }

The above code will read a line from the BufferedReader but it will strip the end-of-line marker (whether that is \r\n, \n, or whatever). When you append this value to the StringBuilder you lose this data.

Unless there is a general need for importing third-party libraries, I try to avoid them. In this case, while the apache commons may have a convenience function, I would hesitate to create a dependency on it for just this function.

Anyway, this particular commons call has a new-in-java7 analogue: byte[] Files.readAllBytes(Path);

Similarly, the OP's actual situation may be solvable by: String[] Files.readAllLines(Path, Charset);

If neither of the above methods will solve the OP's situation, then I would recommend a more traditional approach.....

If you want the data as a byte[] array, then use an Stream approach, but if you want the file as a String, use a Reader, and then:

// guess the amount of characters to be about half the number
// of bytes in the file. It will be something more than this, but
// this will be enough to limit the number of memory re-allocations.
final int charguess = f.length() > Integer.MAX_VALUE
          ? Integer.MAX_VALUE // will likely throw OutOfMemory, not our fault...
          : ((int)(f.length() + 2) / 2 );
final StringBuilder sb = new StringBuilder(charquess);
final char[] buffer = new char[f.length() > 4096 ? 4096 : (int)f.length() + 1];
try (FileReader reader = new FileReader(f)) {
    int len = 0;
    while ((len = reader.read(buffer)) > 0) {
        sb.write(buffer, 0, len);
    }
    return sb.toString();
}

For more robust solutions I recommend specifying the charset encoding that you require the file to be in, and using an InputStream with an InputStreamReader to get the encoding right.

EDIT: Finally, none of the answers so far (including mine, until now) have addressed the OP's actual anticipated usage. He wants to compress the file. The logical solution for this would be a CompressedOutputStream, and a byte[] based InputStream

File outfile = new File(f.getPath() + ".gz");
try (FileInputStream fis = new FileInputStream(f);
     GZipOutputStream gzos = new GZipOutputStream(new FileOutputStream(outfile))) {

    byte[] buffer = new byte[4096];
    int len = 0;
    while ((len = fis.read(buffer)) > 0) {
        gzos.write(buffer, 0, len);
    }
    gzos.finish();
}
\$\endgroup\$
2
\$\begingroup\$

With Guava, a popular Java utility library, you'd do this:

try {
    String contents = Files.toString(new File("file.txt"), Charsets.UTF_8);
catch (IOException e) { 
    // ...
}

Guava is a clean, actively maintained, very well documented library from Google.

If you wanted to use pure Java, try this approach from Stack Overflow.

And since (from question comments) you may actually want to read into a byte array instead of string, with Guava you'd do that similarly with:

Files.toByteArray(new File("debug.apk"));
\$\endgroup\$
0
\$\begingroup\$

Is this good code? The answer is, it depends!

If you're just looking to read a short text file in the default charset of the JVM, then it's quite adequate. I would prefer to propagate the IOException instead of returning null, so that the caller has flexibility in presenting the reason for the failure to the user. Also, I'd read a larger chunk at a time — blocks of 4 kiB or 8 kiB might be good choices, since they should align with the blocks of the underlying disk storage. I don't see any advantage to building the string one line at a time — scanning for the line-break characters is pointless busy work, and you're just going to concatenate everything together again anyway.

On the other hand, if you are expecting binary data, or textual data in some other encoding, or textual data that is possibly malformed (e.g. invalid UTF-8 sequence), then you should read the data into a byte array instead of a String.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.