2009-07-08 151 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 ""