Read a file line by line in reverse order - java

I have a java ee application where I use a servlet to print a log file created with log4j. When reading log files you are usually looking for the last log line and therefore the servlet would be much more useful if it printed the log file in reverse order. My actual code is:
response.setContentType("text");
PrintWriter out = response.getWriter();
try {
FileReader logReader = new FileReader("logfile.log");
try {
BufferedReader buffer = new BufferedReader(logReader);
for (String line = buffer.readLine(); line != null; line = buffer.readLine()) {
out.println(line);
}
} finally {
logReader.close();
}
} finally {
out.close();
}
The implementations I've found in the internet involve using a StringBuffer and loading all the file before printing, isn't there a code light way of seeking to the end of the file and reading the content till the start of the file?

[EDIT]
By request, I am prepending this answer with the sentiment of a later comment: If you need this behavior frequently, a "more appropriate" solution is probably to move your logs from text files to database tables with DBAppender (part of log4j 2). Then you could simply query for latest entries.
[/EDIT]
I would probably approach this slightly differently than the answers listed.
(1) Create a subclass of Writer that writes the encoded bytes of each character in reverse order:
public class ReverseOutputStreamWriter extends Writer {
private OutputStream out;
private Charset encoding;
public ReverseOutputStreamWriter(OutputStream out, Charset encoding) {
this.out = out;
this.encoding = encoding;
}
public void write(int ch) throws IOException {
byte[] buffer = this.encoding.encode(String.valueOf(ch)).array();
// write the bytes in reverse order to this.out
}
// other overloaded methods
}
(2) Create a subclass of log4j WriterAppender whose createWriter method would be overridden to create an instance of ReverseOutputStreamWriter.
(3) Create a subclass of log4j Layout whose format method returns the log string in reverse character order:
public class ReversePatternLayout extends PatternLayout {
// constructors
public String format(LoggingEvent event) {
return new StringBuilder(super.format(event)).reverse().toString();
}
}
(4) Modify my logging configuration file to send log messages to both the "normal" log file and a "reverse" log file. The "reverse" log file would contain the same log messages as the "normal" log file, but each message would be written backwards. (Note that the encoding of the "reverse" log file would not necessarily conform to UTF-8, or even any character encoding.)
(5) Create a subclass of InputStream that wraps an instance of RandomAccessFile in order to read the bytes of a file in reverse order:
public class ReverseFileInputStream extends InputStream {
private RandomAccessFile in;
private byte[] buffer;
// The index of the next byte to read.
private int bufferIndex;
public ReverseFileInputStream(File file) {
this.in = new RandomAccessFile(File, "r");
this.buffer = new byte[4096];
this.bufferIndex = this.buffer.length;
this.in.seek(file.length());
}
public void populateBuffer() throws IOException {
// record the old position
// seek to a new, previous position
// read from the new position to the old position into the buffer
// reverse the buffer
}
public int read() throws IOException {
if (this.bufferIndex == this.buffer.length) {
populateBuffer();
if (this.bufferIndex == this.buffer.length) {
return -1;
}
}
return this.buffer[this.bufferIndex++];
}
// other overridden methods
}
Now if I want to read the entries of the "normal" log file in reverse order, I just need to create an instance of ReverseFileInputStream, giving it the "revere" log file.

This is a old question. I also wanted to do the same thing and after some searching found there is a class in apache commons-io to achieve this:
org.apache.commons.io.input.ReversedLinesFileReader

I think a good choice for this would be using RandomFileAccess class. There is some sample code for back-reading using this class on this page. Reading bytes this way is easy, however reading strings might be a bit more challenging.

If you are in a hurry and want the simplest solution without worrying too much about performance, I would give a try to use an external process to do the dirty job (given that you are running your app in a Un*x server, as any decent person would do XD)
new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("tail yourlogfile.txt -n 50 | rev").getProcess().getInputStream()))

A simpler alternative, because you say that you're creating a servlet to do this, is to use a LinkedList to hold the last N lines (where N might be a servlet parameter). When the list size exceeds N, you call removeFirst().
From a user experience perspective, this is probably the best solution. As you note, the most recent lines are the most important. Not being overwhelmed with information is also very important.

Good question. I'm not aware of any common implementations of this. It's not trivial to do properly either, so be careful what you choose. It should deal with character set encoding and detection of different line break methods. Here's the implementation I have so far that works with ASCII and UTF-8 encoded files, including a test case for UTF-8. It does not work with UTF-16LE or UTF-16BE encoded files.
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
public class ReverseLineReader {
private static final int BUFFER_SIZE = 8192;
private final FileChannel channel;
private final String encoding;
private long filePos;
private ByteBuffer buf;
private int bufPos;
private byte lastLineBreak = '\n';
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
public ReverseLineReader(File file, String encoding) throws IOException {
RandomAccessFile raf = new RandomAccessFile(file, "r");
channel = raf.getChannel();
filePos = raf.length();
this.encoding = encoding;
}
public String readLine() throws IOException {
while (true) {
if (bufPos < 0) {
if (filePos == 0) {
if (baos == null) {
return null;
}
String line = bufToString();
baos = null;
return line;
}
long start = Math.max(filePos - BUFFER_SIZE, 0);
long end = filePos;
long len = end - start;
buf = channel.map(FileChannel.MapMode.READ_ONLY, start, len);
bufPos = (int) len;
filePos = start;
}
while (bufPos-- > 0) {
byte c = buf.get(bufPos);
if (c == '\r' || c == '\n') {
if (c != lastLineBreak) {
lastLineBreak = c;
continue;
}
lastLineBreak = c;
return bufToString();
}
baos.write(c);
}
}
}
private String bufToString() throws UnsupportedEncodingException {
if (baos.size() == 0) {
return "";
}
byte[] bytes = baos.toByteArray();
for (int i = 0; i < bytes.length / 2; i++) {
byte t = bytes[i];
bytes[i] = bytes[bytes.length - i - 1];
bytes[bytes.length - i - 1] = t;
}
baos.reset();
return new String(bytes, encoding);
}
public static void main(String[] args) throws IOException {
File file = new File("my.log");
ReverseLineReader reader = new ReverseLineReader(file, "UTF-8");
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
public static class ReverseLineReaderTest extends TestCase {
public void test() throws IOException {
File file = new File("utf8test.log");
String encoding = "UTF-8";
FileInputStream fileIn = new FileInputStream(file);
Reader fileReader = new InputStreamReader(fileIn, encoding);
BufferedReader bufReader = new BufferedReader(fileReader);
List<String> lines = new ArrayList<String>();
String line;
while ((line = bufReader.readLine()) != null) {
lines.add(line);
}
Collections.reverse(lines);
ReverseLineReader reader = new ReverseLineReader(file, encoding);
int pos = 0;
while ((line = reader.readLine()) != null) {
assertEquals(lines.get(pos++), line);
}
assertEquals(lines.size(), pos);
}
}
}

you can use RandomAccessFile implements this function,such as:
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import com.google.common.io.LineProcessor;
public class FileUtils {
/**
* 反向读取文本文件(UTF8),文本文件分行是通过\r\n
*
* #param <T>
* #param file
* #param step 反向寻找的步长
* #param lineprocessor
* #throws IOException
*/
public static <T> T backWardsRead(File file, int step,
LineProcessor<T> lineprocessor) throws IOException {
RandomAccessFile rf = new RandomAccessFile(file, "r");
long fileLen = rf.length();
long pos = fileLen - step;
// 寻找倒序的第一行:\r
while (true) {
if (pos < 0) {
// 处理第一行
rf.seek(0);
lineprocessor.processLine(rf.readLine());
return lineprocessor.getResult();
}
rf.seek(pos);
char c = (char) rf.readByte();
while (c != '\r') {
c = (char) rf.readByte();
}
rf.readByte();//read '\n'
pos = rf.getFilePointer();
if (!lineprocessor.processLine(rf.readLine())) {
return lineprocessor.getResult();
}
pos -= step;
}
}
use:
FileUtils.backWardsRead(new File("H:/usersfavs.csv"), 40,
new LineProcessor<Void>() {
//TODO implements method
.......
});

The simplest solution is to read through the file in forward order, using an ArrayList<Long> to hold the byte offset of each log record. You'll need to use something like Jakarta Commons CountingInputStream to retrieve the position of each record, and will need to carefully organize your buffers to ensure that it returns the proper values:
FileInputStream fis = // .. logfile
BufferedInputStream bis = new BufferedInputStream(fis);
CountingInputStream cis = new CountingInputSteam(bis);
InputStreamReader isr = new InputStreamReader(cis, "UTF-8");
And you probably won't be able to use a BufferedReader, because it will attempt to read-ahead and throw off the count (but reading a character at a time won't be a performance problem, because you're buffering lower in the stack).
To write the file, you iterate the list backwards and use a RandomAccessFile. There is a bit of a trick: to properly decode the bytes (assuming a multi-byte encoding), you will need to read the bytes corresponding to an entry, and then apply a decoding to it. The list, however, will give you the start and end position of the bytes.
One big benefit to this approach, versus simply printing the lines in reverse order, is that you won't damage multi-line log messages (such as exceptions).

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Inside of C:\\temp\\vaquar.txt we have following content
* vaquar khan is working into Citi He is good good programmer programmer trust me
* #author vaquar.khan#gmail.com
*
*/
public class ReadFileAndDisplayResultsinReverse {
public static void main(String[] args) {
try {
// read data from file
Object[] wordList = ReadFile();
System.out.println("File data=" + wordList);
//
Set<String> uniquWordList = null;
for (Object text : wordList) {
System.out.println((String) text);
List<String> tokens = Arrays.asList(text.toString().split("\\s+"));
System.out.println("tokens" + tokens);
uniquWordList = new HashSet<String>(tokens);
// If multiple line then code into same loop
}
System.out.println("uniquWordList" + uniquWordList);
Comparator<String> wordComp= new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
if(o1==null && o2 ==null) return 0;
if(o1==null ) return o2.length()-0;
if(o2 ==null) return o1.length()-0;
//
return o2.length()-o1.length();
}
};
List<String> fs=new ArrayList<String>(uniquWordList);
Collections.sort(fs,wordComp);
System.out.println("uniquWordList" + fs);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static Object[] ReadFile() throws IOException {
List<String> list = Files.readAllLines(new File("C:\\temp\\vaquar.txt").toPath(), Charset.defaultCharset());
return list.toArray();
}
}
Output:
[Vaquar khan is working into Citi He is good good programmer programmer trust me
tokens[vaquar, khan, is, working, into, Citi, He, is, good, good, programmer, programmer, trust, me]
uniquWordList[trust, vaquar, programmer, is, good, into, khan, me, working, Citi, He]
uniquWordList[programmer, working, vaquar, trust, good, into, khan, Citi, is, me, He]
If you want to Sort A to Z then write one more comparater

Concise solution using Java 7 Autoclosables and Java 8 Streams :
try (Stream<String> logStream = Files.lines(Paths.get("C:\\logfile.log"))) {
logStream
.sorted(Comparator.reverseOrder())
.limit(10) // last 10 lines
.forEach(System.out::println);
}
Big drawback: only works when lines are strictly in natural order, like log files prefixed with timestamps but without exceptions

Related

Reading in a txt file using scanner on Eclipse, what am i doing wrong. java [duplicate]

It seems there are different ways to read and write data of files in Java.
I want to read ASCII data from a file. What are the possible ways and their differences?
My favorite way to read a small file is to use a BufferedReader and a StringBuilder. It is very simple and to the point (though not particularly effective, but good enough for most cases):
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
String everything = sb.toString();
} finally {
br.close();
}
Some has pointed out that after Java 7 you should use try-with-resources (i.e. auto close) features:
try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
String everything = sb.toString();
}
When I read strings like this, I usually want to do some string handling per line anyways, so then I go for this implementation.
Though if I want to actually just read a file into a String, I always use Apache Commons IO with the class IOUtils.toString() method. You can have a look at the source here:
http://www.docjar.com/html/api/org/apache/commons/io/IOUtils.java.html
FileInputStream inputStream = new FileInputStream("foo.txt");
try {
String everything = IOUtils.toString(inputStream);
} finally {
inputStream.close();
}
And even simpler with Java 7:
try(FileInputStream inputStream = new FileInputStream("foo.txt")) {
String everything = IOUtils.toString(inputStream);
// do something with everything string
}
ASCII is a TEXT file so you would use Readers for reading. Java also supports reading from a binary file using InputStreams. If the files being read are huge then you would want to use a BufferedReader on top of a FileReader to improve read performance.
Go through this article on how to use a Reader
I'd also recommend you download and read this wonderful (yet free) book called Thinking In Java
In Java 7:
new String(Files.readAllBytes(...))
(docs)
or
Files.readAllLines(...)
(docs)
In Java 8:
Files.lines(..).forEach(...)
(docs)
The easiest way is to use the Scanner class in Java and the FileReader object. Simple example:
Scanner in = new Scanner(new FileReader("filename.txt"));
Scanner has several methods for reading in strings, numbers, etc... You can look for more information on this on the Java documentation page.
For example reading the whole content into a String:
StringBuilder sb = new StringBuilder();
while(in.hasNext()) {
sb.append(in.next());
}
in.close();
outString = sb.toString();
Also if you need a specific encoding you can use this instead of FileReader:
new InputStreamReader(new FileInputStream(fileUtf8), StandardCharsets.UTF_8)
Here is a simple solution:
String content = new String(Files.readAllBytes(Paths.get("sample.txt")));
Or to read as list:
List<String> content = Files.readAllLines(Paths.get("sample.txt"))
Here's another way to do it without using external libraries:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public String readFile(String filename)
{
String content = null;
File file = new File(filename); // For example, foo.txt
FileReader reader = null;
try {
reader = new FileReader(file);
char[] chars = new char[(int) file.length()];
reader.read(chars);
content = new String(chars);
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null){
reader.close();
}
}
return content;
}
I had to benchmark the different ways. I shall comment on my findings but, in short, the fastest way is to use a plain old BufferedInputStream over a FileInputStream. If many files must be read then three threads will reduce the total execution time to roughly half, but adding more threads will progressively degrade performance until making it take three times longer to complete with twenty threads than with just one thread.
The assumption is that you must read a file and do something meaningful with its contents. In the examples here is reading lines from a log and count the ones which contain values that exceed a certain threshold. So I am assuming that the one-liner Java 8 Files.lines(Paths.get("/path/to/file.txt")).map(line -> line.split(";")) is not an option.
I tested on Java 1.8, Windows 7 and both SSD and HDD drives.
I wrote six different implementations:
rawParse: Use BufferedInputStream over a FileInputStream and then cut lines reading byte by byte. This outperformed any other single-thread approach, but it may be very inconvenient for non-ASCII files.
lineReaderParse: Use a BufferedReader over a FileReader, read line by line, split lines by calling String.split(). This is approximatedly 20% slower that rawParse.
lineReaderParseParallel: This is the same as lineReaderParse, but it uses several threads. This is the fastest option overall in all cases.
nioFilesParse: Use java.nio.files.Files.lines()
nioAsyncParse: Use an AsynchronousFileChannel with a completion handler and a thread pool.
nioMemoryMappedParse: Use a memory-mapped file. This is really a bad idea yielding execution times at least three times longer than any other implementation.
These are the average times for reading 204 files of 4 MB each on an quad-core i7 and SSD drive. The files are generated on the fly to avoid disk caching.
rawParse 11.10 sec
lineReaderParse 13.86 sec
lineReaderParseParallel 6.00 sec
nioFilesParse 13.52 sec
nioAsyncParse 16.06 sec
nioMemoryMappedParse 37.68 sec
I found a difference smaller than I expected between running on an SSD or an HDD drive being the SSD approximately 15% faster. This may be because the files are generated on an unfragmented HDD and they are read sequentially, therefore the spinning drive can perform nearly as an SSD.
I was surprised by the low performance of the nioAsyncParse implementation. Either I have implemented something in the wrong way or the multi-thread implementation using NIO and a completion handler performs the same (or even worse) than a single-thread implementation with the java.io API. Moreover the asynchronous parse with a CompletionHandler is much longer in lines of code and tricky to implement correctly than a straight implementation on old streams.
Now the six implementations followed by a class containing them all plus a parametrizable main() method that allows to play with the number of files, file size and concurrency degree. Note that the size of the files varies plus minus 20%. This is to avoid any effect due to all the files being of exactly the same size.
rawParse
public void rawParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
overrunCount = 0;
final int dl = (int) ';';
StringBuffer lineBuffer = new StringBuffer(1024);
for (int f=0; f<numberOfFiles; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
FileInputStream fin = new FileInputStream(fl);
BufferedInputStream bin = new BufferedInputStream(fin);
int character;
while((character=bin.read())!=-1) {
if (character==dl) {
// Here is where something is done with each line
doSomethingWithRawLine(lineBuffer.toString());
lineBuffer.setLength(0);
}
else {
lineBuffer.append((char) character);
}
}
bin.close();
fin.close();
}
}
public final void doSomethingWithRawLine(String line) throws ParseException {
// What to do for each line
int fieldNumber = 0;
final int len = line.length();
StringBuffer fieldBuffer = new StringBuffer(256);
for (int charPos=0; charPos<len; charPos++) {
char c = line.charAt(charPos);
if (c==DL0) {
String fieldValue = fieldBuffer.toString();
if (fieldValue.length()>0) {
switch (fieldNumber) {
case 0:
Date dt = fmt.parse(fieldValue);
fieldNumber++;
break;
case 1:
double d = Double.parseDouble(fieldValue);
fieldNumber++;
break;
case 2:
int t = Integer.parseInt(fieldValue);
fieldNumber++;
break;
case 3:
if (fieldValue.equals("overrun"))
overrunCount++;
break;
}
}
fieldBuffer.setLength(0);
}
else {
fieldBuffer.append(c);
}
}
}
lineReaderParse
public void lineReaderParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
String line;
for (int f=0; f<numberOfFiles; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
FileReader frd = new FileReader(fl);
BufferedReader brd = new BufferedReader(frd);
while ((line=brd.readLine())!=null)
doSomethingWithLine(line);
brd.close();
frd.close();
}
}
public final void doSomethingWithLine(String line) throws ParseException {
// Example of what to do for each line
String[] fields = line.split(";");
Date dt = fmt.parse(fields[0]);
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCount++;
}
lineReaderParseParallel
public void lineReaderParseParallel(final String targetDir, final int numberOfFiles, final int degreeOfParalelism) throws IOException, ParseException, InterruptedException {
Thread[] pool = new Thread[degreeOfParalelism];
int batchSize = numberOfFiles / degreeOfParalelism;
for (int b=0; b<degreeOfParalelism; b++) {
pool[b] = new LineReaderParseThread(targetDir, b*batchSize, b*batchSize+b*batchSize);
pool[b].start();
}
for (int b=0; b<degreeOfParalelism; b++)
pool[b].join();
}
class LineReaderParseThread extends Thread {
private String targetDir;
private int fileFrom;
private int fileTo;
private DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private int overrunCounter = 0;
public LineReaderParseThread(String targetDir, int fileFrom, int fileTo) {
this.targetDir = targetDir;
this.fileFrom = fileFrom;
this.fileTo = fileTo;
}
private void doSomethingWithTheLine(String line) throws ParseException {
String[] fields = line.split(DL);
Date dt = fmt.parse(fields[0]);
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCounter++;
}
#Override
public void run() {
String line;
for (int f=fileFrom; f<fileTo; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
try {
FileReader frd = new FileReader(fl);
BufferedReader brd = new BufferedReader(frd);
while ((line=brd.readLine())!=null) {
doSomethingWithTheLine(line);
}
brd.close();
frd.close();
} catch (IOException | ParseException ioe) { }
}
}
}
nioFilesParse
public void nioFilesParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
for (int f=0; f<numberOfFiles; f++) {
Path ph = Paths.get(targetDir+filenamePreffix+String.valueOf(f)+".txt");
Consumer<String> action = new LineConsumer();
Stream<String> lines = Files.lines(ph);
lines.forEach(action);
lines.close();
}
}
class LineConsumer implements Consumer<String> {
#Override
public void accept(String line) {
// What to do for each line
String[] fields = line.split(DL);
if (fields.length>1) {
try {
Date dt = fmt.parse(fields[0]);
}
catch (ParseException e) {
}
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCount++;
}
}
}
nioAsyncParse
public void nioAsyncParse(final String targetDir, final int numberOfFiles, final int numberOfThreads, final int bufferSize) throws IOException, ParseException, InterruptedException {
ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(numberOfThreads);
ConcurrentLinkedQueue<ByteBuffer> byteBuffers = new ConcurrentLinkedQueue<ByteBuffer>();
for (int b=0; b<numberOfThreads; b++)
byteBuffers.add(ByteBuffer.allocate(bufferSize));
for (int f=0; f<numberOfFiles; f++) {
consumerThreads.acquire();
String fileName = targetDir+filenamePreffix+String.valueOf(f)+".txt";
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get(fileName), EnumSet.of(StandardOpenOption.READ), pool);
BufferConsumer consumer = new BufferConsumer(byteBuffers, fileName, bufferSize);
channel.read(consumer.buffer(), 0l, channel, consumer);
}
consumerThreads.acquire(numberOfThreads);
}
class BufferConsumer implements CompletionHandler<Integer, AsynchronousFileChannel> {
private ConcurrentLinkedQueue<ByteBuffer> buffers;
private ByteBuffer bytes;
private String file;
private StringBuffer chars;
private int limit;
private long position;
private DateFormat frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public BufferConsumer(ConcurrentLinkedQueue<ByteBuffer> byteBuffers, String fileName, int bufferSize) {
buffers = byteBuffers;
bytes = buffers.poll();
if (bytes==null)
bytes = ByteBuffer.allocate(bufferSize);
file = fileName;
chars = new StringBuffer(bufferSize);
frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
limit = bufferSize;
position = 0l;
}
public ByteBuffer buffer() {
return bytes;
}
#Override
public synchronized void completed(Integer result, AsynchronousFileChannel channel) {
if (result!=-1) {
bytes.flip();
final int len = bytes.limit();
int i = 0;
try {
for (i = 0; i < len; i++) {
byte by = bytes.get();
if (by=='\n') {
// ***
// The code used to process the line goes here
chars.setLength(0);
}
else {
chars.append((char) by);
}
}
}
catch (Exception x) {
System.out.println(
"Caught exception " + x.getClass().getName() + " " + x.getMessage() +
" i=" + String.valueOf(i) + ", limit=" + String.valueOf(len) +
", position="+String.valueOf(position));
}
if (len==limit) {
bytes.clear();
position += len;
channel.read(bytes, position, channel, this);
}
else {
try {
channel.close();
}
catch (IOException e) {
}
consumerThreads.release();
bytes.clear();
buffers.add(bytes);
}
}
else {
try {
channel.close();
}
catch (IOException e) {
}
consumerThreads.release();
bytes.clear();
buffers.add(bytes);
}
}
#Override
public void failed(Throwable e, AsynchronousFileChannel channel) {
}
};
FULL RUNNABLE IMPLEMENTATION OF ALL CASES
https://github.com/sergiomt/javaiobenchmark/blob/master/FileReadBenchmark.java
Here are the three working and tested methods:
Using BufferedReader
package io;
import java.io.*;
public class ReadFromFile2 {
public static void main(String[] args)throws Exception {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
BufferedReader br = new BufferedReader(new FileReader(file));
String st;
while((st=br.readLine()) != null){
System.out.println(st);
}
}
}
Using Scanner
package io;
import java.io.File;
import java.util.Scanner;
public class ReadFromFileUsingScanner {
public static void main(String[] args) throws Exception {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
Scanner sc = new Scanner(file);
while(sc.hasNextLine()){
System.out.println(sc.nextLine());
}
}
}
Using FileReader
package io;
import java.io.*;
public class ReadingFromFile {
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("C:\\Users\\pankaj\\Desktop\\test.java");
int i;
while ((i=fr.read()) != -1){
System.out.print((char) i);
}
}
}
Read the entire file without a loop using the Scanner class
package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ReadingEntireFileWithoutLoop {
public static void main(String[] args) throws FileNotFoundException {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
Scanner sc = new Scanner(file);
sc.useDelimiter("\\Z");
System.out.println(sc.next());
}
}
The methods within org.apache.commons.io.FileUtils may also be very handy, e.g.:
/**
* Reads the contents of a file line by line to a List
* of Strings using the default encoding for the VM.
*/
static List readLines(File file)
I documented 15 ways to read a file in Java and then tested them for speed with various file sizes - from 1 KB to 1 GB and here are the top three ways to do this:
java.nio.file.Files.readAllBytes()
Tested to work in Java 7, 8, and 9.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class ReadFile_Files_ReadAllBytes {
public static void main(String [] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
File file = new File(fileName);
byte [] fileBytes = Files.readAllBytes(file.toPath());
char singleChar;
for(byte b : fileBytes) {
singleChar = (char) b;
System.out.print(singleChar);
}
}
}
java.io.BufferedReader.readLine()
Tested to work in Java 7, 8, 9.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFile_BufferedReader_ReadLine {
public static void main(String [] args) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
FileReader fileReader = new FileReader(fileName);
try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
String line;
while((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
}
}
java.nio.file.Files.lines()
This was tested to work in Java 8 and 9 but won't work in Java 7 because of the lambda expression requirement.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;
public class ReadFile_Files_Lines {
public static void main(String[] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
File file = new File(fileName);
try (Stream linesStream = Files.lines(file.toPath())) {
linesStream.forEach(line -> {
System.out.println(line);
});
}
}
}
What do you want to do with the text? Is the file small enough to fit into memory? I would try to find the simplest way to handle the file for your needs. The FileUtils library is very handle for this.
for(String line: FileUtils.readLines("my-text-file"))
System.out.println(line);
Below is a one-liner of doing it in the Java 8 way. Assuming text.txt file is in the root of the project directory of the Eclipse.
Files.lines(Paths.get("text.txt")).collect(Collectors.toList());
The most intuitive method is introduced in Java 11 Files.readString
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String args[]) throws IOException {
String content = Files.readString(Paths.get("D:\\sandbox\\mvn\\my-app\\my-app.iml"));
System.out.print(content);
}
}
PHP has this luxury for decades! ☺
The buffered stream classes are much more performant in practice, so much so that the NIO.2 API includes methods that specifically return these stream classes, in part to encourage you always to use buffered streams in your application.
Here is an example:
Path path = Paths.get("/myfolder/myfile.ext");
try (BufferedReader reader = Files.newBufferedReader(path)) {
// Read from the stream
String currentLine = null;
while ((currentLine = reader.readLine()) != null)
//do your code here
} catch (IOException e) {
// Handle file I/O exception...
}
You can replace this code
BufferedReader reader = Files.newBufferedReader(path);
with
BufferedReader br = new BufferedReader(new FileReader("/myfolder/myfile.ext"));
I recommend this article to learn the main uses of Java NIO and IO.
Using BufferedReader:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
BufferedReader br;
try {
br = new BufferedReader(new FileReader("/fileToRead.txt"));
try {
String x;
while ( (x = br.readLine()) != null ) {
// Printing out each line in the file
System.out.println(x);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
catch (FileNotFoundException e) {
System.out.println(e);
e.printStackTrace();
}
This is basically the exact same as Jesus Ramos' answer, except with File instead of FileReader plus iteration to step through the contents of the file.
Scanner in = new Scanner(new File("filename.txt"));
while (in.hasNext()) { // Iterates each line in the file
String line = in.nextLine();
// Do something with line
}
in.close(); // Don't forget to close resource leaks
... throws FileNotFoundException
Probably not as fast as with buffered I/O, but quite terse:
String content;
try (Scanner scanner = new Scanner(textFile).useDelimiter("\\Z")) {
content = scanner.next();
}
The \Z pattern tells the Scanner that the delimiter is EOF.
The most simple way to read data from a file in Java is making use of the File class to read the file and the Scanner class to read the content of the file.
public static void main(String args[])throws Exception
{
File f = new File("input.txt");
takeInputIn2DArray(f);
}
public static void takeInputIn2DArray(File f) throws Exception
{
Scanner s = new Scanner(f);
int a[][] = new int[20][20];
for(int i=0; i<20; i++)
{
for(int j=0; j<20; j++)
{
a[i][j] = s.nextInt();
}
}
}
PS: Don't forget to import java.util.*; for Scanner to work.
You can use readAllLines and the join method to get whole file content in one line:
String str = String.join("\n",Files.readAllLines(Paths.get("e:\\text.txt")));
It uses UTF-8 encoding by default, which reads ASCII data correctly.
Also you can use readAllBytes:
String str = new String(Files.readAllBytes(Paths.get("e:\\text.txt")), StandardCharsets.UTF_8);
I think readAllBytes is faster and more precise, because it does not replace new line with \n and also new line may be \r\n. It is depending on your needs which one is suitable.
I don't see it mentioned yet in the other answers so far. But if "Best" means speed, then the new Java I/O (NIO) might provide the fastest preformance, but not always the easiest to figure out for someone learning.
http://download.oracle.com/javase/tutorial/essential/io/file.html
Guava provides a one-liner for this:
import com.google.common.base.Charsets;
import com.google.common.io.Files;
String contents = Files.toString(filePath, Charsets.UTF_8);
Cactoos give you a declarative one-liner:
new TextOf(new File("a.txt")).asString();
This might not be the exact answer to the question. It's just another way of reading a file where you do not explicitly specify the path to your file in your Java code and instead, you read it as a command-line argument.
With the following code,
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class InputReader{
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s="";
while((s=br.readLine())!=null){
System.out.println(s);
}
}
}
just go ahead and run it with:
java InputReader < input.txt
This would read the contents of the input.txt and print it to the your console.
You can also make your System.out.println() to write to a specific file through the command line as follows:
java InputReader < input.txt > output.txt
This would read from input.txt and write to output.txt.
For JSF-based Maven web applications, just use ClassLoader and the Resources folder to read in any file you want:
Put any file you want to read in the Resources folder.
Put the Apache Commons IO dependency into your POM:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
Use the code below to read it (e.g. below is reading in a .json file):
String metadata = null;
FileInputStream inputStream;
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
inputStream = (FileInputStream) loader
.getResourceAsStream("/metadata.json");
metadata = IOUtils.toString(inputStream);
inputStream.close();
}
catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return metadata;
You can do the same for text files, .properties files, XSD schemas, etc.
try {
File f = new File("filename.txt");
Scanner r = new Scanner(f);
while (r.hasNextLine()) {
String data = r.nextLine();
JOptionPane.showMessageDialog(data);
}
r.close();
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog("Error occurred");
ex.printStackTrace();
}
Use Java kiss if this is about simplicity of structure:
import static kiss.API.*;
class App {
void run() {
String line;
try (Close in = inOpen("file.dat")) {
while ((line = readLine()) != null) {
println(line);
}
}
}
}
import java.util.stream.Stream;
import java.nio.file.*;
import java.io.*;
class ReadFile {
public static void main(String[] args) {
String filename = "Test.txt";
try(Stream<String> stream = Files.lines(Paths.get(filename))) {
stream.forEach(System.out:: println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Just use java 8 Stream.
In case you have a large file you can use Apache Commons IO to process the file iteratively without exhausting the available memory.
try (LineIterator it = FileUtils.lineIterator(theFile, "UTF-8")) {
while (it.hasNext()) {
String line = it.nextLine();
// do something with line
}
}
try (Stream<String> stream = Files.lines(Paths.get(String.valueOf(new File("yourFile.txt"))))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
new File(<path_name>)
Creates a new File instance by converting the given pathname string into an abstract pathname. If the given string is the empty string, then the result is the empty abstract pathname.
Params:
pathname – A pathname string
Throws:
NullPointerException – If the pathname argument is null
Files.lines returns a stream of String
Stream<String> stream = Files.lines(Paths.get(String.valueOf(new File("yourFile.txt"))))
can throw nullPointerExcetion , FileNotFoundException so, keepint it inside try will take care of Exception in runtime
stream.forEach(System.out::println);
This is used to iterate over the stream and print in console
If you have different use case you can provide your custome function to manipulate the stream of lines
My new favorite approach to simply read a whole text file from a BufferedReader input goes:
String text = input.lines().collect(Collectors.joining(System.lineSeparator())));
This will read the whole file by adding new line (lineSeparator) behind each line. Without the separator it would join all lines together as one.
This appears to have existed since Java 8.
For Android developers ending up here (who use Kotlin):
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val text = myFileUrl.readText() // Not recommended for huge files
println(text)
Other solution:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
val lines = file.readLines() // Not recommended for huge files
lines.forEach(::println)
Another good solution which can be used for huge files as well:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
file
.bufferedReader()
.lineSequence()
.forEach(::println)
Or:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
file.useLines { lines ->
lines.forEach(::println)
}
Notes:
The vegetables.txt file should be in your classpath (for example, in src/main/resources directory)
The above solutions all treat the file encodings as UTF-8 by default. You can specify your desired encoding as the argument for the functions.
The above solutions do not need any further action like closing the files or readers. They are automatically taken care of by the Kotlin standard library.

PrintWriter won't stop writing in Java, stuck in infinite loop [duplicate]

It seems there are different ways to read and write data of files in Java.
I want to read ASCII data from a file. What are the possible ways and their differences?
My favorite way to read a small file is to use a BufferedReader and a StringBuilder. It is very simple and to the point (though not particularly effective, but good enough for most cases):
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
String everything = sb.toString();
} finally {
br.close();
}
Some has pointed out that after Java 7 you should use try-with-resources (i.e. auto close) features:
try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
String everything = sb.toString();
}
When I read strings like this, I usually want to do some string handling per line anyways, so then I go for this implementation.
Though if I want to actually just read a file into a String, I always use Apache Commons IO with the class IOUtils.toString() method. You can have a look at the source here:
http://www.docjar.com/html/api/org/apache/commons/io/IOUtils.java.html
FileInputStream inputStream = new FileInputStream("foo.txt");
try {
String everything = IOUtils.toString(inputStream);
} finally {
inputStream.close();
}
And even simpler with Java 7:
try(FileInputStream inputStream = new FileInputStream("foo.txt")) {
String everything = IOUtils.toString(inputStream);
// do something with everything string
}
ASCII is a TEXT file so you would use Readers for reading. Java also supports reading from a binary file using InputStreams. If the files being read are huge then you would want to use a BufferedReader on top of a FileReader to improve read performance.
Go through this article on how to use a Reader
I'd also recommend you download and read this wonderful (yet free) book called Thinking In Java
In Java 7:
new String(Files.readAllBytes(...))
(docs)
or
Files.readAllLines(...)
(docs)
In Java 8:
Files.lines(..).forEach(...)
(docs)
The easiest way is to use the Scanner class in Java and the FileReader object. Simple example:
Scanner in = new Scanner(new FileReader("filename.txt"));
Scanner has several methods for reading in strings, numbers, etc... You can look for more information on this on the Java documentation page.
For example reading the whole content into a String:
StringBuilder sb = new StringBuilder();
while(in.hasNext()) {
sb.append(in.next());
}
in.close();
outString = sb.toString();
Also if you need a specific encoding you can use this instead of FileReader:
new InputStreamReader(new FileInputStream(fileUtf8), StandardCharsets.UTF_8)
Here is a simple solution:
String content = new String(Files.readAllBytes(Paths.get("sample.txt")));
Or to read as list:
List<String> content = Files.readAllLines(Paths.get("sample.txt"))
Here's another way to do it without using external libraries:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public String readFile(String filename)
{
String content = null;
File file = new File(filename); // For example, foo.txt
FileReader reader = null;
try {
reader = new FileReader(file);
char[] chars = new char[(int) file.length()];
reader.read(chars);
content = new String(chars);
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null){
reader.close();
}
}
return content;
}
I had to benchmark the different ways. I shall comment on my findings but, in short, the fastest way is to use a plain old BufferedInputStream over a FileInputStream. If many files must be read then three threads will reduce the total execution time to roughly half, but adding more threads will progressively degrade performance until making it take three times longer to complete with twenty threads than with just one thread.
The assumption is that you must read a file and do something meaningful with its contents. In the examples here is reading lines from a log and count the ones which contain values that exceed a certain threshold. So I am assuming that the one-liner Java 8 Files.lines(Paths.get("/path/to/file.txt")).map(line -> line.split(";")) is not an option.
I tested on Java 1.8, Windows 7 and both SSD and HDD drives.
I wrote six different implementations:
rawParse: Use BufferedInputStream over a FileInputStream and then cut lines reading byte by byte. This outperformed any other single-thread approach, but it may be very inconvenient for non-ASCII files.
lineReaderParse: Use a BufferedReader over a FileReader, read line by line, split lines by calling String.split(). This is approximatedly 20% slower that rawParse.
lineReaderParseParallel: This is the same as lineReaderParse, but it uses several threads. This is the fastest option overall in all cases.
nioFilesParse: Use java.nio.files.Files.lines()
nioAsyncParse: Use an AsynchronousFileChannel with a completion handler and a thread pool.
nioMemoryMappedParse: Use a memory-mapped file. This is really a bad idea yielding execution times at least three times longer than any other implementation.
These are the average times for reading 204 files of 4 MB each on an quad-core i7 and SSD drive. The files are generated on the fly to avoid disk caching.
rawParse 11.10 sec
lineReaderParse 13.86 sec
lineReaderParseParallel 6.00 sec
nioFilesParse 13.52 sec
nioAsyncParse 16.06 sec
nioMemoryMappedParse 37.68 sec
I found a difference smaller than I expected between running on an SSD or an HDD drive being the SSD approximately 15% faster. This may be because the files are generated on an unfragmented HDD and they are read sequentially, therefore the spinning drive can perform nearly as an SSD.
I was surprised by the low performance of the nioAsyncParse implementation. Either I have implemented something in the wrong way or the multi-thread implementation using NIO and a completion handler performs the same (or even worse) than a single-thread implementation with the java.io API. Moreover the asynchronous parse with a CompletionHandler is much longer in lines of code and tricky to implement correctly than a straight implementation on old streams.
Now the six implementations followed by a class containing them all plus a parametrizable main() method that allows to play with the number of files, file size and concurrency degree. Note that the size of the files varies plus minus 20%. This is to avoid any effect due to all the files being of exactly the same size.
rawParse
public void rawParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
overrunCount = 0;
final int dl = (int) ';';
StringBuffer lineBuffer = new StringBuffer(1024);
for (int f=0; f<numberOfFiles; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
FileInputStream fin = new FileInputStream(fl);
BufferedInputStream bin = new BufferedInputStream(fin);
int character;
while((character=bin.read())!=-1) {
if (character==dl) {
// Here is where something is done with each line
doSomethingWithRawLine(lineBuffer.toString());
lineBuffer.setLength(0);
}
else {
lineBuffer.append((char) character);
}
}
bin.close();
fin.close();
}
}
public final void doSomethingWithRawLine(String line) throws ParseException {
// What to do for each line
int fieldNumber = 0;
final int len = line.length();
StringBuffer fieldBuffer = new StringBuffer(256);
for (int charPos=0; charPos<len; charPos++) {
char c = line.charAt(charPos);
if (c==DL0) {
String fieldValue = fieldBuffer.toString();
if (fieldValue.length()>0) {
switch (fieldNumber) {
case 0:
Date dt = fmt.parse(fieldValue);
fieldNumber++;
break;
case 1:
double d = Double.parseDouble(fieldValue);
fieldNumber++;
break;
case 2:
int t = Integer.parseInt(fieldValue);
fieldNumber++;
break;
case 3:
if (fieldValue.equals("overrun"))
overrunCount++;
break;
}
}
fieldBuffer.setLength(0);
}
else {
fieldBuffer.append(c);
}
}
}
lineReaderParse
public void lineReaderParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
String line;
for (int f=0; f<numberOfFiles; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
FileReader frd = new FileReader(fl);
BufferedReader brd = new BufferedReader(frd);
while ((line=brd.readLine())!=null)
doSomethingWithLine(line);
brd.close();
frd.close();
}
}
public final void doSomethingWithLine(String line) throws ParseException {
// Example of what to do for each line
String[] fields = line.split(";");
Date dt = fmt.parse(fields[0]);
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCount++;
}
lineReaderParseParallel
public void lineReaderParseParallel(final String targetDir, final int numberOfFiles, final int degreeOfParalelism) throws IOException, ParseException, InterruptedException {
Thread[] pool = new Thread[degreeOfParalelism];
int batchSize = numberOfFiles / degreeOfParalelism;
for (int b=0; b<degreeOfParalelism; b++) {
pool[b] = new LineReaderParseThread(targetDir, b*batchSize, b*batchSize+b*batchSize);
pool[b].start();
}
for (int b=0; b<degreeOfParalelism; b++)
pool[b].join();
}
class LineReaderParseThread extends Thread {
private String targetDir;
private int fileFrom;
private int fileTo;
private DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private int overrunCounter = 0;
public LineReaderParseThread(String targetDir, int fileFrom, int fileTo) {
this.targetDir = targetDir;
this.fileFrom = fileFrom;
this.fileTo = fileTo;
}
private void doSomethingWithTheLine(String line) throws ParseException {
String[] fields = line.split(DL);
Date dt = fmt.parse(fields[0]);
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCounter++;
}
#Override
public void run() {
String line;
for (int f=fileFrom; f<fileTo; f++) {
File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
try {
FileReader frd = new FileReader(fl);
BufferedReader brd = new BufferedReader(frd);
while ((line=brd.readLine())!=null) {
doSomethingWithTheLine(line);
}
brd.close();
frd.close();
} catch (IOException | ParseException ioe) { }
}
}
}
nioFilesParse
public void nioFilesParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
for (int f=0; f<numberOfFiles; f++) {
Path ph = Paths.get(targetDir+filenamePreffix+String.valueOf(f)+".txt");
Consumer<String> action = new LineConsumer();
Stream<String> lines = Files.lines(ph);
lines.forEach(action);
lines.close();
}
}
class LineConsumer implements Consumer<String> {
#Override
public void accept(String line) {
// What to do for each line
String[] fields = line.split(DL);
if (fields.length>1) {
try {
Date dt = fmt.parse(fields[0]);
}
catch (ParseException e) {
}
double d = Double.parseDouble(fields[1]);
int t = Integer.parseInt(fields[2]);
if (fields[3].equals("overrun"))
overrunCount++;
}
}
}
nioAsyncParse
public void nioAsyncParse(final String targetDir, final int numberOfFiles, final int numberOfThreads, final int bufferSize) throws IOException, ParseException, InterruptedException {
ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(numberOfThreads);
ConcurrentLinkedQueue<ByteBuffer> byteBuffers = new ConcurrentLinkedQueue<ByteBuffer>();
for (int b=0; b<numberOfThreads; b++)
byteBuffers.add(ByteBuffer.allocate(bufferSize));
for (int f=0; f<numberOfFiles; f++) {
consumerThreads.acquire();
String fileName = targetDir+filenamePreffix+String.valueOf(f)+".txt";
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get(fileName), EnumSet.of(StandardOpenOption.READ), pool);
BufferConsumer consumer = new BufferConsumer(byteBuffers, fileName, bufferSize);
channel.read(consumer.buffer(), 0l, channel, consumer);
}
consumerThreads.acquire(numberOfThreads);
}
class BufferConsumer implements CompletionHandler<Integer, AsynchronousFileChannel> {
private ConcurrentLinkedQueue<ByteBuffer> buffers;
private ByteBuffer bytes;
private String file;
private StringBuffer chars;
private int limit;
private long position;
private DateFormat frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public BufferConsumer(ConcurrentLinkedQueue<ByteBuffer> byteBuffers, String fileName, int bufferSize) {
buffers = byteBuffers;
bytes = buffers.poll();
if (bytes==null)
bytes = ByteBuffer.allocate(bufferSize);
file = fileName;
chars = new StringBuffer(bufferSize);
frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
limit = bufferSize;
position = 0l;
}
public ByteBuffer buffer() {
return bytes;
}
#Override
public synchronized void completed(Integer result, AsynchronousFileChannel channel) {
if (result!=-1) {
bytes.flip();
final int len = bytes.limit();
int i = 0;
try {
for (i = 0; i < len; i++) {
byte by = bytes.get();
if (by=='\n') {
// ***
// The code used to process the line goes here
chars.setLength(0);
}
else {
chars.append((char) by);
}
}
}
catch (Exception x) {
System.out.println(
"Caught exception " + x.getClass().getName() + " " + x.getMessage() +
" i=" + String.valueOf(i) + ", limit=" + String.valueOf(len) +
", position="+String.valueOf(position));
}
if (len==limit) {
bytes.clear();
position += len;
channel.read(bytes, position, channel, this);
}
else {
try {
channel.close();
}
catch (IOException e) {
}
consumerThreads.release();
bytes.clear();
buffers.add(bytes);
}
}
else {
try {
channel.close();
}
catch (IOException e) {
}
consumerThreads.release();
bytes.clear();
buffers.add(bytes);
}
}
#Override
public void failed(Throwable e, AsynchronousFileChannel channel) {
}
};
FULL RUNNABLE IMPLEMENTATION OF ALL CASES
https://github.com/sergiomt/javaiobenchmark/blob/master/FileReadBenchmark.java
Here are the three working and tested methods:
Using BufferedReader
package io;
import java.io.*;
public class ReadFromFile2 {
public static void main(String[] args)throws Exception {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
BufferedReader br = new BufferedReader(new FileReader(file));
String st;
while((st=br.readLine()) != null){
System.out.println(st);
}
}
}
Using Scanner
package io;
import java.io.File;
import java.util.Scanner;
public class ReadFromFileUsingScanner {
public static void main(String[] args) throws Exception {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
Scanner sc = new Scanner(file);
while(sc.hasNextLine()){
System.out.println(sc.nextLine());
}
}
}
Using FileReader
package io;
import java.io.*;
public class ReadingFromFile {
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("C:\\Users\\pankaj\\Desktop\\test.java");
int i;
while ((i=fr.read()) != -1){
System.out.print((char) i);
}
}
}
Read the entire file without a loop using the Scanner class
package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ReadingEntireFileWithoutLoop {
public static void main(String[] args) throws FileNotFoundException {
File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
Scanner sc = new Scanner(file);
sc.useDelimiter("\\Z");
System.out.println(sc.next());
}
}
The methods within org.apache.commons.io.FileUtils may also be very handy, e.g.:
/**
* Reads the contents of a file line by line to a List
* of Strings using the default encoding for the VM.
*/
static List readLines(File file)
I documented 15 ways to read a file in Java and then tested them for speed with various file sizes - from 1 KB to 1 GB and here are the top three ways to do this:
java.nio.file.Files.readAllBytes()
Tested to work in Java 7, 8, and 9.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class ReadFile_Files_ReadAllBytes {
public static void main(String [] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
File file = new File(fileName);
byte [] fileBytes = Files.readAllBytes(file.toPath());
char singleChar;
for(byte b : fileBytes) {
singleChar = (char) b;
System.out.print(singleChar);
}
}
}
java.io.BufferedReader.readLine()
Tested to work in Java 7, 8, 9.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFile_BufferedReader_ReadLine {
public static void main(String [] args) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
FileReader fileReader = new FileReader(fileName);
try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
String line;
while((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
}
}
java.nio.file.Files.lines()
This was tested to work in Java 8 and 9 but won't work in Java 7 because of the lambda expression requirement.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;
public class ReadFile_Files_Lines {
public static void main(String[] pArgs) throws IOException {
String fileName = "c:\\temp\\sample-10KB.txt";
File file = new File(fileName);
try (Stream linesStream = Files.lines(file.toPath())) {
linesStream.forEach(line -> {
System.out.println(line);
});
}
}
}
What do you want to do with the text? Is the file small enough to fit into memory? I would try to find the simplest way to handle the file for your needs. The FileUtils library is very handle for this.
for(String line: FileUtils.readLines("my-text-file"))
System.out.println(line);
Below is a one-liner of doing it in the Java 8 way. Assuming text.txt file is in the root of the project directory of the Eclipse.
Files.lines(Paths.get("text.txt")).collect(Collectors.toList());
The most intuitive method is introduced in Java 11 Files.readString
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String args[]) throws IOException {
String content = Files.readString(Paths.get("D:\\sandbox\\mvn\\my-app\\my-app.iml"));
System.out.print(content);
}
}
PHP has this luxury for decades! ☺
The buffered stream classes are much more performant in practice, so much so that the NIO.2 API includes methods that specifically return these stream classes, in part to encourage you always to use buffered streams in your application.
Here is an example:
Path path = Paths.get("/myfolder/myfile.ext");
try (BufferedReader reader = Files.newBufferedReader(path)) {
// Read from the stream
String currentLine = null;
while ((currentLine = reader.readLine()) != null)
//do your code here
} catch (IOException e) {
// Handle file I/O exception...
}
You can replace this code
BufferedReader reader = Files.newBufferedReader(path);
with
BufferedReader br = new BufferedReader(new FileReader("/myfolder/myfile.ext"));
I recommend this article to learn the main uses of Java NIO and IO.
Using BufferedReader:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
BufferedReader br;
try {
br = new BufferedReader(new FileReader("/fileToRead.txt"));
try {
String x;
while ( (x = br.readLine()) != null ) {
// Printing out each line in the file
System.out.println(x);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
catch (FileNotFoundException e) {
System.out.println(e);
e.printStackTrace();
}
This is basically the exact same as Jesus Ramos' answer, except with File instead of FileReader plus iteration to step through the contents of the file.
Scanner in = new Scanner(new File("filename.txt"));
while (in.hasNext()) { // Iterates each line in the file
String line = in.nextLine();
// Do something with line
}
in.close(); // Don't forget to close resource leaks
... throws FileNotFoundException
Probably not as fast as with buffered I/O, but quite terse:
String content;
try (Scanner scanner = new Scanner(textFile).useDelimiter("\\Z")) {
content = scanner.next();
}
The \Z pattern tells the Scanner that the delimiter is EOF.
The most simple way to read data from a file in Java is making use of the File class to read the file and the Scanner class to read the content of the file.
public static void main(String args[])throws Exception
{
File f = new File("input.txt");
takeInputIn2DArray(f);
}
public static void takeInputIn2DArray(File f) throws Exception
{
Scanner s = new Scanner(f);
int a[][] = new int[20][20];
for(int i=0; i<20; i++)
{
for(int j=0; j<20; j++)
{
a[i][j] = s.nextInt();
}
}
}
PS: Don't forget to import java.util.*; for Scanner to work.
You can use readAllLines and the join method to get whole file content in one line:
String str = String.join("\n",Files.readAllLines(Paths.get("e:\\text.txt")));
It uses UTF-8 encoding by default, which reads ASCII data correctly.
Also you can use readAllBytes:
String str = new String(Files.readAllBytes(Paths.get("e:\\text.txt")), StandardCharsets.UTF_8);
I think readAllBytes is faster and more precise, because it does not replace new line with \n and also new line may be \r\n. It is depending on your needs which one is suitable.
I don't see it mentioned yet in the other answers so far. But if "Best" means speed, then the new Java I/O (NIO) might provide the fastest preformance, but not always the easiest to figure out for someone learning.
http://download.oracle.com/javase/tutorial/essential/io/file.html
Guava provides a one-liner for this:
import com.google.common.base.Charsets;
import com.google.common.io.Files;
String contents = Files.toString(filePath, Charsets.UTF_8);
Cactoos give you a declarative one-liner:
new TextOf(new File("a.txt")).asString();
This might not be the exact answer to the question. It's just another way of reading a file where you do not explicitly specify the path to your file in your Java code and instead, you read it as a command-line argument.
With the following code,
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class InputReader{
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s="";
while((s=br.readLine())!=null){
System.out.println(s);
}
}
}
just go ahead and run it with:
java InputReader < input.txt
This would read the contents of the input.txt and print it to the your console.
You can also make your System.out.println() to write to a specific file through the command line as follows:
java InputReader < input.txt > output.txt
This would read from input.txt and write to output.txt.
For JSF-based Maven web applications, just use ClassLoader and the Resources folder to read in any file you want:
Put any file you want to read in the Resources folder.
Put the Apache Commons IO dependency into your POM:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
Use the code below to read it (e.g. below is reading in a .json file):
String metadata = null;
FileInputStream inputStream;
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
inputStream = (FileInputStream) loader
.getResourceAsStream("/metadata.json");
metadata = IOUtils.toString(inputStream);
inputStream.close();
}
catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return metadata;
You can do the same for text files, .properties files, XSD schemas, etc.
try {
File f = new File("filename.txt");
Scanner r = new Scanner(f);
while (r.hasNextLine()) {
String data = r.nextLine();
JOptionPane.showMessageDialog(data);
}
r.close();
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog("Error occurred");
ex.printStackTrace();
}
Use Java kiss if this is about simplicity of structure:
import static kiss.API.*;
class App {
void run() {
String line;
try (Close in = inOpen("file.dat")) {
while ((line = readLine()) != null) {
println(line);
}
}
}
}
import java.util.stream.Stream;
import java.nio.file.*;
import java.io.*;
class ReadFile {
public static void main(String[] args) {
String filename = "Test.txt";
try(Stream<String> stream = Files.lines(Paths.get(filename))) {
stream.forEach(System.out:: println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Just use java 8 Stream.
In case you have a large file you can use Apache Commons IO to process the file iteratively without exhausting the available memory.
try (LineIterator it = FileUtils.lineIterator(theFile, "UTF-8")) {
while (it.hasNext()) {
String line = it.nextLine();
// do something with line
}
}
try (Stream<String> stream = Files.lines(Paths.get(String.valueOf(new File("yourFile.txt"))))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
new File(<path_name>)
Creates a new File instance by converting the given pathname string into an abstract pathname. If the given string is the empty string, then the result is the empty abstract pathname.
Params:
pathname – A pathname string
Throws:
NullPointerException – If the pathname argument is null
Files.lines returns a stream of String
Stream<String> stream = Files.lines(Paths.get(String.valueOf(new File("yourFile.txt"))))
can throw nullPointerExcetion , FileNotFoundException so, keepint it inside try will take care of Exception in runtime
stream.forEach(System.out::println);
This is used to iterate over the stream and print in console
If you have different use case you can provide your custome function to manipulate the stream of lines
My new favorite approach to simply read a whole text file from a BufferedReader input goes:
String text = input.lines().collect(Collectors.joining(System.lineSeparator())));
This will read the whole file by adding new line (lineSeparator) behind each line. Without the separator it would join all lines together as one.
This appears to have existed since Java 8.
For Android developers ending up here (who use Kotlin):
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val text = myFileUrl.readText() // Not recommended for huge files
println(text)
Other solution:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
val lines = file.readLines() // Not recommended for huge files
lines.forEach(::println)
Another good solution which can be used for huge files as well:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
file
.bufferedReader()
.lineSequence()
.forEach(::println)
Or:
val myFileUrl = object{}.javaClass.getResource("/vegetables.txt")
val file = File(myFileUrl.toURI())
file.useLines { lines ->
lines.forEach(::println)
}
Notes:
The vegetables.txt file should be in your classpath (for example, in src/main/resources directory)
The above solutions all treat the file encodings as UTF-8 by default. You can specify your desired encoding as the argument for the functions.
The above solutions do not need any further action like closing the files or readers. They are automatically taken care of by the Kotlin standard library.

Append penultimate line

I'm currently storing the read file in a List<String>, adding my String to the penultimate position of the list, and finally writing my list into the original file.
It works fine because my file is only 20ish lines, but I feel like it would be a really long process with a bigger file.
Hence my question is : Is there a Append-like method to simply choose the position where you want to append your line ?
Just open a java.io.FileOutputStream with append mode and just write your single String.
Use the following constructor of this class is:
FileOutputStream(String name, boolean append)
This is for appending to the end of the file.
You can use java.io.RandomAccessFile for editing anywhere inside a file.
EDIT:
Sample code to achieve the task using RandomAccessFile.
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
public class InsertPenultimateLine {
private static String nl = System.getProperty("line.separator");
private static void usingRAF() throws Exception {
String fileName = "somefile.txt";
String myLine = "blah-blah-blah";
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
List<Byte> bytes = new ArrayList<Byte>();
long len = raf.length();
long index = len-1;
for(;index>0;index--) {
raf.seek(index);
byte b = raf.readByte();
if(b == 10) {
break;
}
bytes.add(0, b);
}
byte[] rawBytes = new byte[bytes.size()];
for(int j=0;j<bytes.size();j++) {
rawBytes[j] = bytes.get(j);
}
String str = new String(rawBytes);
str = myLine + nl + str;
raf.seek(index + 1);
raf.write(str.getBytes());
raf.close();
}
public static void main(String[] args) throws Exception {
usingRAF();
}
}

In java is there a readObject() alternative for RandomAccessFile

I'm trying to save a couple of Strings but I'm currently using ObjectInputStream, which apparently doesn't save my data in a permanent manner. Here is the code which I commented in my project. It saves the string in a temporal manner. Anytime I exit my program, puff the data is gone:
ObjectInputStream FileIn= new ObjectInputStream(new FileInputStream("cars.txt"));
//AND HERE IS THE CODE FOR THE RandomAccessFile VERSION:
RandomAccessFile FileIn = new RandomAccessFile("cars.txt", "rw");
au=(Cars)FileIn.readObject(); //THIS readObject(), is giving me errors
//Cars is a Class
Is there any other alternative that I can use to read RandomAccessFile... Please help and thanks.
Just like FileInputStream, you need to wrap a RandomAccessFile in an ObjectInputStream. i.e. RandomAccessFile doesn't buy you anything.
final RandomAccessFile raf = new RandomAccessFile("file.dat", "r");
ObjectInputStream ois = new ObjectInputStream(new InputStream() {
#Override
public int read(byte[] b, int off, int len) throws IOException {
return raf.read(b, off, len);
}
#Override
public int read() throws IOException {
return raf.read();
}
});
For simple String objects is far easier using plain DataInputStream / DataOutputStream:
package test;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
public class TestFile {
static final String FILE = "/tmp/cars.txt";
public static void main(String[] args) {
try {
List<String> strs = new ArrayList<String>();
strs.add("Audi");
strs.add("Seat");
strs.add("Renault");
saveStrings(strs);
strs = loadStrings();
System.out.println("Read strings: " + strs);
} catch (Exception e) {
}
}
static List<String> loadStrings() throws Exception {
DataInputStream dis = null;
List<String> list = new ArrayList<String>();
try {
dis = new DataInputStream(new FileInputStream(FILE));
while (dis.available() > 0) {
list.add(dis.readUTF());
}
} finally {
if (dis != null)
dis.close();
}
return list;
}
static void saveStrings(List<String> list) throws Exception {
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream(FILE));
for (String str : list) {
dos.writeUTF(str);
}
} finally {
if (dos != null)
dos.close();
}
}
}
If you are asking whether you can use RandomAccessFile to seek around inside an object stream and read objects then the short answer is "no". Serialized object streams are heavily encoded with backwards pointers to previously used objects including previously dumped class definitions, etc..
Serialization stream specification with some format details.
Here's some more details on the serialization format.
Discussion about random writing from Java docs.
We had a similar requirement and wrote some code which closes and re-opens the serialized stream once and a while and recorded the positions of these break points. This didn't give us the ability to read a particular object but it did give us the ability to append to serialized stream and to skip over a particular portion of the file -- skip to the next break.
Well you have to invoke, writeObject() instead of readObject() which is actually to read from disk to memory, and of course when the program ends, so does the memory used by that program.

How to get file content in java?

to get the content of a txt file I usually use a scanner and iterate over each line to get the content:
Scanner sc = new Scanner(new File("file.txt"));
while(sc.hasNextLine()){
String str = sc.nextLine();
}
Does the java api provide a way to get the content with one line of code like:
String content = FileUtils.readFileToString(new File("file.txt"))
Not the built-in API - but Guava does, amongst its other treasures. (It's a fabulous library.)
String content = Files.toString(new File("file.txt"), Charsets.UTF_8);
There are similar methods for reading any Readable, or loading the entire contents of a binary file as a byte array, or reading a file into a list of strings, etc.
Note that this method is now deprecated. The new equivalent is:
String content = Files.asCharSource(new File("file.txt"), Charsets.UTF_8).read();
With Java 7 there is an API along those lines.
Files.readAllLines(Path path, Charset cs)
commons-io has:
IOUtils.toString(new FileReader("file.txt"), "utf-8");
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public static void main(String[] args) throws IOException {
String content = Files.readString(Paths.get("foo"));
}
From https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#readString(java.nio.file.Path)
You could use the FileReader class together with the BufferedReader to read the text file.
File fileToRead = new File("file.txt");
try( FileReader fileStream = new FileReader( fileToRead );
BufferedReader bufferedReader = new BufferedReader( fileStream ) ) {
String line = null;
while( (line = bufferedReader.readLine()) != null ) {
//do something with line
}
} catch ( FileNotFoundException ex ) {
//exception Handling
} catch ( IOException ex ) {
//exception Handling
}
After a bit of testing, I find BufferedReader and Scanner both problematic under various circumstances (the former often fails to detect new lines and the latter often strips spaces, for instance, from a JSON string exported by org.json library). There are other methods available but the problem is they are only supported after certain Java versions (which is bad for an Android developer, for example) and you might not want to use Guava or Apache commons library just for a single purpose like this. Hence, my solution is to read the whole file as bytes and convert it to string. The code below are taken from one of my hobby projects:
/**
* Get byte array from an InputStream most efficiently.
* Taken from sun.misc.IOUtils
* #param is InputStream
* #param length Length of the buffer, -1 to read the whole stream
* #param readAll Whether to read the whole stream
* #return Desired byte array
* #throws IOException If maximum capacity exceeded.
*/
public static byte[] readFully(InputStream is, int length, boolean readAll)
throws IOException {
byte[] output = {};
if (length == -1) length = Integer.MAX_VALUE;
int pos = 0;
while (pos < length) {
int bytesToRead;
if (pos >= output.length) {
bytesToRead = Math.min(length - pos, output.length + 1024);
if (output.length < pos + bytesToRead) {
output = Arrays.copyOf(output, pos + bytesToRead);
}
} else {
bytesToRead = output.length - pos;
}
int cc = is.read(output, pos, bytesToRead);
if (cc < 0) {
if (readAll && length != Integer.MAX_VALUE) {
throw new EOFException("Detect premature EOF");
} else {
if (output.length != pos) {
output = Arrays.copyOf(output, pos);
}
break;
}
}
pos += cc;
}
return output;
}
/**
* Read the full content of a file.
* #param file The file to be read
* #param emptyValue Empty value if no content has found
* #return File content as string
*/
#NonNull
public static String getFileContent(#NonNull File file, #NonNull String emptyValue) {
if (file.isDirectory()) return emptyValue;
try {
return new String(readFully(new FileInputStream(file), -1, true), Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
return emptyValue;
}
}
You can simply use getFileContent(file, "") to read the content of a file.

Categories