Читать строку строка за строкой
Учитывая строку, которая не слишком длинная, как лучше всего читать ее построчно?
Я знаю, что вы можете сделать:
BufferedReader reader = new BufferedReader(new StringReader(<string>));
reader.readLine();
Другой способ - взять подстроку в eol:
final String eol = System.getProperty("line.separator");
output = output.substring(output.indexOf(eol + 1));
Есть ли другие, может быть, более простые способы сделать это? У меня нет проблем с вышеуказанными подходами, просто интересно узнать, знает ли кто-нибудь из вас что-то, что может выглядеть проще и эффективнее?
10 ответов
Вы также можете использовать split
метод строки:
String[] lines = myString.split(System.getProperty("line.separator"));
Это дает вам все строки в удобном массиве.
Я не знаю о производительности раскола. Он использует регулярные выражения.
Существует также Scanner
, Вы можете использовать его так же, как BufferedReader
:
Scanner scanner = new Scanner(myString);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// process the line
}
scanner.close();
Я думаю, что это немного более чистый подход, чем оба предложенных.
Поскольку меня особенно интересовал угол эффективности, я создал небольшой тестовый класс (ниже). Результат на 5 000 000 строк:
Comparing line breaking performance of different solutions
Testing 5000000 lines
Split (all): 14665 ms
Split (CR only): 3752 ms
Scanner: 10005
Reader: 2060
Как обычно, точное время может отличаться, но соотношение сохраняется, однако я часто его использую.
Вывод: "более простые" и "более эффективные" требования ОП не могут быть выполнены одновременно, 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();
}
}
Используя Apache Commons IOUtils, вы можете сделать это красиво через
List<String> lines = IOUtils.readLines(new StringReader(string));
Это не делает ничего умного, но это красиво и компактно. Он будет обрабатывать потоки, и вы можете получить LineIterator
тоже, если вы предпочитаете.
Начиная с Java 11, появился новый метод String.lines
:
/**
* Returns a stream of lines extracted from this string,
* separated by line terminators.
* ...
*/
public Stream<String> lines() { ... }
Использование:
"line1\nline2\nlines3"
.lines()
.forEach(System.out::println);
Решение с использованием Java 8
такие функции, как Stream API
а также Method 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
}
Вы также можете использовать:
String[] lines = someString.split("\n");
Если это не сработает, попробуйте заменить \n
с \r\n
,
Вы можете использовать потоковый API и StringReader, обернутый в BufferedReader, который получил поток lines() в java 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
Как и в readLine BufferedReader, сами символы новой строки не включены. Поддерживаются все виды разделителей новой строки (даже в одной строке).
Или используйте новую попытку с предложением ресурсов в сочетании со Scanner:
try (Scanner scanner = new Scanner(value)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// process the line
}
}
Вы можете попробовать следующее регулярное выражение:
\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 ""
Самый простой и универсальный подход - просто использовать регулярное выражение Linebreak matcher
\R
который соответствует Any Unicode linebreak sequence
:
Pattern NEWLINE = Pattern.compile("\\R")
String lines[] = NEWLINE.split(input)
@ см. https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html