So, given a file with the following format:
[header]
line of data
line of data
line of data
line of data[header 2]
line of data
line of data
line of data...
I add a line of data to a specific header so the result would be:
...
[target header]
line of data
...
new line inserted
The program currently only adds lines but is written so it accepts an input file that lists instructions with the format instruction|[header]|line to add
e.g. A|[Passions]|Code Review would add Code Review to the Passions section.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
public class AdjustList {
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("No arguments provided.");
System.exit(1);
}
try (Scanner fileScanner = new Scanner(new File(args[0]))) {
while (fileScanner.hasNextLine()) {
String[] commandParameters = fileScanner.nextLine().split("\\|");
if (commandParameters.length != 3) {
throw new IllegalArgumentException("Invalid parameter format");
}
File targetFile = new File("ImportantList.md");
processCommand(targetFile, commandParameters[0], commandParameters[1], commandParameters[2]);
}
} catch (FileNotFoundException fnfe) {
System.err.println("File doesn't exist / wrong directory");
}
}
private static void processCommand(File file, String instruction, String targetSection, String inputLine) {
boolean taskComplete = false;
StringBuilder fileBuilder = new StringBuilder();
try (Scanner fileScanner = new Scanner(file)) {
if (instruction.equals("A")) { // add
System.out.println("Adding " + inputLine + " to " + targetSection);
while (fileScanner.hasNextLine()) {
String line = fileScanner.nextLine();
if (line.equals(targetSection)) {
fileBuilder.append(line).append('\n');
while (!taskComplete) {
line = fileScanner.nextLine();
if (line.isEmpty()) {
fileBuilder.append(inputLine).append('\n');
taskComplete = true;
} else {
fileBuilder.append(line).append('\n');
}
}
} else {
fileBuilder.append(line).append('\n');
}
}
}
} catch(FileNotFoundException fnfe) {
System.err.println("File doesn't exist / wrong directory");
System.exit(1);
}
writeOutput(file, fileBuilder.toString());
}
private static void writeOutput(File file, String output) {
try (PrintWriter writer = new PrintWriter(file, "UTF-8")) {
writer.write(output);
} catch (FileNotFoundException | UnsupportedEncodingException ex) {
ex.printStackTrace();
}
}
}
The file is read, line by line, storing the input into a StringBuilder
with the additional line added based on a flag. This works but I get the feeling it's not the cleanest way.
2 Answers 2
Input validation
It looks like if instruction
is not "A"
, then you will be overwriting "ImportantList.md"
with the contents of an empty StringBuilder
.
Iterable
-based processing
If the file sizes you are dealing with are relatively small enough to fit contents in memory, you can consider reading them in as a List
, do the required processing, then write it out via Files.write(Path, Iterable, Charset, OpenOption)
. This avoids having to concatenate \n
repetitively while using the StringBuilder
. For example:
private static void process(Path path, String targetHeader, String newLine) {
List<String> lines = null;
try {
lines = Files.readAllLines(path, StandardCharsets.UTF_8);
} catch (IOException e) {
// handle error here
}
int index = lines.indexOf(targetHeader);
if (index == -1) {
// log that target header is not found?
return;
}
int i = index + 1;
for (; i < lines.size(); i++) {
if (lines.get(i).isEmpty()) {
lines.add(i, newLine);
break;
}
}
if (i == lines.size()) {
lines.add(newLine);
}
try {
Files.write(path, lines, StandardCharsets.UTF_8);
} catch (IOException e) {
// handle error here
}
}
Here, we also handle the case where the target header is the last header and there is no trailing newline at the end of the text file, using the comparison i == lines.size()
.
Misleading Message?
if (args.length != 1) { System.err.println("No arguments provided."); System.exit(1); }
So if I have 2 arguments, I get the message No arguments provided
. Umm... I provided 2 arguments!
Suggested fix:
Change the above code to:
if (args.length == 0) {
System.err.println("No arguments provided.");
System.exit(1);
}
And you can possible warn the user about only requiring one argument if they provide too much. Something like:
if (args.length > 1) {
System.out.println("Warning! Too many arguments; only the first one will be considered.");
}