Pergunta

O que é uma maneira eficiente de dividir uma string em pedaços de 1024 bytes em java? Se houver mais de um bloco, em seguida, o cabeçalho (string tamanho fixo) precisa ser repetido em todos os pedaços subseqüentes.

Foi útil?

Solução

Cordas e bytes são duas coisas completamente diferentes, assim querendo dividir uma string em bytes é tão sem sentido quanto querer dividir uma pintura em versos.

O que é que você realmente quer fazer?

Para converter entre strings e bytes, você precisa especificar uma codificação que pode codificar todos os caracteres na cadeia. Dependendo da codificação e os personagens, alguns deles podem abranger mais de um byte.

Você pode dividir a string em pedaços de 1024 caracteres e codificá-los como bytes, mas, em seguida, cada pedaço pode ser mais do que 1024 bytes.

Ou você pode codificar a string original em bytes e, em seguida, dividi-los em pedaços de 1024, mas então você tem que ter certeza de acrescentar-los como bytes antes de decodificar o todo em uma String de novo, ou você pode ficar ilegível caracteres no pontos de divisão quando um personagem abrange mais de 1 byte.

Se você está preocupado com o uso de memória quando a corda pode ser muito longo, você deve usar correntes (pacote java.io) para o en / decodificação e divisão, a fim de evitar manter os dados na memória várias vezes como cópias. Idealmente, você deve evitar que a cadeia original em uma única peça em tudo e em vez uso córregos para lê-lo em pequenos pedaços de onde você tira isso.

Outras dicas

Você tem dois caminhos, o rápido ea forma conservadora memória. Mas, primeiro, você precisa saber o que personagens estão na cadeia. ASCII? Existem tremas (caracteres entre 128 e 255) ou mesmo Unicode (s.getChar () retorna algo> 256). Dependendo disso, você precisará usar uma codificação diferente. Se você tiver dados binários, tente "iso-8859-1", porque vai preservar os dados na cadeia. Se você tem Unicode, tente "utf-8". Eu vou assumir dados binários:

String encoding = "iso-8859-1";

A maneira mais rápida:

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

Note que a cadeia é Unicode, por isso, todas as necessidades de caracteres dois bytes. Você terá que especificar a codificação (não contar com o "default plataforma". Isso só vai causar dor mais tarde).

Agora você pode lê-lo em 1024 pedaços usando

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

Isso precisa cerca de três vezes mais memória RAM quanto a string original.

Uma maneira conservadora mais memória é escrever um conversor que leva um StringReader e um OutputStreamWriter (que envolve um ByteArrayOutputStream). Copiar bytes a partir do leitor para o gravador até que o buffer subjacente contém um pedaço de dados:

Quando isso acontece, copiar os dados para a saída real (antecedendo o cabeçalho), copiar os bytes adicionais (que o Unicode-> conversão de byte podem ter gerado) a um tampão de temperatura, buffer.reset chamada () e a escrever tampão temporário para tamponar.

código é assim (não testado):

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 ...

Isso só precisa de um par de kilobytes de memória RAM.

[EDIT] Houve uma longa discussão sobre os dados binários em Cordas nos comentários. Primeiro de tudo, é perfeitamente seguro para colocar dados binários em uma String, desde que você tome cuidado ao criar-lo e armazená-lo em algum lugar. Para criar uma tal cadeia, tomar uma matriz de bytes [] e:

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

Em Java, iso-8859-1 (a.k.a ISO-Latim1) é uma mistura 1: 1 de mapeamento. Isto significa que os bytes no conjunto não serão interpretados de qualquer forma. Agora você pode usar substring () e similares sobre os dados ou procurá-la com o índice, de nele regexp correr, etc. Por exemplo, a posição de um 0-byte:

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

Isto é especialmente útil se você não sabe a codificação dos dados e quer ter um olhar para ele antes de alguns messes codec com ele.

Para escrever a um lugar de dados, a operação inversa é:

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

Nunca use o padrão métodos new String(array) ou String.getBytes()! Um dia, seu código vai ser executado em uma plataforma diferente e ele vai quebrar.

Agora o problema de caracteres> 255 na cadeia. Se você usar esse método, você nunca terá qualquer personagem em suas cordas. Dito isto, se havia algum por algum motivo, então getBytes () iria lançar uma exceção, porque não há maneira de expressar todos os caracteres Unicode em ISO-Latin1, então você está seguro no sentido de que o código não irá falhar silenciosamente.

Alguns podem argumentar que isso não é suficiente seguro e você nunca deve misturar bytes e Cadeia. Neste dia uma idade, não temos esse luxo. A grande quantidade de dados não tem nenhuma informação de codificação explícita (arquivos, por exemplo, não têm um atributo "codificação" da mesma forma como eles têm permissões de acesso ou um nome). XML é um dos poucos formatos que tem informação de codificação explícita e há editores como o Emacs ou jEdit que usam comentários para especificar esta informação vital. Isto significa que, quando o processamento fluxos de bytes, você deve sempre saber em qual codificação que são. A partir de agora, não é possível escrever código que irá funcionar sempre, não importa onde vêm os dados.

Mesmo com XML, você deve ler o cabeçalho do arquivo como bytes para determinar a codificação antes que você possa decodificar a carne.

O ponto importante é sentar e descobrir qual codificação foi usada para gerar o fluxo de dados que você tem que processar. Se você fizer isso, você é bom, se você não fizer isso, você está condenado. Os origina confusão deo fato de que a maioria das pessoas não estão cientes de que o mesmo byte pode significar coisas diferentes, dependendo da codificação ou mesmo que há mais de uma codificação. Além disso, ele teria ajudado se a Sun não tinha introduzido a noção de "encoding plataforma padrão."

Pontos importantes para iniciantes:

  • Há mais de uma codificação (conjunto de caracteres).
  • Existem mais caracteres do que os usos da língua inglesa. Há ainda vários de dígitos (ASCII, largura total, Arabic- Indic, Bengali).
  • Você deve saber qual codificação foi usada para gerar os dados que você está processando.
  • Você deve saber qual codificação você deve usar para gravar os dados que você está processando.
  • Você deve saber a maneira correta de especificar esta informação de codificação para que o próximo programa pode decodificar a sua saída (cabeçalho XML, HTML meta tag, comentário especial de codificação, o que for).

Os dias de ASCII são mais.

Eu sei que estou atrasado, no entanto eu estava procurando uma solução mim e, em seguida, encontrei a minha resposta como melhor resposta:

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;
}

Exemplo :

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

saída :

abced
fghij
klmno
pqrst
uvwxy
z

Eu estava tentando isso para mim, eu preciso pedaço um enorme String (quase 10 MB) por 1 MB. Isso ajuda pedaço dos dados em quantidade mínima de tempo. (Menos de um 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

Sim, a maioria, se não todos os acima seria definitivamente trabalho.

Ou você poderia verificar este projecto que faz exatamente isso; só ele é capaz de pedaço não apenas strings, mas também matrizes de bytes, InputStreams e arquivos.

Tem 2 classes: DataChunker e 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());
            }
        };

O blob no construtor construtor Datachunker's ou é uma matriz de bytes, um File ou um InputStream

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top