遍历 Java 字符串行的最佳方法是啥?

Posted

技术标签:

【中文标题】遍历 Java 字符串行的最佳方法是啥?【英文标题】:What is the best way to iterate over the lines of a Java String?遍历 Java 字符串行的最佳方法是什么? 【发布时间】:2012-03-04 18:56:44 【问题描述】:

目前我正在使用类似的东西:

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

我对这种方法不太满意,因为它创建了一个沉重的数组(假设textContent 可以包含一本书)。

有没有更好的解决方案来遍历String 的行?

【问题讨论】:

现在也使用 JDK/11,可以make use of String.lines 提供比拆分更好的性能。 【参考方案1】:

你可以使用:

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

并使用readLine() 方法:

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



【讨论】:

感谢您的回答。此解决方案是否提供更好的性能?我注意到这个解决方案使用 3 对象。我想限制创建对象有足够的内存,那么BufferedReaderStringReader比String数组轻吗? 正如 BufferedReader 的 javadoc 所述,使用所述类是包装成本高昂的读取方法以实现具有成本效益的读取的有效方法。见docs.oracle.com/javase/6/docs/api/java/io/BufferedReader.html【参考方案2】:

为这个问题添加 Java 8 方式:

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

当然,如果您确定文件来自与 vm 运行相同的平台,您也可以使用 System.lineSeparator() 进行拆分。

或者甚至更好地使用带有过滤器、映射和收集的流 api 甚至更具侵略性:

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

【讨论】:

真正的java8方式可能会直接使用System.lineSeparator()而不是属性 @xenoterracide 你是对的!相应地更改了答案。 @Torque 我解决了这个问题。 此解决方案的缺点是 split 方法将处理整个字符串,因此它可以在返回之前构建所有行的完整数组。如果您的字符串很大,您将创建一个包含大量对象的巨型数组,这很昂贵。【参考方案3】:

我相信您从 Java-11 开始有更好的 API 可用,您可以使用 String.lines() API 执行相同操作,该 API 返回从由行终止符分区的该字符串中提取的字符串流。

public Stream<String> lines()

同样的用法可以是:-

Stream<String> linesFromString = textContent.lines();
linesFromString.forEach(l ->   //do sth );

重要的 API 说明:-

@implNote This method provides better performance than
          split("\R") by supplying elements lazily and
          by faster search of new line terminators.

【讨论】:

【参考方案4】:

你可以使用 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.

【讨论】:

【参考方案5】:

Guava 的Splitter 运行良好。特别是你可以删除空行

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

【讨论】:

来自guava的源代码:Splitter.on(Pattern.compile("\r?\n")).split(entireFile) 更准确地说,它在Splitter#on的Javadoc中:google.github.io/guava/releases/snapshot/api/docs/com/google/…【参考方案6】:

Scanner

Java 1.5 中添加的java.util.Scanner 类呢?

总结:

一个简单的文本扫描器,可以解析原始类型和字符串 使用正则表达式。

扫描器使用分隔符模式将其输入分解为标记, 默认情况下匹配空格。然后生成的令牌可能是 使用各种 next 转换为不同类型的值 方法。

对于你的场景值得注意:

扫描器还可以使用空格以外的分隔符。这 示例从字符串中读取多个项目:

     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();

【讨论】:

【参考方案7】:

您实际上可以争吵Scanner 以允许您使用普通的for 循环:

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);
        
    

给我们:

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

【讨论】:

这会将字符串拆分为空格和换行符,这不是问题所要寻找的。​​span> 感谢@Zulakis - 我已更正代码以使用显式分隔符。 我认为使用 System.getProperty("line.separator") 会有所改进。【参考方案8】:

结合java.io.StringReaderjava.io.LineNumberReader

【讨论】:

感谢您的回答。其他建议BufferedReaderjava.io.LineNumberReader有什么优势? 其实我只是没有意识到 BufferedReader 也实现了 readLine() 方法。 对于未来的读者:LineNumberReader 扩展了 BufferedReader,因此 LineNumberReader 是 BufferedReader 的直接替代品,具有跟踪您刚刚阅读的行的行号的附加行为。见docs.oracle.com/javase/8/docs/api/java/io/LineNumberReader.html。【参考方案9】:

如果您使用的是 Java 1.8(或 android),请尝试以下操作:

new BufferedReader(new StringReader(str)).lines().forEachOrdered((line) -> 
    // process each line as you like
);

Docs state

Stream 是惰性填充的,即在终端流操作期间只读发生。

这意味着这比在迭代开始之前首先生成大量字符串数组的其他解决方案运行得更快。

如果您使用的是 Java 11 或更高版本,那么 @Naman 给出的推荐 String#lines() 方法的答案也更加简洁和快速,请参阅 https://***.com/a/50631579/215266

【讨论】:

【参考方案10】:

使用带有 StringReader 参数的 BufferedReader。 BufferedReader 有一个方法 readLine() 所以你可以逐行读取你的字符串。

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

【讨论】:

@alain.janinm,当您保留一个分割线数组时,该数组会占用您所说的大量内存。在这种情况下,文本的所有行都不会加载到内存中。 BufferedReader 只记住最后一个读取点,当您调用 readLine() 方法时,它只会读取字符串的下一行(在 StringReader 的帮助下)。因此,在每次迭代中,内存中只有一行文本(在 line 变量中)而不是所有行。

以上是关于遍历 Java 字符串行的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在Java中递归反转字符串的最佳方法是啥?

将单色图像转换为二进制字符串的最佳方法是啥?

保持从文件中读取的字符串值列表的最佳java方法是啥[关闭]

在不使用正则表达式的情况下,判断一个字符是 Java 中的字母还是数字的最佳方法是啥?

在 Java 中为 XML 编码文本数据的最佳方法是啥?

在 python 中创建字符串数组的最佳方法是啥?