4
\$\begingroup\$

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.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

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().

\$\endgroup\$
2
\$\begingroup\$

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.");
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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