java - clase - inputstreamreader
¿Longitud máxima de línea para BufferedReader.readLine() en Java? (6)
Uso el método readLine()
BufferedReader para leer líneas de texto desde un socket.
No hay una manera obvia de limitar la longitud de la línea leída.
Me preocupa que la fuente de los datos pueda (maliciosamente o por error) escribir muchos datos sin ningún carácter de salto de línea, y esto hará que BufferedReader asigne una cantidad ilimitada de memoria.
¿Hay alguna manera de evitar eso? ¿O tengo que implementar una versión limitada de readLine()
?
El límite para una cadena es de 2 mil millones de caracteres. Si desea que el límite sea menor, debe leer los datos usted mismo. Puede leer un char a la vez desde el flujo en búfer hasta que se alcance el límite o una nueva línea char.
En BufferedReader
, en lugar de usar String readLine()
, use int read(char[] cbuf, int off, int len)
; luego puede usar boolean ready()
para ver si lo tiene todo y convertirlo en una cadena usando el constructor String(byte[] bytes, int offset, int length)
.
Si no te importa el espacio en blanco y solo quieres tener un número máximo de caracteres por línea, entonces la propuesta que Stephen sugirió es realmente simple.
import java.io.BufferedReader;
import java.io.IOException;
public class BoundedReader extends BufferedReader {
private final int bufferSize;
private char buffer[];
BoundedReader(final BufferedReader in, final int bufferSize) {
super(in);
this.bufferSize = bufferSize;
this.buffer = new char[bufferSize];
}
@Override
public String readLine() throws IOException {
int no;
/* read up to bufferSize */
if((no = this.read(buffer, 0, bufferSize)) == -1) return null;
String input = new String(buffer, 0, no).trim();
/* skip the rest */
while(no >= bufferSize && ready()) {
if((no = read(buffer, 0, bufferSize)) == -1) break;
}
return input;
}
}
Editar: esto pretende leer líneas de un terminal de usuario. Bloquea hasta la siguiente línea y devuelve una String
bufferSize
búfer; Cualquier entrada adicional en la línea se descarta.
Hay algunas maneras de evitar esto:
- si la cantidad de datos en general es muy pequeña, cargue los datos desde el socket en un búfer (matriz de bytes, bytebuffer, dependiendo de lo que prefiera), luego ajuste BufferedReader alrededor de los datos en la memoria (a través de un ByteArrayInputStream, etc.);
- solo captura el OutOfMemoryError, si ocurre; la captura de este error generalmente no es confiable, pero en el caso específico de la captura de fallas en la asignación de matrices, es básicamente segura (pero no resuelve el problema de cualquier efecto de detonación que un subproceso que asigna grandes cantidades del montón podría tener en otros subprocesos) ejecutando en su aplicación, por ejemplo);
- implemente un InputStream contenedor que solo leerá tantos bytes, luego inserte esto entre el socket y BufferedReader;
- ditch BufferedReader y divida sus líneas a través del marco de expresiones regulares (implemente un CharSequence cuyos caracteres se extraen del flujo y luego defina una expresión regular que limite la longitud de las líneas); en principio, se supone que CharSequence es un acceso aleatorio, pero para una simple expresión regular de "división de líneas", en la práctica es probable que siempre se soliciten caracteres sucesivos, de modo que pueda "hacer trampa" en su implementación.
La forma más sencilla de hacerlo será implementar su propio lector de línea limitada.
O incluso más simple, reutilice el código de esta clase BoundedBufferedReader
.
En realidad, la codificación de un readLine()
que funciona igual que el método estándar no es trivial. Tratar los 3 tipos de terminadores de línea CORRECTAMENTE requiere una codificación bastante cuidadosa. Es interesante comparar los diferentes enfoques del enlace anterior con la versión de Sun y la versión de Apache Harmony de BufferedReader.
Nota: No estoy completamente convencido de que la versión limitada o la versión de Apache sean 100% correctas. La versión limitada supone que la secuencia subyacente admite la marca y el restablecimiento, lo que ciertamente no siempre es cierto. La versión de Apache parece leer por adelantado un carácter si ve un CR como el último carácter en el búfer. Esto se rompería en MacOS al leer la entrada escrita por el usuario. La versión de Sun se encarga de esto configurando una bandera para hacer que el posible LF después de la CR se omita en la siguiente operación de read...
; es decir, no lectura falsa por delante.
Otra opción es BoundedInputStream Apache Commons:
InputStream bounded = new BoundedInputStream(is, MAX_BYTE_COUNT);
BufferedReader reader = new BufferedReader(new InputStreamReader(bounded));
String line = reader.readLine();
Quizás la solución más fácil es adoptar un enfoque ligeramente diferente. En lugar de intentar evitar un DoS limitando una lectura en particular, limite la cantidad total de datos en bruto. De esta manera, no necesita preocuparse por el uso de un código especial para cada lectura y bucle, siempre que la memoria asignada sea proporcional a los datos entrantes.
Puede medir el Reader
, o probablemente más adecuadamente, el Stream
decodificado o equivalente.