2016-02-10 22 views
6
 try (Stream<String> lines = Files.lines(targetFile)) { 
    List<String> replacedContent = lines.map(line -> 
             StringUtils.replaceEach(line,keys, values)) 
             .parallel() 
             .collect(Collectors.toList()); 
    Files.write(targetFile, replacedContent); 
} 

Sto provando a sostituire più schemi di testo in ogni riga del file. Ma sto osservando che "\ r \ n" (byte equivalente 10 e 13) viene sostituito con solo "\ r" (solo 10) e i miei test di comparazione stanno fallendo.Come conservare la nuova riga durante la lettura di un file utilizzando lo stream - java 8

Voglio conservare le nuove righe come sono nel file di input e non voglio che java li tocchi. Qualcuno potrebbe suggerire se c'è un modo per farlo senza dover utilizzare una sostituzione predefinita separata per "\ r \ n".

+0

Ci scusiamo per manca quella. Appena aggiunto. – AshwiniR

+0

Appena rimosso il replaceEach per isolare il problema e il suo Files.line() che sembra fare questo. – AshwiniR

+1

Dove sta succedendo la sostituzione? Il codice incollato ha creato un elenco di stringhe, non ha alcun carattere di nuova riga. – mks

risposta

9

Il problema è che Files.lines() è implementato in cima a BufferedReader.readLine(), che legge una riga fino al terminatore di riga e la getta via. Quindi, quando si scrivono le righe con qualcosa come Files.write(), questo fornisce il terminatore di linea specifico del sistema dopo ogni riga, che potrebbe differire dal terminatore di linea letto.

Se si desidera conservare esattamente i terminatori di riga esattamente così come sono, anche se sono una combinazione di terminatori di linea diversi, è possibile utilizzare una regex e Scanner per quello.

prima definire un modello che corrisponde a una linea inclusi i terminatori di linea validi o EOF:

Pattern pat = Pattern.compile(".*\\R|.+\\z"); 

Il \\R è una speciale matcher di riga che corrisponde ai terminatori di linea solite più un paio di terminatori di linea Unicode che ho mai sentito di. :-) Potresti usare qualcosa come (\\r\\n|\\r|\\n) se vuoi solo i soliti CRLF, CR o LF terminatori.

È necessario includere .+\\z per abbinare una potenziale "riga" nel file che non ha un terminatore di riga. Assicurati che la regex corrisponda sempre ad almeno un carattere in modo che non venga trovata alcuna corrispondenza quando lo Scanner raggiunge la fine del file.

Quindi, leggere le linee con un Scanner fino a quando non ritorna null:

try (Scanner in = new Scanner(Paths.get(INFILE), "UTF-8")) { 
    String line; 
    while ((line = in.findWithinHorizon(pat, 0)) != null) { 
     // Process the line, then write the output using something like 
     // FileWriter.write(String) that doesn't add another line terminator. 
    } 
} 
+0

Stuart e altri, penso di non poter usare lo scanner con il programma multithread giusto? C'è un altro modo per ottenere questo per i programmi con multithreading? – AshwiniR

+0

@AshwiniR È possibile utilizzare una singola istanza 'Scanner' da un solo thread alla volta in un programma con multithreading. Più thread possono utilizzare diverse istanze 'Scanner', a condizione che non ci siano due thread attivi nella stessa istanza. Se si desidera elaborare linee da un singolo file in parallelo, questo è difficile, poiché la lettura del file e la scrittura dell'output sono sequenziali. Probabilmente vale la pena correre in parallelo se c'è una grande quantità di calcolo per ogni linea. –

+0

Grazie Stuart. Creo un'istanza 'Scanner' in una discussione. Questa istanza legge tutte le linee una per una, crea un elenco di linee e chiude lo scanner.Qualsiasi altro thread in esecuzione in parallelo a questo thread avrà la propria istanza di 'Scanner'. Quindi non devo preoccuparmi che 'Scanner' non sia thread-safe o che sincronizzi il metodo in cui utilizzo' Scanner' giusto? – AshwiniR

3

Le righe nel flusso non includono alcun carattere di nuova riga.

Sarebbe bello se la documentazione del metodo per Files.lines() menzionato questo. Tuttavia, se si segue l'implementazione, alla fine si arriva a BufferedReader.readLine(). Questo metodo è documentato per restituire il contenuto della riga, not including any line-termination characters.

È possibile aggiungere un carattere di fine riga alle linee quando vengono scritte.

una linea di separazione dipendente dal sistema viene utilizzato con il metodo Files.write() che stai chiamando, come documented in its sibling. È anche possibile ottenere questo separatore di linea dipendente dal sistema con System.lineSeparator().

Se si desidera un separatore di linea diverso e sapere di cosa si tratta, è possibile specificarlo. Per esempio:

try (PrintStream out = new PrintStream(Files.newOutputStream(targetFile))) 
    { 
     lines.forEach(line -> out.print(line + "\r\n")); 
    } 

Se si desidera separatori di riga del file originale, non si può contare solo su un metodo che mette a nudo quelli fuori. Le opzioni includono:

  • Leggere il separatore della prima riga e supporre che sia coerente in tutto il file. Ciò consente di continuare a utilizzare Files.lines() per leggere le righe.
  • Utilizzare un'API che consente di ottenere linee con i relativi separatori.
  • Leggere carattere per carattere anziché riga per riga in modo da ottenere i separatori di riga.

ATTENZIONE: Il codice legge e scrive dallo stesso file. Potresti perdere i tuoi dati originali a causa di terminazioni o errori anomali.

+0

Sembra che 'Files.write()' aggiunga la sequenza "fine riga" mentre scrive ogni riga nella lista data. –

+0

Penso che Files.write li stia aggiungendo ma aggiungendo solo "\ r". Il mio file di input ha "\ r \ n". Non vedo un modo per cambiarlo in Files.write() !! – AshwiniR

+0

@AshwiniR - Potresti essere in grado di farlo impostando la proprietà 'line.separator', ma quell'hack influenza l'intero processo. Usare un meccanismo diverso da 'Files.write()' potrebbe essere preferibile. Vedi un esempio nel testo modificato sopra. Nota anche l'avviso aggiunto dopo il tuo commento. –