Was ist der beste Weg, eine Zeichenfolge, die nicht zu lang ist, Zeile für Zeile zu lesen?
Ich weiß, dass Sie Folgendes tun können:
BufferedReader reader = new BufferedReader(new StringReader(<string>));
reader.readLine();
Eine andere Möglichkeit wäre, die Teilzeichenfolge auf dem Eol zu nehmen:
final String eol = System.getProperty("line.separator");
output = output.substring(output.indexOf(eol + 1));
Irgendwelche anderen vielleicht einfacheren Möglichkeiten, es zu tun? Ich habe keine Probleme mit den oben genannten Ansätzen, bin nur daran interessiert zu wissen, ob jemand von Ihnen etwas kennt, das einfacher und effizienter aussieht?
Nun, Ihre Anforderung lautete “Zeile für Zeile lesen”, was bedeutet, dass Sie nicht alle Zeilen gleichzeitig im Speicher benötigen, also würde ich beim BufferedReader- oder Scanner-Ansatz bleiben, je nachdem, mit welchem Sie sich wohler fühlen (weiß nicht was effizienter ist). Auf diese Weise ist Ihr Speicherbedarf geringer. Es ermöglicht Ihnen auch, die Anwendung zu “skalieren”, um größere Zeichenfolgen zu verwenden, indem Sie möglicherweise in Zukunft Daten aus einer Datei lesen.
– camickr
8. Juli 2009 um 16:38 Uhr
nichtnoop
Es gibt auch Scanner. Sie können es genauso verwenden wie die BufferedReader:
Scanner scanner = new Scanner(myString);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// process the line
}
scanner.close();
Ich denke, dass dies ein etwas saubererer Ansatz ist als die beiden vorgeschlagenen.
Ich denke jedoch nicht, dass es ein fairer Vergleich ist – String.split verlässt sich darauf, dass die gesamte Eingabe in den Speicher gelesen wird, was nicht immer machbar ist (z. B. bei großen Dateien).
– Adamsky
8. Juli 2009 um 8:00 Uhr
Die Eingabe muss sich im Speicher befinden, vorausgesetzt, die Eingabe ist String. Der Speicheraufwand ist das Array. Außerdem verwenden die resultierenden Strings dasselbe Back-End-Zeichenarray wieder.
– nichtnoop
9. Juli 2009 um 13:21 Uhr
Vorsicht Scanner kann falsche Ergebnisse liefern, wenn Sie eine UTF-8-Datei mit Unicode-Zeichen scannen und die Kodierung in Scanner nicht angeben. Es könnte ein anderes Zeichen als Zeilenende interpretieren. In Windows verwendet es seine Standardcodierung.
– Liebe leben
7. November 2017 um 4:12 Uhr
ftl
Sie können auch die verwenden split Methode von String:
Dadurch erhalten Sie alle Linien in einem handlichen Array.
Ich weiß nichts über die Leistung von Split. Es verwendet reguläre Ausdrücke.
Und hoffen Sie, dass das Zeilentrennzeichen keine Regex-Zeichen enthält. 🙂
– Tom Hawtin – Angelleine
8. Juli 2009 um 9:06 Uhr
“line.separator” ist sowieso nicht zuverlässig. Nur weil der Code (z. B.) unter Unix läuft, was hindert die Datei daran, Zeilentrennzeichen im Windows-Stil “\r\n” zu haben? BufferedReader.readLine() und Scanner.nextLine() suchen immer nach allen drei Arten von Trennzeichen.
– Alan Moore
9. Juli 2009 um 6:25 Uhr
Ich weiß, dass dieser Kommentar wirklich alt ist, aber … In der Frage werden Dateien überhaupt nicht erwähnt. Unter der Annahme, dass der String nicht aus einer Datei gelesen wurde, ist dieser Ansatz wahrscheinlich sicher.
– Ruck
4. Juni 2013 um 12:20 Uhr
@Jolta Dies ist selbst für manuell erstellte Strings nicht sicher. Wenn Sie unter Windows arbeiten und Ihren String mit ‘\ n’ erstellen und dann auf line.separator aufteilen, erhalten Sie keine Zeilen.
– masterxilo
4. Mai 2016 um 11:47 Uhr
Häh? Wenn ich eine Zeichenfolge auf meiner Linux-Box mit erstelle line.separator und jemand anderes liest es unter Windows mit line.separator, es ist immer noch bucklig. Das sind keine inkompetenten Programmierer, die dumme Dinge tun, es ist einfach so, wie die Dinge (nicht immer) funktionieren.
– Larry
12. Januar 2017 um 17:49 Uhr
Da mich besonders der Wirkungsgradwinkel interessierte, habe ich eine kleine Testklasse erstellt (unten). Ergebnis für 5.000.000 Zeilen:
Comparing line breaking performance of different solutions
Testing 5000000 lines
Split (all): 14665 ms
Split (CR only): 3752 ms
Scanner: 10005
Reader: 2060
Wie üblich können die genauen Zeiten variieren, aber das Verhältnis gilt, egal wie oft ich es ausgeführt habe.
Fazit: Die “einfacheren” und “effizienteren” Anforderungen des OP können nicht gleichzeitig erfüllt werden, die split Lösung (in beiden Inkarnationen) ist einfacher, aber die Reader Implementierung schlägt die anderen Hände nach unten.
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();
}
}
Ab Java8 hat der BufferedReader eine lines() Funktion, die a zurückgibt Stream<String> der Zeilen, die Sie auf Wunsch in einer Liste sammeln können, oder verarbeiten Sie den Stream.
Es macht nichts Cleveres, aber es ist schön und kompakt. Es wird auch mit Streams umgehen, und Sie können eine bekommen LineIterator auch, wenn Sie es vorziehen.
Lösung verwenden Java 8 Funktionen wie z Stream API und Method references
new BufferedReader(new StringReader(myString))
.lines().forEach(System.out::println);
oder
public void someMethod(String myLongString) {
new BufferedReader(new StringReader(myLongString))
.lines().forEach(this::parseString);
}
private void parseString(String data) {
//do something
}
Seit Java 11 gibt es eine neue Methode String.lines:
/**
* Returns a stream of lines extracted from this string,
* separated by line terminators.
* ...
*/
public Stream<String> lines() { ... }
Wenn das nicht funktioniert, versuchen Sie es mit einem Austausch \n mit \r\n.
Die Hardcodierung der Darstellung von Zeilenumbrüchen macht die Lösung plattformabhängig.
– thSoft
7. April 2015 um 15:35 Uhr
@thSoft Ich würde argumentieren, dass dasselbe gesagt werden kann nicht harcoding it – wenn Sie es nicht fest codieren, erhalten Sie auf verschiedenen Plattformen unterschiedliche Ergebnisse für dieselbe Eingabe (dh mit genau denselben Zeilenumbrüchen anstelle von plattformabhängigen Zeilenumbrüchen in der Eingabe). Dies ist nicht wirklich ein Ja/Nein und Sie müssen darüber nachdenken, was Ihr Input sein wird.
– Jiri Tousek
17. Juli 2019 um 17:09 Uhr
Ja, in der Praxis habe ich die Methode, mit der ich geantwortet habe, hunderte Male verwendet und gesehen. Es ist einfach einfacher, eine Zeile zu haben, die Ihre Textblöcke unterbricht, als die Scanner-Klasse zu verwenden. Das heißt, wenn Ihre Saite nicht ungewöhnlich massiv ist.
– Olin Kirkland
18. Juli 2019 um 6:25 Uhr
13445300cookie-checkLesen Sie den String Zeile für Zeileyes
Nun, Ihre Anforderung lautete “Zeile für Zeile lesen”, was bedeutet, dass Sie nicht alle Zeilen gleichzeitig im Speicher benötigen, also würde ich beim BufferedReader- oder Scanner-Ansatz bleiben, je nachdem, mit welchem Sie sich wohler fühlen (weiß nicht was effizienter ist). Auf diese Weise ist Ihr Speicherbedarf geringer. Es ermöglicht Ihnen auch, die Anwendung zu “skalieren”, um größere Zeichenfolgen zu verwenden, indem Sie möglicherweise in Zukunft Daten aus einer Datei lesen.
– camickr
8. Juli 2009 um 16:38 Uhr