Pregunta

¿Cuál es una forma eficiente de dividir una cadena en fragmentos de 1024 bytes en Java?Si hay más de un fragmento, entonces el encabezado (cadena de tamaño fijo) debe repetirse en todos los fragmentos posteriores.

¿Fue útil?

Solución

Cuerdas y bytes son dos cosas completamente diferentes, por lo que querer dividir una cadena en bytes es tan poco sentido como querer dividir un cuadro en versos.

¿Qué es lo que realmente quiere hacer?

Para las conversiones entre cadenas y bytes, es necesario especificar una codificación que puede codificar todos los caracteres de la cadena. Dependiendo de la codificación y los personajes, algunos de ellos pueden abarcar más de un byte.

Usted puede dividir la cadena en trozos de 1.024 caracteres y codificar los bytes como, pero luego cada trozo puede contener más de 1024 bytes.

O puede codificar la cadena original en bytes y luego partirlas en trozos de 1024, pero entonces usted tiene que asegurarse de que los añade como bytes antes de decodificar el conjunto en una cadena de nuevo, o puede tener caracteres ilegibles en el puntos de división cuando un personaje se extiende más allá de 1 byte.

Si usted está preocupado por el uso de memoria cuando la cadena puede ser muy largo, se debe utilizar corrientes (paquete java.io) hasta que la linea / decodificación y la división, con el fin de evitar que se mantengan los datos en la memoria varias veces copias. Idealmente, usted debe evitar que la cadena original en una sola pieza en absoluto y en lugar de utilizar las corrientes de leerlo en pequeños trozos de donde se obtiene de.

Otros consejos

Hay dos formas, el ayuno y la forma conservadora de memoria. Pero en primer lugar, lo que necesita saber cómo son los personajes en la cadena. ASCII? ¿Hay diéresis (caracteres entre 128 y 255) o incluso Unicode (s.getChar () devuelve algo> 256). En función de eso, usted tendrá que utilizar una codificación diferente. Si tiene datos binarios, trate de "iso-8859-1" porque va a conservar los datos en la cadena. Si tiene Unicode, intente "UTF-8". Vamos a suponer datos binarios:

String encoding = "iso-8859-1";

La forma más rápida:

ByteArrayInputStream in = new ByteArrayInputStream (string.getBytes(encoding));

Tenga en cuenta que la cadena es Unicode, por lo que las necesidades de cada carácter dos bytes. Tendrá que especificar la codificación (no se basan en la "plataforma por defecto". Esto sólo hará que el dolor más adelante).

Ahora puede leerlo en 1024 utilizando trozos

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > 0) { ... }

Esto necesita aproximadamente tres veces más memoria RAM como la cadena original.

A más memoria de forma conservadora es escribir un convertidor que tiene un StringReader y un OutputStreamWriter (que se ajusta un ByteArrayOutputStream). Copia los bytes desde el lector al escritor hasta que el búfer subyacente contiene un fragmento de datos:

Cuando lo hace, copiar los datos a la salida real (anteponiendo la cabecera), copiar los bytes adicionales (que el Unicode-> conversión de bytes puede haber generado) a un búfer temporal, llame buffer.reset () y escribir el tampón temp a la memoria intermedia.

código es el siguiente (no probado):

StringReader r = new StringReader (string);
ByteArrayOutputStream buffer = new ByteArrayOutputStream (1024*2); // Twice as large as necessary
OutputStreamWriter w = new OutputStreamWriter  (buffer, encoding);

char[] cbuf = new char[100];
byte[] tempBuf;
int len;
while ((len = r.read(cbuf, 0, cbuf.length)) > 0) {
    w.write(cbuf, 0, len);
    w.flush();
    if (buffer.size()) >= 1024) {
        tempBuf = buffer.toByteArray();
        ... ready to process one chunk ...
        buffer.reset();
        if (tempBuf.length > 1024) {
            buffer.write(tempBuf, 1024, tempBuf.length - 1024);
        }
    }
}
... check if some data is left in buffer and process that, too ...

Esto sólo necesita un par de kilobytes de memoria RAM.

[EDIT] Ha habido una larga discusión sobre los datos binarios en cadenas en los comentarios. En primer lugar, es perfectamente seguro para poner los datos binarios en una cadena, siempre y cuando se tiene cuidado al crear y almacenarlo en algún lugar. Para crear una cadena tal, tomar a [] matriz de bytes y:

String safe = new String (array, "iso-8859-1");

En Java, ISO-8859-1 (a.k.a ISO-Latin1) es un 1: mapeo de 1. Esto significa que los bytes de la matriz no se interpretarán de ninguna manera. Ahora puede utilizar substring () y similares en los datos o buscarla con el índice de gestión de expresiones regulares sobre él, etc. Por ejemplo, encontrar la posición de un byte 0:

int pos = safe.indexOf('\u0000');

Esto es especialmente útil si no conoce la codificación de los datos y desea tener una mirada en ella antes de que algunos líos codec con él.

Para escribir los datos en alguna parte, la operación inversa es:

byte [] = datos safe.getBytes ( "iso-8859-1");

No utilice los métodos predeterminados new String(array) o String.getBytes() Un día, su código va a ser ejecutado en una plataforma diferente y se romperá.

Ahora el problema de caracteres> 255 en la cadena. Si se utiliza este método, no tendrá nunca estos caracteres en sus cadenas. Dicho esto, si hubiera alguna, por alguna razón, entonces getBytes () lanzaría una excepción porque no hay manera de expresar todos los caracteres Unicode en la norma ISO-Latin1, por lo que está seguro en el sentido de que el código no fallará en silencio.

Algunos podrían argumentar que esto no es lo suficientemente seguro y nunca se debe mezclar bytes y de cadena. En este día una edad, no tenemos ese lujo. Una gran cantidad de datos no tiene información de codificación explícita (archivos, por ejemplo, no tienen un atributo "codificación" de la misma manera ya que tienen permisos de acceso o un nombre). XML es uno de los pocos formatos de codificación que tiene información explícita y hay editores como Emacs o jEdit que utilizan los comentarios para especificar esta información vital. Esto significa que, al procesar flujos de bytes, siempre se debe saber en qué codificación que son. A partir de ahora, no es posible escribir código que siempre va a funcionar, no importa dónde proceden los datos.

A pesar de XML, debe leer el encabezado del archivo como bytes para determinar la codificación antes de poder decodificar la carne.

El punto importante es sentarse y averiguar qué codificación se utilizó para generar el flujo de datos que hay que procesar. Si lo hace, usted es bueno, si no lo haces, estás condenado. La confusión se origina en el hecho de que la mayoría de las personas no son conscientes de que el mismo byte puede significar cosas diferentes dependiendo de la codificación o incluso that existe más de una codificación. Además, habría ayudado si Sun no se había introducido el concepto de "plataforma de codificación por defecto."

Puntos importantes para los principiantes:

  • Hay más de una codificación (charset).
  • Hay más caracteres que utiliza el idioma Inglés. Incluso hay varios (ASCII, ancho, Arabic- Indic, bengalí).
  • Es necesario saber qué codificación se utilizó para generar los datos que se va a procesar.
  • Usted debe saber qué codificación se debe utilizar para escribir los datos que está procesando.
  • Debe conocer la forma correcta de especificar esta información de codificación por lo que el próximo programa puede decodificar la salida (cabecera XML, HTML etiqueta meta, la codificación de un comentario especial, lo que sea).

Los días de ASCII son más.

Sé que estoy tarde, sin embargo yo estaba buscando una solución a mí mismo y luego encontrado mi respuesta como mejor respuesta:

private static String chunk_split(String original, int length, String separator) throws IOException {
    ByteArrayInputStream bis = new ByteArrayInputStream(original.getBytes());
    int n = 0;
    byte[] buffer = new byte[length];
    String result = "";
    while ((n = bis.read(buffer)) > 0) {
        for (byte b : buffer) {
            result += (char) b;
        }
        Arrays.fill(buffer, (byte) 0);
        result += separator;
    }
    return result;
}

Ejemplo :

public static void main(String[] args) throws IOException{
       String original = "abcdefghijklmnopqrstuvwxyz";
       System.out.println(chunk_split(original,5,"\n"));
}

Salida

abced
fghij
klmno
pqrst
uvwxy
z

Yo estaba tratando esto por mí mismo, necesito un enorme trozo de cuerda (casi el 10 MB) por 1 MB. Esto ayuda a fragmentar los datos en cantidad mínima de tiempo. (Menos de un segundo).

private static ArrayList<String> chunkLogMessage(String logMessage) throws Exception {
    ArrayList<String> messages = new ArrayList<>();
    if(logMessage.getBytes().length > CHUNK_SIZE) {
        Log.e("chunk_started", System.currentTimeMillis()+"");
        byte[] buffer = new byte[CHUNK_SIZE];
        int start = 0, end = buffer.length;
        long remaining = logMessage.getBytes().length;
        ByteArrayInputStream inputStream = new ByteArrayInputStream(logMessage.getBytes());
        while ((inputStream.read(buffer, start, end)) != -1){
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(buffer, start, end);
            messages.add(outputStream.toString("UTF-8"));
            remaining = remaining - end;
            if(remaining <= end){
                end = (int) remaining;
            }
        }
        Log.e("chunk_ended", System.currentTimeMillis()+"");
        return messages;
    }
    messages.add(logMessage);
    return messages;
}

Logcat:

22:08:00.262 3382-3425/com.sample.app E/chunk_started: 1533910080261
22:08:01.228 3382-3425/com.sample.app E/chunk_ended: 1533910081228
22:08:02.468 3382-3425/com.sample.app E/chunk_started: 1533910082468
22:08:03.478 3382-3425/com.sample.app E/chunk_ended: 1533910083478
22:09:19.801 3382-3382/com.sample.app E/chunk_started: 1533910159801
22:09:20.662 3382-3382/com.sample.app E/chunk_ended: 1533910160662

Sí, la mayoría, si no todo, de lo anterior definitivamente funcionaría.

O podrías consultar este proyecto que hace exactamente eso;sólo que es capaz de fragmentar no sólo cadenas, sino también matrices de bytes, flujos de entrada y archivos.

Tiene 2 clases: DataChunker y StringChunker


DataChunker chunker = new DataChunker(8192, blob) {
@Override 
public void chunkFound(byte[] foundChunk, int bytesProcessed) {
//process chunk here
}
@Override 
public void chunksExhausted(int bytesProcessed) { 
//called when all the blocks have been exhausted
} 
};

String blob = "Experience is wasted if history does not repeat itself...Gbemiro Jiboye";

 final StringBuilder builder = new StringBuilder();
        StringChunker chunker = new StringChunker(4, blob) {
            @Override
            public void chunkFound(String foundChunk, int bytesProcessed) {
                builder.append(foundChunk);
                System.out.println("Found: "+foundChunk+", bytesProcessed: "+bytesProcessed+" bytes");
            }

            @Override
            public void chunksExhausted(int bytesProcessed) {
                System.out.println("Processed all of: "+bytesProcessed+" bytes. Rebuilt string is: "+builder.toString());
            }
        };

El blob en el constructor Datachunker's constructor es una matriz de bytes, un File o un InputStream

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top