Java Getriebe

Java und NetBeans

Search & Replace in Dateien

Die Änderungen in den letzten beiden Java Versionen sind toll. NIO2, Streams und Lambdas machen einige Dinge unheimlich effizient und kompakt. Ich habe aktuell zum Beispiel das Problem, dass ich in einem Verzeichnisbaum bestimmte (Text-)Dateien suchen muss und darin eine bestimmte Zeile durch einen neuen Wert ersetzen möchte.

Musste man vor Java7 noch die Rekursion zum Durchlaufen der Verzeichnisse selber schreiben erledigt dies mit der NIO2 Files.walkFileTree() sehr elegant.

Auch das Einlesen und Schreiben ist dank NIO2 jeweils ein Einzeiler geworden. Zumindest wenn man nicht gigabytegroße Dateien hat.

1
2
3
4
5
6
List<String> converted = new ArrayList<>();
List<String> lines =
    Files.readAllLines(file, StandardCharsets.UTF_8);
/*... some stuff ...*/
Files.write(file, converted,
    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

Die Neuerungen zu Streams und Lambdas aus Java8 machen das Verarbeiten der Datei sehr klein 1. Die Stream geben einem zusätzlich die Möglichkeit eine solche Verarbeitung im Handumdrehen in parallelen Threads durchzuführen. Bei der Datenmenge hier war dies allerdings nicht nötig.

1
2
3
4
5
6
lines.stream().forEach((line) ->
{
  converted.add(line.replace(
      "<credentialsId>oldID</credentialsId>",
      "<credentialsId>newID</credentialsId>"));
});

Zum Schluss das Ganze als vollständige Klasse (ohne imports). Der zu durchsuchende Pfad wird einfach als Kommandozeilenparameter übergeben. Es fehlen diverse Abfragen, die vor Fehlbenutzung schützen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class FastChange
{
  public static void main(String[] args) throws IOException
  {
    Path root = Paths.get(args[0]);
    Files.walkFileTree(root, new SimpleFileVisitor<Path>()
    {
      @Override
      public FileVisitResult visitFile(
          Path file, BasicFileAttributes attrs)
          throws IOException
      {
        if("config.xml".equalsIgnoreCase(
            file.getFileName().toString()))
        {
          List<String> converted = new ArrayList<>();
          List<String> lines =
              Files.readAllLines(file, StandardCharsets.UTF_8);
          lines.stream().forEach((line) ->
          {
            converted.add(line.replace(
                "<credentialsId>oldID</credentialsId>",
                "<credentialsId>newID</credentialsId>"));
          });

          Files.write(file, converted,
              StandardOpenOption.CREATE,
              StandardOpenOption.TRUNCATE_EXISTING);
        }

        return FileVisitResult.CONTINUE;
      }
    });
  }
}

Notes:

  1. Zugegeben in diesem Fall wäre eine erweiterte for-Schleife genauso kompakt gewesen.

One Response to Search & Replace in Dateien

  1. Michael says:

    So sparst Du Dir 2 intermediäre Listen:

    1
    2
    3
    4
    5
    6
    7
    List converted =
      Files.lines(file, StandardCharsets.UTF_8)
        .map(line -> line.replace(
          "oldID",
          "newID")
        )
        .collect(Collectors.toList());