2009-07-08 171 views
107

給定一個不太長的字符串,逐行讀取它的最佳方法是什麼?逐行讀取字符串

我知道你可以這樣做:

BufferedReader reader = new BufferedReader(new StringReader(<string>)); 
reader.readLine(); 

另一種方法是取子串的EOL:

final String eol = System.getProperty("line.separator"); 
output = output.substring(output.indexOf(eol + 1)); 

任何其他做的,也許更簡單的方法?我對上述方法沒有任何問題,只是有興趣知道你們中的任何人是否知道可能看起來更簡單和更高效的東西?

+4

嗯,你的要求說,這意味着你不需要在內存中的所有線路在同一時間「一行閱讀行」,所以我會堅持使用BufferedReader或Scanner方法,無論你覺得哪個更舒服(不知道哪個更有效)。這樣你的內存需求就會減少。它還將允許您「擴展」應用程序,以便將來通過從文件中讀取數據來使用更大的字符串。 – camickr 2009-07-08 16:38:43

回答

96

您還可以使用字符串的方法split

String[] lines = myString.split(System.getProperty("line.separator")); 

這使您在方便的陣列中的所有行。

我不知道拆分的性能。它使用正則表達式。

+2

並希望行分隔符中沒有正則表達式字符。 :) – 2009-07-08 09:06:59

+36

「line.separator」反正是不可靠的。僅僅因爲代碼在Unix上運行,那麼如何阻止文件使用Windows風格的「\ r \ n」行分隔符? BufferedReader.readLine()和Scanner.nextLine()總是檢查所有三種風格的分隔符。 – 2009-07-09 06:25:04

161

還有Scanner。您可以使用它就像BufferedReader

Scanner scanner = new Scanner(myString); 
while (scanner.hasNextLine()) { 
    String line = scanner.nextLine(); 
    // process the line 
} 
scanner.close(); 

我認爲這是一個有點更簡潔的方法,無論是建議者的。

+5

雖然我不認爲這是一個公平的比較 - String.split依靠整個輸入被讀入內存,這並不總是可行的(例如對於大型文件)。 – Adamski 2009-07-08 08:00:32

+3

輸入必須駐留在內存中,因爲輸入是字符串。內存開銷是數組。而且,生成的字符串重用相同的後端字符數組。 – notnoop 2009-07-09 13:21:40

+10

完成閱讀後,請勿忘記關閉掃描儀。 – 2013-01-11 10:24:56

20

使用Apache Commons IOUtils您可以通過

List<String> lines = IOUtils.readLines(new StringReader(string)); 

它沒有做任何事情聰明很好做到這一點,但它很高興和緊湊。它也可以處理流,如果您願意,也可以獲得LineIterator

5

您還可以使用:

String[] lines = someString.split("\n"); 

如果不行嘗試\r\n更換\n

28

由於我對效率角度特別感興趣,因此我創建了一個小測試類(如下)。結果爲500萬行:

Comparing line breaking performance of different solutions 
Testing 5000000 lines 
Split (all): 14665 ms 
Split (CR only): 3752 ms 
Scanner: 10005 
Reader: 2060 

像往常一樣,確切的時間可能有所不同,但其比例也是如此但是經常我碰到它。結論:OP的「更簡單」和「更高效」的要求不能同時得到滿足,split解決方案(兩種形式)都比較簡單,但是Reader的實現卻擊敗了其他人。

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.StringReader; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Scanner; 

/** 
* Test class for splitting a string into lines at linebreaks 
*/ 
public class LineBreakTest { 
    /** Main method: pass in desired line count as first parameter (default = 10000). */ 
    public static void main(String[] args) { 
     int lineCount = args.length == 0 ? 10000 : Integer.parseInt(args[0]); 
     System.out.println("Comparing line breaking performance of different solutions"); 
     System.out.printf("Testing %d lines%n", lineCount); 
     String text = createText(lineCount); 
     testSplitAllPlatforms(text); 
     testSplitWindowsOnly(text); 
     testScanner(text); 
     testReader(text); 
    } 

    private static void testSplitAllPlatforms(String text) { 
     long start = System.currentTimeMillis(); 
     text.split("\n\r|\r"); 
     System.out.printf("Split (regexp): %d%n", System.currentTimeMillis() - start); 
    } 

    private static void testSplitWindowsOnly(String text) { 
     long start = System.currentTimeMillis(); 
     text.split("\n"); 
     System.out.printf("Split (CR only): %d%n", System.currentTimeMillis() - start); 
    } 

    private static void testScanner(String text) { 
     long start = System.currentTimeMillis(); 
     List<String> result = new ArrayList<>(); 
     try (Scanner scanner = new Scanner(text)) { 
      while (scanner.hasNextLine()) { 
       result.add(scanner.nextLine()); 
      } 
     } 
     System.out.printf("Scanner: %d%n", System.currentTimeMillis() - start); 
    } 

    private static void testReader(String text) { 
     long start = System.currentTimeMillis(); 
     List<String> result = new ArrayList<>(); 
     try (BufferedReader reader = new BufferedReader(new StringReader(text))) { 
      String line = reader.readLine(); 
      while (line != null) { 
       result.add(line); 
       line = reader.readLine(); 
      } 
     } catch (IOException exc) { 
      // quit 
     } 
     System.out.printf("Reader: %d%n", System.currentTimeMillis() - start); 
    } 

    private static String createText(int lineCount) { 
     StringBuilder result = new StringBuilder(); 
     StringBuilder lineBuilder = new StringBuilder(); 
     for (int i = 0; i < 20; i++) { 
      lineBuilder.append("word "); 
     } 
     String line = lineBuilder.toString(); 
     for (int i = 0; i < lineCount; i++) { 
      result.append(line); 
      result.append("\n"); 
     } 
     return result.toString(); 
    } 
} 
1

隨着番石榴:

ImmutableList<String> lines = CharSource.wrap(str).readLines(); 
2

或者使用新的嘗試與資源條款與掃描儀結合使用:

try (Scanner scanner = new Scanner(value)) { 
     while (scanner.hasNextLine()) { 
      String line = scanner.nextLine(); 
      // process the line 
     } 
    } 
3

可以使用流API和一個StringReader包裹在其中得到了一個BufferedReader java中的lines()流輸出8:

import java.util.stream.*; 
import java.io.*; 
class test { 
    public static void main(String... a) { 
     String s = "this is a \nmultiline\rstring\r\nusing different newline styles"; 

     new BufferedReader(new StringReader(s)).lines().forEach(
      (line) -> System.out.println("one line of the string: " + line) 
     ); 
    } 
} 

給人

one line of the string: this is a 
one line of the string: multiline 
one line of the string: string 
one line of the string: using different newline styles 

就像在BufferedReader中的的readLine,換行符(S)本身不包括在內。支持各種換行符分隔符(甚至在同一個字符串中)。

8

使用解決方案Java 8功能,如Stream APIMethod references

new BufferedReader(new StringReader(myString)) 
     .lines().forEach(System.out::println); 

public void someMethod(String myLongString) { 

    new BufferedReader(new StringReader(myLongString)) 
      .lines().forEach(this::parseString); 
} 

private void parseString(String data) { 
    //do something 
} 
0

你可以試試下面的正則表達式:

\r?\n 

代碼:

String input = "\nab\n\n \n\ncd\nef\n\n\n\n\n"; 
String[] lines = input.split("\\r?\\n", -1); 
int n = 1; 
for(String line : lines) { 
    System.out.printf("\tLine %02d \"%s\"%n", n++, line); 
} 

輸出:

Line 01 "" 
Line 02 "ab" 
Line 03 "" 
Line 04 " " 
Line 05 "" 
Line 06 "cd" 
Line 07 "ef" 
Line 08 "" 
Line 09 "" 
Line 10 "" 
Line 11 "" 
Line 12 ""