2012-02-13 12 views
37

Obecnie używam coś takiego:Jaki jest najlepszy sposób na iterowanie po liniach ciągu Java?

String[]lines = textContent.split(System.getProperty("line.separator")); 
for(String tmpLine : lines){ 
    //do something 
} 

nie jestem bardzo zadowolony z tej metody, ponieważ tworzą ciężką tablicę (powiedzmy textContent może zawierać książkę).

Czy istnieje lepsze rozwiązanie do iteracji na liniach String?

Odpowiedz

41

Można użyć:

BufferedReader bufReader = new BufferedReader(new StringReader(textContent)); 

I użyj metody readLine():

String line=null; 
while((line=bufReader.readLine()) != null) 
{ 

} 
+0

Dzięki za odpowiedź. Czy to rozwiązanie zapewnia lepszą wydajność? Zauważyłem, że to rozwiązanie wykorzystuje obiekt ** 3 **. Chcę ograniczyć tworzenie obiektu, aby mieć wystarczającą ilość pamięci, więc 'BufferedReader' i' StringReader' są jaśniejsze niż tablica String? –

+0

Jako javadoc dla stanów BufferedReader, używanie tej klasy jest poprawnym sposobem owijania kosztownych metod odczytu dla ekonomicznych odczytów. Zobacz http://docs.oracle.com/javase/6/docs/api/java/io/BufferedReader.html –

6

Można użyć String.indexOf() /() String.substring

String separator = System.getProperty("line.separator"); 
int index = textContent.indexOf(separator); 

while (index > 0) 
{ 
    int nextIndex = textContent.indexOf(separator, index + separator.length()); 
    String line = textContent.substring(index + separator.length(), nextIndex); 

    // do something with line. 
} 
3

A co z klasą java.util.Scanner?

Podsumowując:

Prosty skaner tekst, który można analizować prymitywnych typów i ciągów za pomocą wyrażeń regularnych.

Skaner dzieli swoje wejście na tokeny za pomocą wzoru separatora, , który domyślnie dopasowuje białe znaki. Wynikowe tokeny mogą następnie zostać przekształcone na wartości różnych typów przy użyciu różnych następnych metod.

i notatki do scenariusza:

Skaner może także używać ograniczników inne niż białe znaki. Ten przykład odczytuje kilka przedmiotów z ciągiem:

 String input = "1 fish 2 fish red fish blue fish"; 
    Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*"); 
    System.out.println(s.nextInt()); 
    System.out.println(s.nextInt()); 
    System.out.println(s.next()); 
    System.out.println(s.next()); 
    s.close(); 
1

stosowanie BufferedReader z StringReader argument. BufferedReader ma metodę readLine(), dzięki czemu możesz czytać linię ciągów po linii.

StringReader reader = new StringReader(myBigTextString); 
    BufferedReader br = new BufferedReader(reader); 
    String line; 
    while((line=br.readLine())!=null) 
    { 
     //do what you want 
    } 
+1

@ alain.janinm, jeśli zachowasz tablicę podzielonych linii, które zajmują dużo pamięci, jak powiedziałeś . W tym przypadku wszystkie wiersze tekstu nie są wczytywane do pamięci. BufferedReader właśnie pamięta ostatni punkt odczytu, a kiedy wywołuje metodę readLine(), po prostu czyta następny wiersz łańcucha (za pomocą StringReadera). Więc w każdej iteracji masz tylko jedną linię tekstu w pamięci (w zmiennej 'line') zamiast wszystkich linii. – shift66

1

Kombajny java.io.StringReader i java.io.LineNumberReader

+0

Dzięki za odpowiedź. inne proponowane 'BufferedReader'. Jakie są zalety 'java.io.LineNumberReader'? –

+0

Właściwie to po prostu nie zdawałem sobie sprawy, że BufferedReader ma również zaimplementowaną metodę readLine(). –

+0

Dla przyszłych czytelników: LineNumberReader rozszerza BufferedReader, więc LineNumberReader jest zamiennym zamiennikiem BufferedReader z dodatkowym zachowaniem śledzenia numeru linii właśnie przeczytanej linii. Zobacz http://docs.oracle.com/javase/8/docs/api/java/io/LineNumberReader.html. – MonkeyWithDarts

5

guawy za Splitter działa dobrze. Zwłaszcza jak można usunąć puste wiersze

Splitter splitter = Splitter.on(System.getProperty("line.separator")) 
          .trimResults() 
          .omitEmptyStrings(); 
for (String line : splitter.split(input)){ 
    // do work here 
} 
+2

Z kodu źródłowego guavy: '' 'Splitter.on (Pattern.compile (" \ r? \ N ")). Split (wholeFile)' '' –

+0

Dokładniej, jest w Javadoc dla 'Splitter # on': https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/base/Splitter.html#on-java.util.regex.Pattern- – simon04

12

Aby dodać drogę Java 8 na to pytanie:

Arrays.stream(content.split("\\r?\\n")).forEach(line -> /*do something */) 

przekleństwa można również wykorzystać System.lineSeparator() podzielić jeśli masz pewność, że plik jest comming od ta sama platforma, na której działa vm.

Albo jeszcze lepiej wykorzystać API strumień nawet więcej agressiv z filtrem, mapę i zbierać:

String result = Arrays.stream(content.split(System.lineSeparator())) 
        .filter(/* filter for lines you are interested in*/) 
        .map(/*convert string*/) 
        .collect(Collectors.joining(";")); 
+0

prawdziwy sposób java8 prawdopodobnie użyłby 'System.lineSeparator()' zamiast właściwości bezpośrednio – xenoterracide

+0

@xenoterracide masz rację! Zmieniono odpowiedź odpowiednio. – leo

+0

Przypadkowo edytowałeś fragment "content" w swoim drugim przykładzie. – Torque

1

Rzeczywiście można użerać Scanner, aby umożliwić korzystanie z normalną for pętlę:

import java.util.Scanner; 
public class IterateLines { 
    public static void main(String[] args) { 
     Iterable<String> sc =() -> 
      new Scanner("foo bar\nbaz\n").useDelimiter("\n"); 
     for (String line: sc) { 
      System.out.println(line); 
     } 
    } 
} 

daje nam:

$ javac IterateLines.java && java IterateLines 
foo bar 
baz 
+0

Spowoduje to podział ciągu na spacje i znaki nowej linii, co nie jest tym, czego szuka. – Zulakis

+0

Dzięki @Zulakis - Poprawiłem kod, aby użyć wyraźnego ogranicznika. –

Powiązane problemy