Su
Dada una cadena que no es demasiado larga, ¿cuál es la mejor manera de leerla línea por línea?
Sé que puedes hacer:
BufferedReader reader = new BufferedReader(new StringReader(<string>));
reader.readLine();
Otra forma sería tomar la subcadena en el eol:
final String eol = System.getProperty("line.separator");
output = output.substring(output.indexOf(eol + 1));
¿Alguna otra forma quizás más simple de hacerlo? No tengo problemas con los enfoques anteriores, solo me interesa saber si alguno de ustedes sabe algo que pueda parecer más simple y eficiente.
noop
También hay Scanner
. Puedes usarlo como el BufferedReader
:
Scanner scanner = new Scanner(myString);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// process the line
}
scanner.close();
Creo que este es un enfoque un poco más limpio que los dos sugeridos.
-
Sin embargo, no creo que sea una comparación justa: String.split se basa en que toda la entrada se lee en la memoria, lo que no siempre es factible (por ejemplo, para archivos grandes).
– Adamski
8 de julio de 2009 a las 8:00
-
La entrada tiene que residir en la memoria, dado que la entrada es String. La sobrecarga de memoria es la matriz. Además, las cadenas resultantes reutilizan la misma matriz de caracteres de back-end.
– noop
9 de julio de 2009 a las 13:21
-
Cuidado Scanner puede producir resultados erróneos si escanea un archivo UTF-8 con caracteres Unicode y no especifica la codificación en Scanner. Podría interpretar un carácter diferente como final de línea. En Windows utiliza su codificación predeterminada.
– vive el amor
7 de noviembre de 2017 a las 4:12
ftl
También puede utilizar el split
método de cadena:
String[] lines = myString.split(System.getProperty("line.separator"));
Esto le da todas las líneas en una matriz práctica.
No sé sobre el rendimiento de la división. Utiliza expresiones regulares.
-
Y espero que el separador de línea no tenga caracteres regulares. 🙂
– Tom Hawtin – tachuela
8 de julio de 2009 a las 9:06
-
“line.separator” no es confiable de todos modos. Solo porque el código se ejecuta en (por ejemplo) Unix, ¿qué impide que el archivo tenga separadores de línea “\r\n” al estilo de Windows? BufferedReader.readLine() y Scanner.nextLine() siempre verifican los tres estilos de separador.
–Alan Moore
9 de julio de 2009 a las 6:25
-
Sé que este comentario es muy antiguo, pero… La pregunta no menciona archivos en absoluto. Suponiendo que String no se leyó de un archivo, este enfoque probablemente sea seguro.
– Jolta
04/06/2013 a las 12:20
-
@Jolta Esto no es seguro incluso para cadenas construidas manualmente, si está en Windows y construyó su cadena con ‘\ n’ y luego se divide en línea. Separador no obtiene líneas.
– maestroxilo
4 mayo 2016 a las 11:47
-
¿Eh? Si creo una cadena en mi caja de Linux usando
line.separator
y alguien más lo lee en Windows usandoline.separator
, todavía está jorobado. No se trata de codificadores incompetentes por hacer cosas estúpidas, es solo cómo funcionan las cosas (no siempre).– Larry
12 de enero de 2017 a las 17:49
Como estaba especialmente interesado en el ángulo de la eficiencia, creé una pequeña clase de prueba (abajo). Resultado para 5,000,000 líneas:
Comparing line breaking performance of different solutions
Testing 5000000 lines
Split (all): 14665 ms
Split (CR only): 3752 ms
Scanner: 10005
Reader: 2060
Como de costumbre, los tiempos exactos pueden variar, pero la proporción se mantiene independientemente de la frecuencia con la que la haya ejecutado.
Conclusión: los requisitos “más simples” y “más eficientes” del OP no pueden satisfacerse simultáneamente, el split
solución (en cualquier encarnación) es más simple, pero el Reader
la implementación supera a las demás.
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();
}
}
-
A partir de Java8, el BufferedReader tiene un
lines()
función que devuelve unStream<String>
de las líneas, que puede recopilar en una lista si lo desea, o procesar la transmisión.– Steve K.
30 de septiembre de 2015 a las 5:54
Brian Agnew
Usando Apache Commons IOUtils Puedes hacerlo muy bien a través de
List<String> lines = IOUtils.readLines(new StringReader(string));
No está haciendo nada inteligente, pero es agradable y compacto. También manejará flujos, y puede obtener un LineIterator
también si lo prefieres.
Solución usando Java 8
características tales como Stream API
y Method references
new BufferedReader(new StringReader(myString))
.lines().forEach(System.out::println);
o
public void someMethod(String myLongString) {
new BufferedReader(new StringReader(myLongString))
.lines().forEach(this::parseString);
}
private void parseString(String data) {
//do something
}
Desde Java 11, hay un nuevo método. String.lines
:
/**
* Returns a stream of lines extracted from this string,
* separated by line terminators.
* ...
*/
public Stream<String> lines() { ... }
Uso:
"line1\nline2\nlines3"
.lines()
.forEach(System.out::println);
tisho
También puedes usar:
String[] lines = someString.split("\n");
Si eso no funciona, intente reemplazar \n
con \r\n
.
-
Codificar la representación de nueva línea hace que la solución dependa de la plataforma.
– suave
07/04/2015 a las 15:35
-
@thSoft Yo diría que se puede decir lo mismo sobre no codificar it: si no lo codifica, obtendrá un resultado diferente en diferentes plataformas para la misma entrada (es decir, con exactamente los mismos saltos de línea en lugar de saltos de línea dependientes de la plataforma en la entrada). Esto no es realmente un sí/no y tienes que pensar cuál será tu entrada.
–Jiri Tousek
17 de julio de 2019 a las 17:09
-
Sí, en la práctica he usado y visto el método con el que respondí cientos de veces. Es más sencillo tener una línea que divida los fragmentos de texto que usar la clase Scanner. Es decir, si su cadena no es anormalmente masiva.
–Olin Kirkland
18 de julio de 2019 a las 6:25
Bueno, su requisito decía “léalo línea por línea”, lo que implica que no necesita todas las líneas en la memoria al mismo tiempo, por lo que me quedaría con el enfoque BufferedReader o Scanner, cualquiera con el que se sienta más cómodo (no sé que es más eficiente). De esta manera, sus requisitos de memoria son menores. También le permitirá “ampliar” la aplicación para usar cadenas más grandes al leer potencialmente datos de un archivo en el futuro.
– camickr
8 de julio de 2009 a las 16:38