Java code optimization, replacing all chars in a file - java

I have tried doing it like this:
import java.io.*;
public class ConvertChar {
public static void main(String args[]) {
Long now = System.nanoTime();
String nomCompletFichier = "C:\\Users\\aahamed\\Desktop\\test\\test.xml";
Convert(nomCompletFichier);
Long inter = System.nanoTime() - now;
System.out.println(inter);
}
public static void Convert(String nomCompletFichier) {
FileWriter writer = null;
BufferedReader reader = null;
try {
File file = new File(nomCompletFichier);
reader = new BufferedReader(new FileReader(file));
String oldtext = "";
while (reader.ready()) {
oldtext += reader.readLine() + "\n";
}
reader.close();
// replace a word in a file
// String newtext = oldtext.replaceAll("drink", "Love");
// To replace a line in a file
String newtext = oldtext.replaceAll("&(?!amp;)", "&");
writer = new FileWriter(file);
writer.write(newtext);
writer.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
However the code above takes more time to execute than creating two different files:
import java.io.*;
public class ConvertChar {
public static void main(String args[]) {
Long now = System.nanoTime();
String nomCompletFichier = "C:\\Users\\aahamed\\Desktop\\test\\test.xml";
Convert(nomCompletFichier);
Long inter = System.nanoTime() - now;
System.out.println(inter);
}
private static void Convert(String nomCompletFichier) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
File file = new File(nomCompletFichier);
File tempFile = File.createTempFile("buffer", ".tmp");
bw = new BufferedWriter(new FileWriter(tempFile, true));
br = new BufferedReader(new FileReader(file));
while (br.ready()) {
bw.write(br.readLine().replaceAll("&(?!amp;)", "&") + "\n");
}
bw.close();
br.close();
file.delete();
tempFile.renameTo(file);
} catch (IOException e) {
// writeLog("Erreur lors de la conversion des caractères : " + e.getMessage(), 0);
} finally {
try {
bw.close();
} catch (Exception ignore) {
}
try {
br.close();
} catch (Exception ignore) {
}
}
}
}
Is there any way to do the 2nd code without creating a temp file and reducing the execution time? I am doing a code optimization.

The main reason why your first program is slow is probably that it's building up the string oldtext incrementally. The problem with that is that each time you add another line to it it may need to make a copy of it. Since each copy takes time roughly proportional to the length of the string being copied, your execution time will scale like the square of the size of your input file.
You can check whether this is your problem by trying with files of different lengths and seeing how the runtime depends on the file size.
If so, one easy way to get around the problem is Java's StringBuilder class which is intended for exactly this task: building up a large string incrementally.

The main culprit in your first example is that you're building oldtext inefficiently using String concatenations, as explained here. This allocates a new string for every concatenation. Java provides you StringBuilder for building strings:
StringBuilder builder = new StringBuilder;
while(reader.ready()){
builder.append(reader.readLine());
builder.append("\n");
}
String oldtext = builder.toString();
You can also do the replacement when you're building your text in StringBuilder. Another problem with your code is that you shouldn't use ready() to check if there is some content left in the file - check the result of readLine(). Finally, closing the stream should be in a finally or try-with-resources block. The result could look like this:
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line = reader.readLine();
while (line != null) {
builder.append(line.replaceAll("&(?!amp;)", "&"));
builder.append('\n');
line = reader.readLine();
}
}
String newText = builder.toString();
Writing to a temporary file is a good solution too, though. The amount of I/O, which is the slowest to handle, is the same in both cases - read the full content once, write result once.

Related

Simplest way to concatenate multi lines of text in java through File Handling

I tried concatenating 2 lines of text in a given text file and printing the output to the console. My code is very complicated, is there a simpler method to achieve this by using FileHandling basic concepts ?
import java.io.*;
public class ConcatText{
public static void main(String[] args){
BufferedReader br = null;
try{
String currentLine;
br = new BufferedReader(new FileReader("C:\\Users\\123\\Documents\\CS105\\FileHandling\\concat.file.text"));
StringBuffer text1 = new StringBuffer (br.readLine());
StringBuffer text2 = new StringBuffer(br.readLine());
text1.append(text2);
String str = text1.toString();
str = str.trim();
String array[] = str.split(" ");
StringBuffer result = new StringBuffer();
for(int i=0; i<array.length; i++) {
result.append(array[i]);
}
System.out.println(result);
}
catch(IOException e){
e.printStackTrace();
}finally{
try{
if(br != null){
br.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
The text file is as follows :
GTAGCTAGCTAGC
AGCCACGTA
the output should be as follows (concatenation of the text file Strings) :
GTAGCTAGCTAGCAGCCACGTA
If you are using java 8 or newer, the simplest way would be:
List<String> lines = Files.readAllLines(Paths.get(filePath));
String result = String.join("", lines);
If you are using java 7, at least you can use try with resources to reduce the clutter in the code, like this:
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
StringBuffer text1 = new StringBuffer (br.readLine());
StringBuffer text2 = new StringBuffer(br.readLine());
// ...
}catch(IOException e){
e.printStackTrace();
}
This way, resources will be autoclosed and you don't need to call br.close().
Short answer, there is:
public static void main(String[] args) {
//this is called try-with-resources, it handles closing the resources for you
try (BufferedReader reader = new BufferedReader(...)) {
StringBuilder stringBuilder = new StringBuilder();
String line = reader.readLine();
//readLine() will return null when there are no more lines
while (line != null) {
//replace any spaces with empty string
//first argument is regex matching any empty spaces, second is replacement
line = line.replaceAll("\\s+", "");
//append the current line
stringBuilder.append(line);
//read the next line, will be null when there are no more
line = reader.readLine();
}
System.out.println(stringBuilder);
} catch (IOException exc) {
exc.printStackTrace();
}
}
First of all read on try with resources, when you are using it you don't need to close manually resources(files, streams, etc.), it will do it for you. This for example.
You don't need to wrap read lines in StringBuffer, you don't get anything out of it in this case.
Also read about the methods provided by String class starting with the java doc - documentation.

How to use conditional statement or contains method in a text file?

I have a java program that can read multiple files and replace values accordingly. However, I am struggling to apply a condition to it and apply the changes only when a certain condition is met. For example, if the file contains this specific character ':20:' then apply the changes otherwise leave the text file as it is.
The problem here is, since I don't have fields to look for to apply the condition accordingly I don't know how these can be applied to such a text file which contains just data like : (12345555555) 233344 100 :20:aaa.
I also looked at using the contains() method to look into the file to find the value I want then apply the changes but couldn't make it work.
public class TextFm
{
public static void main(String[] args)
{
File folder = new File("C:\\tmp");
File[] listOfFiles = folder.listFiles();
for(File file : listOfFiles)
{
replaceText(file);
}
}
public static void replaceText(File file)
{
try
{
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = "", oldtext = "";
while ((line = reader.readLine()) != null)
{
oldtext = oldtext + line + System.lineSeparator();
}
reader.close();
String replacedtext = oldtext.replaceAll("100", "200");
FileWriter writer = new FileWriter(file);
writer.write(replacedtext);
writer.close();
System.out.println("Done");
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
Using contains() method will work fine in this case. You can do that like this:
String line = "", oldtext = "";
while ((line = reader.readLine()) != null)
{
oldtext = oldtext + line + System.lineSeparator();
}
reader.close();
if(oldtext.contains(":20:")) {
String replacedtext = oldtext.replaceAll("100", "200");
FileWriter writer = new FileWriter(file);
writer.write(replacedtext);
writer.close();
System.out.println("Done");
}
public static void replaceText(File file)
{
try
{
Charset charset = Charsets.defaulCharset();
String oldText = new String(Files.readAllBytes(file.toPath()),
charset);
if (!oldText.contains(":20")) {
return;
}
if (!oldText.matches("(?s).*you (idiot|unspeakable).*")) {
return;
}
String replacedtext = oldtext.replace("100", "200");
replacedtext = replacedtext .replaceAll("\\d", "X"); // Digit X-ed out
if (!replacedText.equals(oldText)) {
Files.write(file.toPath(), replacedText.getBytes(charset));
}
System.out.println("Done");
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
For speed one should not collect the file contents with a String and +. Better use a StringBuilder, or the very nice Files class.
Finding text goes either by contains or by a regular expression match.
Replacement can be done by either too.
The replace, replaceFirst and replaceAll methods return the original string when nothing could be replaced.
Regex (?s) lets . (=any char) also match line breaks.

Writing to File duplicating data the second time JAVA

I'm creating a program to remove doctors from an arrayList that is utilising a queue. This works the first time perfectly however, the second time it's duplicating the data inside the text file. How can I solve this?
/**
*
* #throws Exception
*/
public void writeArrayListToFile() throws Exception {
String path = "src/assignment1com327ccab/DoctorRecordsFile.txt";
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(path));
BufferedWriter br = new BufferedWriter(os);
PrintWriter out = new PrintWriter(br);
DoctorNode temp; //create a temporary doctorNode object
temp = end; //temp is equal to the end of the queue
//try this while temp is not equal to null (queue is not empty)
StringBuilder doctor = new StringBuilder();
while (temp != null) {
{
doctor.append(temp.toStringFile());
doctor.append("\n");
//temp is equal to temp.getNext doctor to get the next doctor to count
temp = temp.getNext();
}
}
System.out.println("Finished list");
System.out.println("Doctors is : " + doctor.toString());
out.println(doctor.toString());
System.out.println("Done");
br.newLine();
br.close();
}
This is not 100% solution but I think it will give you the right directions. I don't want to do 100% work for you :)
In my comment I said
Read file content
Store it in variable
Remove file
Remove doctors from variable
Write variables to new file
So, to read file content we would use something file this (if it's txt file):
public static String read(File file) throws FileNotFoundException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file.getAbsoluteFile()));
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
if (line != null) sb.append(System.lineSeparator());
}
String everything = sb.toString();
return everything;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
This method returns String as file content. We can store it in a variable like this:
String fileContent = MyClass.read(new File("path to file"));
Next step would be to remove our file. Since we have it in memory, and we don't want duplicate values...
file.delete();
Now we should remove our doctors from fileContent. It's basic String operations. I would recommend using method replace() or replaceAll().
And after the String manipulation, just write fileContent to our file again.
File file = new File("the same path");
file.createNewFile();
Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file, true), "UTF-8"));
out.write(fileContent);
out.flush();
out.close();

Iterative function issue

Im having some issues with a function that I have written. The function basically takes a file and a string into the method as parameters and searches the file for that string and replaces it with "".
public void removeReminder(File a, String search) throws IOException {
File tempFile = File.createTempFile("file", ".txt", a.getParentFile());
BufferedReader br = new BufferedReader(new FileReader(a));
PrintWriter pw = new PrintWriter(new FileWriter(tempFile));
for (String line; (line = br.readLine()) != null;) {
line = line.replace(search, "");
pw.println(line);
}
br.close();
pw.close();
a.delete();
tempFile.renameTo(a);
}
I then have 3 text files that I need to run this method for. Below is the code where i run the function.
removeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// TODO
try {
String names = reminderNameField.getText();
String date = reminderDate.getText();
String details = reminderDetailsField.getText();
File fileName = new File("reminderNames.txt");
File fileDate = new File("reminderDate.txt");
File fileDetails = new File("reminderDetails.txt");
removeReminder(fileName, names);
removeReminder(fileDate, date);
removeReminder(fileDetails, details);
} catch (IOException e){
e.printStackTrace();
}
I dont know why this isnt working. It works for the first iteration (e.g removeReminder(fileName, names);) But it doesnt work for the other ones, it seems to just ignore them :s can anyone tell me why this is?
I always flush printwriter. Try flushing PrintWriter before calling close().
pw.flush();

Reading multiple text file in Java

I have few text files. Each text file contains some path and/or the reference of some other file.
File1
#file#>D:/FilePath/File2.txt
Mod1>/home/admin1/mod1
Mod2>/home/admin1/mod2
File2
Mod3>/home/admin1/mod3
Mod4>/home/admin1/mod4
All I want is, copy all the paths Mod1, Mod2, Mod3, Mod4 in another text file by supplying only File1.txt as input to my java program.
What I have done till now?
public void readTextFile(String fileName){
try {
br = new BufferedReader(new FileReader(new File(fileName)));
String line = br.readLine();
while(line!=null){
if(line.startsWith("#file#>")){
String string[] = line.split(">");
readTextFile(string[1]);
}
else if(line.contains(">")){
String string[] = line.split(">");
svnLinks.put(string[0], string[1]);
}
line=br.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Currently my code reads the contents of File2.txt only, control does not come back to File1.txt.
Please ask if more inputs are required.
First of all you are jumping to another file without closing the current reader and when you come back you lose the cursor. Read one file first and then write all its contents that match to another file. Close the current reader (Don't close the writer) and then open the next file to read and so on.
Seems pretty simple. You need to write your file once your svnLinks Map is populated, assuming your present code works (haven't seen anything too weird in it).
So, once the Map is populated, you could use something along the lines of:
File newFile = new File("myPath/myNewFile.txt");
// TODO check file can be written
// TODO check file exists or create
FileOutputStream fos = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
fos = new FileOutputStream(newFile);
osw = new OutputStreamWriter(fos);
bw = new BufferedWriter(osw);
for (String key: svnLinks.keySet()) {
bw.write(key.concat(" my separator ").concat(svnLinks.get(key)).concat("myNewLine"));
}
}
catch (Throwable t) {
// TODO handle more gracefully
t.printStackTrace();
if (bw != null) {
try {
bw.close();
}
catch (Throwable t) {
t.printStackTrace();
}
}
Here is an non-recursive implementation of your method :
public static void readTextFile(String fileName) throws IOException {
LinkedList<String> list = new LinkedList<String>();
list.add(fileName);
while (!list.isEmpty()) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(new File(list.pop())));
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith("#file#>")) {
String string[] = line.split(">");
list.add(string[1]);
} else if (line.contains(">")) {
String string[] = line.split(">");
svnLinks.put(string[0], string[1]);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
br.close();
}
}
}
Just used a LinkedList to maintain the order. I suggest you to add some counter if you to limit the reading of files to a certain number(depth). eg:
while (!list.isEmpty() && readCount < 10 )
This will eliminate the chance of running the code to infinity(in case of circular reference).

Categories