2016-02-05 29 views
6

Próbuję napisać interfejs API, który zastąpi wszystkie wiersze zawierające określony ciąg z innym ciągiem w pliku tekstowym.Zastąp tekst w pliku przy użyciu Stream- Java 8

Używam strumienia Java 8 do filtrowania linii zawierających dany wzorzec. Mam problem z częścią zapisu pliku.

Files.lines(targetFile).filter(line -> line.contains(plainTextPattern)).parallel() 
     .map(line-> line.replaceAll(plainTextPattern, replaceWith)).parallel(); 

Powyższy kod odczytuje linia mądry plik, filtruje wiersze, które pasują do wzorca i zastępuje z ciągiem dawać i daje z powrotem do strumienia ciągów, które ma tylko zastąpionych linie.

Musimy zapisać te linie z powrotem do pliku. Ponieważ tracimy strumień raz rurociąg kończy, ja dołączone następujące rurociągu:

.forEach(line -> { 
try { 
      Files.write(targetFile, line.toString().getBytes()); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 

Miałam nadzieję, że to zapisu pliku tylko zmodyfikowany (ponieważ jest w rurociągu) linii i zachować drugi linie nietknięte.

Ale wydaje się, że przyciął plik dla każdej linii w pliku i zachował tylko ostatnią przetworzoną linię i usunął wszystkie linie, które nie zostały dopasowane do potoku.

Czy jest coś, czego mi brakuje w obsłudze plików za pomocą strumieni?

+3

Od [dokumentacja] (https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#write-java. nio.file.Path-byte: A-java.nio.file.OpenOption ...-): "Domyślnie metoda tworzy nowy plik lub zastępuje istniejący plik.". Dla rozwiązania przeczytaj resztę dokumentacji. Pamiętaj też, że nie możesz po prostu zmienić starego pliku tekstowego. Musisz utworzyć nowy z odpowiednimi zmianami, a następnie go zastąpić. – Pshemo

+0

Dzięki! Myślałem o użyciu StandardOpenOption.APPEND. Ale czułem, że to nie ma sensu, ponieważ nie chcę, aby doszło do końca pliku. Musi zastąpić istniejącą linię. Czy znowu coś mi brakuje? – AshwiniR

+0

Po prostu próbowałem dołączyć. Dodaje całą zawartość (w tym linie, które nie pasują) do końca pliku. Przynajmniej to mówi mi, że niedopasowane linie nie zostały całkowicie utracone. Ale muszę wymyślić, jak przepisać plik z nową zawartością. – AshwiniR

Odpowiedz

16

Zastosowanie filter eliminuje wszystko, co nie pasuje do filtra ze strumienia. (Dodatkowo, o ile warto, a) wystarczy użyć tylko jeden raz, parallel, b) parallel nie jest tak skuteczny w przypadku strumieni pochodzących ze źródeł I/O, c) prawie nigdy nie warto używać parallel, dopóki nie faktycznie wypróbował to nierównolegle i stwierdził, że jest zbyt wolny.)

Powiedział: nie ma potrzeby odfiltrowywania linii pasujących do wzorca, jeśli zamierzasz wykonać replaceAll. Kod powinien wyglądać następująco:

try (Stream<String> lines = Files.lines(targetFile)) { 
    List<String> replaced = lines 
     .map(line-> line.replaceAll(plainTextPattern, replaceWith)) 
     .collect(Collectors.toList()); 
    Files.write(targetFile, replaced); 
} 
+0

Wszystkie komentarze były bardzo pouczające! Dziękuję wam wszystkim! – AshwiniR

6

Przykro mi to mówić, że nie działają w ten sposób pliki. Jeśli chcesz pisać w środku pliku, musisz mieć RandomAccess; Zdobądź FilePointer, szukaj, wskaż i napisz stamtąd.

Zachowuje to, jeśli rozmiar danych, które chcesz zapisać, jest równy rozmiarowi danych, które chcesz nadpisać. Jeśli tak nie jest, musisz skopiować ogon pliku do bufora tymczasowego i dołączyć go do tekstu, który chcesz napisać.

I przy okazji, parallelStreams na IO związane zadania to często zły pomysł.

Powiązane problemy