I wish to read and write some files line-by-line.
My first thoughts were to use BufferedReader and BufferedWriter but my gotcha is that I need to know how far through the files I am.
When reading I would like to who how far through processing the file I am and I would like it to be accurate, so after every readLine() I am expecting the position to update, this is complicated by the fact that there is a buffer involved.
Same applies to writing, I need to get the position accurately before and after writing a line. I'm guessing this isn't as difficult as my understanding is that a BufferedWriter flushes after every newline char anyways, so given that I am writing lines, I could just write straight to the channel.
Before I reinvent the wheel here, or use some dodgy reflection, is there anything in the JDK that can help me out here?
EDIT: Just to clear things up, I am looking for byte positions.
It wasn't entirely clear based on your question what you mean by "how far through processing the file I am". Does that mean line number or byte position?
If it means line number, there are really two options:
A) Keep a track of the line numbers manually. Shouldn't be that hard. Just increment a counter for every line.
B) Use java.io.LineNumberReader as suggested by #Jesper
If it means byte position, increment the byte position counter by each line length and don't forget to include the newlines in the byte position as well.
When writing, there is no LineNumberWriter so you need to keep track of the counters manually. Shouldn't be hard.
see below example for reading file using RandomAccessFile
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Timer;
import java.util.TimerTask;
public class ConsoleReader {
/**
* #param args
*/
public static void main(String[] args) {
File file = new File("fileName");
try {
RandomAccessFile r = new RandomAccessFile(file, "r");
//First time read
String str = null;
while((str = r.readLine()) != null) {
System.out.println(str);
}
r.seek(r.getFilePointer());
startTimer(r);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void startTimer(final RandomAccessFile r) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
String str = null;
try {
while((str = r.readLine()) != null) {
System.out.println(str);
}
r.seek(r.getFilePointer());
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, 1000);
}
}
Related
I have written the following code for a lab assignment, however, my professor wants me to break my main method down into other methods and call those methods in the main method. I tried creating methods for creating the input and output streams, and I tried to create a method for the actual writing of the reverse file but I am getting no where. Can someone point me in the right direction? do I need to create another class that I will instantiate and call these methods on? I'm new to java, so any help is appreciated!
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/**
* Reads lines of text from a file named testclass.txt and writes them to a file
* named output.txt in reverse order.
*/
public class ReverseFile {
public static void main(String[] args) {
Scanner fileRead; // input stream for reading text file.
PrintWriter fileWrite; // output stream for writing text file.
ArrayList<String> fileLines; // An ArrayList for holding the lines of the text file.
fileLines = new ArrayList<String>();
try { // Create the input stream.
fileRead = new Scanner(new File("testclass.txt"));
}
catch (FileNotFoundException e) {
System.out.println("Can't find text file");
return; // End the program by returning from main().
}
try { // Create the output stream.
fileWrite = new PrintWriter(new FileWriter("output.txt"));
}
catch (IOException e) {
System.out.println("Can't open file output.txt");
System.out.println("Error: " + e);
fileRead.close(); // Close the input file.
return; // End the program.
}
while ( fileRead.hasNextLine() ) { // Read until end-of-file.
String textLine = fileRead.nextLine();
System.out.println(textLine);
fileLines.add(textLine);
}
// Output the text in reverse order.
for (int i = fileLines.size()-1; i >= 0; i--)
fileWrite.println(fileLines.get(i));
//System.out.println(reverseLines);
System.out.println("Done, check output.txt");
fileRead.close();
fileWrite.close();
} // end of main()
}
Ideally each method should do one thing only and have a name that makes it clear what that one thing is.
My suggestion is that your main method should look something like:
public static void main(String[] args) {
List<String> lines = createAndReadInputStream();
reverse(lines);
createAndWriteOutputStream(lines);
}
That makes it perfectly clear to the reader exactly what the method does and all implementation details are in other methods.
Then do the same for the next method:
private List<String> createAndReadInputStream() {
Scanner inputScanner = createInputStream();
return scanToLines(inputScanner);
}
And so on. If correctly structured your class variables all become locally scoped variables and your code is straightforward and easy to read. You'll also find you need far fewer comments to explain what's happening: the names of the methods are generally enough on their own.
If you are interested in learning more about why your professor asked for this, and how to go about doing it, I highly recommend the book "Clean Code" by Robert Martin. The software development team of which I am a part (80 people in 11 agile teams) adopted it several years ago and the quality, readability and maintainability of our code has improved out of sight. While it takes some getting used to it's worth the effort. In my view the old adage that more code means more bugs is just completely false - as long as the extra code is there for readability, testability, maintainability then it means fewer bugs not more.
Here is an example. Move this section of code:
try { // Create the input stream.
fileRead = new Scanner(new File("testclass.txt"));
}
catch (FileNotFoundException e) {
System.out.println("Can't find text file");
return; // End the program by returning from main().
}
into a new private method called createInputStream within the ReverseFile class. Call the new member from the point in the code where you removed the section. Don't forget to return "fileRead" from the method.
What about this:
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/** * Reads lines of text from a file named testclass.txt and writes them to a file * named output.txt in reverse order. */
public class ReverseFile {
public static ArrayList<String> readFile(String fileName) {
Scanner fileRead; // Scanner for reading text file.
// Try to open file for reading
try {
fileRead = new Scanner(new File(fileName));
// On failure -> print message & return null
} catch (FileNotFoundException e) {
System.out.println("Can't find text file");
return null;
}
// Create ArrayList for readed lines
ArrayList<String> fileLines = new ArrayList<String>();
// Read file line-by-line until end-of-file.
while ( fileRead.hasNextLine() ) {
String textLine = fileRead.nextLine(); // Read next line
System.out.println(textLine); // Print line to terminal
fileLines.add(textLine); // Add line to ArrayList
}
// Close scanner -> will close allso opened file
fileRead.close();
// Return loaded lines
return fileLines;
}
public static void createReversedFile(String filename, ArrayList<String> fileLines) {
PrintWriter fileWrite; // PrintWriter for writing text file.
// Try to open file for writing
try {
fileWrite = new PrintWriter(new FileWriter(filename));
// On failure -> print message & return
} catch (IOException e) {
System.out.println("Can't open file output.txt");
System.out.println("Error: " + e);
fileRead.close(); // Close the input file.
return;
}
// Output the text in reverse order
for (int i = fileLines.size()-1; i >= 0; i--) {
fileWrite.println(fileLines.get(i));
//System.out.println(reverseLines);
}
System.out.println("Done, check " + filename);
// Close PrintWriter -> will close allso opened file
fileWrite.close();
}
public static void main(String[] args) {
// Read file & store it's lines into ArrayList
ArrayList<String> fileLines = readFile("testclass.txt");
// If file was not loaded due to FileNotFoundException exception -> exit
if (fileLines == null) {
return;
}
// Create reversed output.txt file
createReversedFile("output.txt", fileLines);
}
}
i had a input file having 45311 instance. after applying my programing task. when i m write it back in new file it actually write 43371 instance only.it is run successfully but where are my remaining instances.
package kmean;
//package greenblocks.statistics;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import weka.clusterers.SimpleKMeans;
import weka.core.Instances;
/**
*
* #author admin
*/
public class Kmean {
public static BufferedReader readDataFile(String filename) {
BufferedReader inputReader = null;
try {
inputReader = new BufferedReader(new FileReader(filename));
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + filename);
}
return inputReader;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException, Exception {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("perturbed1.csv"));
}
catch (IOException e) {
}
SimpleKMeans kmeans = new SimpleKMeans();
kmeans.setSeed(10);
//important parameter to set: preserver order, number of cluster.
kmeans.setPreserveInstancesOrder(true);
kmeans.setNumClusters(5);
BufferedReader datafile = readDataFile("elecNormNew.arff");
// BufferedReader datafile = readDataFile("perturbed.csv");
Instances data = new Instances(datafile);
kmeans.buildClusterer(data);
// This array returns the cluster number (starting with 0) for each instance
// The array has as many elements as the number of instances
int[] assignments = kmeans.getAssignments();
StringBuilder sb = new StringBuilder();
int i=0;
for(int clusterNum : assignments) {
// System.out.printf("Instance %d -> Cluster %d \n", i, clusterNum);
sb.append(i);
sb.append(";");
sb.append(clusterNum);
sb.append("\n");
//System.out.printf("\n");
i++;
}
System.out.println(sb.toString());
writer.write(sb.toString()+"\n");
// TODO code application logic here
}
}
The neat fact about buffered file writers are, that they take your input and keep it, until the buffer is full. This reduces the i/o operations. At best one write operation fits into one hdd write buffer so the operating system take the whole buffer as one i/o command. The downside is that if at the end if you do not flush() the buffer, the rest of the content will not be written to disk. If you call close() any pending bytes will be written and the resources be freed. In java 7 and above you can use the autoclosing feature by just opening the stream in your try statement:
try(Inputstream is = new ...) {
If you may have any data to write after your code, you can use .flush() to ensure the data is written.
The buffer size is set by default to 8k, but this may wary from jre and version.
You should call writer.close() at the end after writing all data.
insted of writer.write(sb.toString()+"\n");
try writer.write(sb.toString()+writer.newLine());
and finish your writig progress with a
writer.flush();
writer.close();
had some problems myself with "\n" maby thats the problem.
I'm stuck on writing to a specific line using File, BufferedReader, & BufferedWriter.
What I'm trying to achieve is getting my text files total line count (-3) & writing to that line.
Currently it just erases the whole file & nothing is written.
Image of what I'm doing:
(In Image) line 25 is blank & 26 doesn't contain a doc. it contains "}"
& My code:
package com.tests.writer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JFrame;
public class NewWriter {
static Integer tableCount;
static File file;
static FileWriter fw;
static FileReader fr;
static BufferedWriter bw;
static BufferedReader br;
public static void main(String[] args) {
JFrame frame = new JFrame("Test {New Writer}");
frame.setBounds(500, 500, 500, 500);
frame.setVisible(true);
frame.setResizable(false);
frame.setAutoRequestFocus(true);
try {
startApplication();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void startApplication () throws IOException {
writeToFile ();
}
private static void writeToFile () throws IOException {
Integer lineTCount = 0;
file = new File("Tables.txt");
if (file.exists()) {
fw = new FileWriter(file.getAbsolutePath(), true);
fr = new FileReader(file.getAbsolutePath());
bw = new BufferedWriter(fw);
br = new BufferedReader(fr);
for (String line = br.readLine(); line != null; line = br.readLine()) {
lineTCount++;
System.out.println(lineTCount);
}
System.out.println(lineTCount);
bw.write("Test Text to insert");
System.out.println("done");
System.exit(0);
} else {
file.createNewFile();
System.out.println("New File = " + file.toString());
writeToFile();
}
}
}
If there is an easier way of doing this, I'm open to all idea's as I'm still not familiar with Java.io
& If anyone can tell me this is correct. I may have to add the whole file to a list, add the new text then re-write the file.
There are a few things you did a little bit off.
First and foremost; the reason your file is returning empty is because you aren't calling
bw.close();
After you have finished writing to the file.
I don't know of any way to write to a specific part of a file. I don't think there is one.
What you'll have to do is re-write the file. Basically, make a temporary copy, then write all the lines just as before, except for the third-to-last one.
See this question:
I want to open a text file and edit a specific line in java
(I don't know how to mark questions as duplicate)
BufferedWriter's 'write(String s, int off, int len)' method actually states
Writes a portion of a String.
Note; that is NOT 'writes to a specific location in the file'.
EDIT
I wouldn't use System.exit(0);. That tells your OS 'This execution failed'. You should just let your program close itself normally by excluding this line.
See: When to use system.exit(0)?
The bw pointer does not move when you loop through your file. Use this to write to a specific location in your file. Also why are there two variables lineCount & lineTCount
public void write(String s,
int off,
int len)
throws IOException
Ref: http://docs.oracle.com/javase/7/docs/api/java/io/BufferedWriter.html#write(java.lang.String,%20int,%20int)
EDIT: you are right, my bad. You will have to read the previous contents -> make the changes that you want and rewrite the whole file again.
I want to merge huge files containing strings into one file and tried to use nio2. I do not want to load the whole file into memory, so I tried it with BufferedReader:
public void mergeFiles(filesToBeMerged) throws IOException{
Path mergedFile = Paths.get("mergedFile");
Files.createFile(mergedFile);
List<Path> _filesToBeMerged = filesToBeMerged;
try (BufferedWriter writer = Files.newBufferedWriter(mergedFile,StandardOpenOption.APPEND)) {
for (Path file : _filesToBeMerged) {
// this does not work as write()-method does not accept a BufferedReader
writer.append(Files.newBufferedReader(file));
}
} catch (IOException e) {
System.err.println(e);
}
}
I tried it with this, this works, hower, the format of the strings (e.g. new lines, etc is not copied to the merged file):
...
try (BufferedWriter writer = Files.newBufferedWriter(mergedFile,StandardOpenOption.APPEND)) {
for (Path file : _filesToBeMerged) {
// writer.write(Files.newBufferedReader(file));
String line = null;
BufferedReader reader = Files.newBufferedReader(file);
while ((line = reader.readLine()) != null) {
writer.append(line);
writer.append(System.lineSeparator());
}
reader.close();
}
} catch (IOException e) {
System.err.println(e);
}
...
How can I merge huge Files with NIO2 without loading the whole file into memory?
If you want to merge two or more files efficiently you should ask yourself, why on earth are you using char based Reader and Writer to perform that task.
By using these classes you are performing a conversion of the file’s bytes to characters from the system’s default encoding to unicode and back from unicode to the system’s default encoding. This means the program has to perform two data conversion on the entire files.
And, by the way, BufferedReader and BufferedWriter are by no means NIO2 artifacts. These classes exists since the very first version of Java.
When you are using byte-wise copying via real NIO functions, the files can be transferred without being touched by the Java application, in the best case the transfer will be performed directly in the file system’s buffer:
import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
public class MergeFiles
{
public static void main(String[] arg) throws IOException {
if(arg.length<2) {
System.err.println("Syntax: infiles... outfile");
System.exit(1);
}
Path outFile=Paths.get(arg[arg.length-1]);
System.out.println("TO "+outFile);
try(FileChannel out=FileChannel.open(outFile, CREATE, WRITE)) {
for(int ix=0, n=arg.length-1; ix<n; ix++) {
Path inFile=Paths.get(arg[ix]);
System.out.println(inFile+"...");
try(FileChannel in=FileChannel.open(inFile, READ)) {
for(long p=0, l=in.size(); p<l; )
p+=in.transferTo(p, l-p, out);
}
}
}
System.out.println("DONE.");
}
}
With
Files.newBufferedReader(file).readLine()
you create a new Buffer everytime and it gets always reset in the first line.
Replace with
BufferedReader reader = Files.newBufferedReader(file);
while ((line = reader.readLine()) != null) {
writer.write(line);
}
and .close() the reader when done.
readLine() does not yield the line ending ("\n" or "\r\n"). That was the error.
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.write("\r\n"); // Windows
}
You might also disregard this filtering of (possibly different) line endings, and use
try (OutputStream out = new FileOutputStream(file);
for (Path source : filesToBeMerged) {
Files.copy(path, out);
out.write("\r\n".getBytes(StandardCharsets.US_ASCII));
}
}
This writes a newline explicitly, in the case that the last line does not end with a line break.
There might still be a problem with the optional, ugly Unicode BOM character to mark the text as UTF-8/UTF-16LE/UTF-16BE at the beginning of the file.
I need to have this file print to an array, not to screen.And yes, I MUST use an array - School Project - I'm very new to java so any help is appreciated. Any ideas? thanks
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class HangmanProject
{
public static void main(String[] args) throws FileNotFoundException
{
String scoreKeeper; // to keep track of score
int guessesLeft; // to keep track of guesses remaining
String wordList[]; // array to store words
Scanner keyboard = new Scanner(System.in); // to read user's input
System.out.println("Welcome to Hangman Project!");
// Create a scanner to read the secret words file
Scanner wordScan = null;
try {
wordScan = new Scanner(new BufferedReader(new FileReader("words.txt")));
while (wordScan.hasNext()) {
System.out.println(wordScan.next());
}
} finally {
if (wordScan != null) {
wordScan.close();
}
}
}
}
Nick, you just gave us the final piece of the puzzle. If you know the number of lines you will be reading, you can simply define an array of that length before you read the file
Something like...
String[] wordArray = new String[10];
int index = 0;
String word = null; // word to be read from file...
// Use buffered reader to read each line...
wordArray[index] = word;
index++;
Now that example's not going to mean much to be honest, so I did these two examples
The first one uses the concept suggested by Alex, which allows you to read an unknown number of lines from the file.
The only trip up is if the lines are separated by more the one line feed (ie there is a extra line between words)
public static void readUnknownWords() {
// Reference to the words file
File words = new File("Words.txt");
// Use a StringBuilder to buffer the content as it's read from the file
StringBuilder sb = new StringBuilder(128);
BufferedReader reader = null;
try {
// Create the reader. A File reader would be just as fine in this
// example, but hay ;)
reader = new BufferedReader(new FileReader(words));
// The read buffer to use to read data into
char[] buffer = new char[1024];
int bytesRead = -1;
// Read the file to we get to the end
while ((bytesRead = reader.read(buffer)) != -1) {
// Append the results to the string builder
sb.append(buffer, 0, bytesRead);
}
// Split the string builder into individal words by the line break
String[] wordArray = sb.toString().split("\n");
System.out.println("Read " + wordArray.length + " words");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
The second demonstrates how to read the words into an array of known length. This is probably closer to the what you actually want
public static void readKnownWords()
// This is just the same as the previous example, except we
// know in advance the number of lines we will be reading
File words = new File("Words.txt");
BufferedReader reader = null;
try {
// Create the word array of a known quantity
// The quantity value could be defined as a constant
// ie public static final int WORD_COUNT = 10;
String[] wordArray = new String[10];
reader = new BufferedReader(new FileReader(words));
// Instead of reading to a char buffer, we are
// going to take the easy route and read each line
// straight into a String
String text = null;
// The current array index
int index = 0;
// Read the file till we reach the end
// ps- my file had lots more words, so I put a limit
// in the loop to prevent index out of bounds exceptions
while ((text = reader.readLine()) != null && index < 10) {
wordArray[index] = text;
index++;
}
System.out.println("Read " + wordArray.length + " words");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
If you find either of these useful, I would appropriate it you would give me a small up-vote and check Alex's answer as correct, as it's his idea that I've adapted.
Now, if you're really paranoid about which line break to use, you can find the values used by the system via the System.getProperties().getProperty("line.separator") value.
Do you need more help with the reading the file, or getting the String to a parsed array? If you can read the file into a String, simply do:
String[] words = readString.split("\n");
That will split the string at each line break, so assuming this is your text file:
Word1
Word2
Word3
words will be: {word1, word2, word3}
If the words you are reading are stored in each line of the file, you can use the hasNextLine() and nextLine() to read the text one line at a time. Using the next() will also work, since you just need to throw one word in the array, but nextLine() is usually always preferred.
As for only using an array, you have two options:
You either declare a large array, the size of whom you are sure will never be less than the total amount of words;
You go through the file twice, the first time you read the amount of elements, then you initialize the array depending on that value and then, go through it a second time while adding the string as you go by.
It is usually recommended to use a dynamic collection such as an ArrayList(). You can then use the toArray() method to turnt he list into an array.