2009-02-06 7 views
6

Qual è un modo efficiente di dividere una stringa in blocchi di 1024 byte in java? Se c'è più di un blocco, l'intestazione (stringa di dimensioni fisse) deve essere ripetuta in tutti i blocchi successivi.Dividere la stringa Java in blocchi di 1024 byte

+0

Basta controllare se siete a conoscenza che t in Java, le stringhe sono composte da caratteri e non da byte. Un char può essere più byte. – mparaz

+0

Grazie ne sono molto consapevole. Tuttavia è possibile ottenere il byte corrispondente [] di una stringa usando String.getBytes(). Questo è un problema comune quando, ad esempio, si desidera inviare il contenuto della stringa sulla rete. – user54729

+0

Perché è necessario ripetere l'intestazione, esattamente? –

risposta

5

Stringhe e byte sono due cose completamente diverse, quindi voler dividere una stringa in byte è privo di significato quanto voler dividere un dipinto in versi.

Che cosa vuoi veramente fare?

Per convertire tra stringhe e byte, è necessario specificare una codifica in grado di codificare tutti i caratteri nella stringa. A seconda della codifica e dei caratteri, alcuni di essi possono estendersi su più di un byte.

È possibile dividere la stringa in blocchi di 1024 caratteri e codificarli come byte, ma ogni blocco può essere superiore a 1024 byte.

Oppure puoi codificare la stringa originale in byte e quindi dividerli in blocchi di 1024, ma devi assicurarti di aggiungerli come byte prima di decodificare l'intero in una stringa di nuovo, oppure potresti ottenere caratteri confusi a i punti di divisione quando un personaggio occupa più di 1 byte.

Se si è preoccupati per l'utilizzo della memoria quando la stringa può essere molto lunga, è necessario utilizzare gli stream (pacchetto java.io) per l'en/decodifica e divisione, al fine di evitare di conservare i dati in memoria più volte come copie. Idealmente, dovresti evitare di avere la Stringa originale in un unico pezzo e utilizzare invece i flussi per leggerla in piccoli blocchi da dove la trovi.

7

Hai due modi, veloce e conservativo. Ma prima, devi sapere quali caratteri ci sono nella stringa. ASCII? Ci sono dieresi (caratteri compresi tra 128 e 255) o anche Unicode (s.getChar() restituisce qualcosa> 256). A seconda di ciò, sarà necessario utilizzare una codifica diversa. Se si dispone di dati binari, provare "iso-8859-1" perché conserverà i dati nella stringa. Se hai Unicode, prova "utf-8". Darò per scontato dati binari:

String encoding = "iso-8859-1"; 

Il modo più veloce:

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

noti che la stringa è Unicode, in modo che ogni personaggio deve due byte. Dovrai specificare la codifica (non fare affidamento sul "default della piattaforma". Questo causerà solo dolore in seguito).

Ora si può leggere in 1024 pezzi utilizzando

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

Questo ha bisogno di circa tre volte di più RAM come la stringa originale.

Un modo più conservativo di memoria è scrivere un convertitore che accetta StringReader e OutputStreamWriter (che include un ByteArrayOutputStream). Copia byte dal lettore allo scrittore fino a quando il buffer sottostante contiene un blocco di dati:

Quando lo fa, copia i dati nell'output reale (anteporre l'intestazione), copia i byte aggiuntivi (che Unicode-> byte può aver generato una conversione) su un buffer temporaneo, chiamare buffer.reset() e scrivere il buffer temporaneo nel buffer.

codice simile a questo (non testata):

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

Questo bisogno solo di un paio di kilobyte di RAM.

[EDIT] C'è stata una lunga discussione sui dati binari in Stringhe nei commenti. Prima di tutto, è perfettamente sicuro mettere i dati binari in una stringa fintanto che si presta attenzione durante la creazione e la memorizzazione da qualche parte. Per creare tale stringa, prendere un array di byte [] e:

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

In Java, ISO-8859-1 (a.k.a ISO-Latin1) è un 1: 1 mapping. Ciò significa che i byte nell'array non verranno interpretati in alcun modo. Ora è possibile utilizzare substring() e simili sui dati o cercare con indice, eseguire regexp di su di esso, ecc, ad esempio, trovare la posizione di un 0 byte:

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

Ciò è particolarmente utile se non si conosce la codifica dei dati e si vuole dare un'occhiata prima che qualche codec si mischi con esso.

per scrivere i dati da qualche parte, l'operazione inversa è:

byte [] = dati al sicuro.getBytes ("iso-8859-1");

Non utilizzare mai i metodi predefiniti new String(array) o String.getBytes()! Un giorno, il codice verrà eseguito su una piattaforma diversa e si romperà.

Ora il problema dei caratteri> 255 nella stringa. Se usi questo metodo, non avrai mai un tale personaggio nelle tue stringhe. Detto questo, se ci fosse qualche motivo, allora getBytes() genererebbe un'eccezione perché non c'è modo di esprimere tutti i caratteri Unicode in ISO-Latin1, quindi sei al sicuro nel senso che il codice non fallirà in modo silenzioso.

Alcuni potrebbero sostenere che questo non è abbastanza sicuro e non si dovrebbe mai mescolare byte e stringa. In questo giorno di un'età, non abbiamo quel lusso. Molti dati non hanno informazioni di codifica esplicite (i file, per esempio, non hanno un attributo "encoding" nello stesso modo in cui hanno permessi di accesso o un nome). XML è uno dei pochi formati con informazioni di codifica esplicite e ci sono editor come Emacs o jEdit che usano i commenti per specificare queste informazioni vitali. Ciò significa che, quando si elaborano flussi di byte, è necessario sapere sempre in quale codifica essi sono. A partire da ora, non è possibile scrivere codice che funzioni sempre, indipendentemente da dove provengano i dati.

Anche con XML, è necessario leggere l'intestazione del file come byte per determinare la codifica prima di poter decodificare la carne.

Il punto importante è sedersi e capire quale codifica è stata utilizzata per generare il flusso di dati che si deve elaborare. Se lo fai, sei bravo, se non lo fai, sei condannato. La confusione deriva dal fatto che molte persone non sono consapevoli del fatto che lo stesso byte può significare cose diverse a seconda della codifica o anche che ci sia più di una codifica. Inoltre, sarebbe stato d'aiuto se Sun non avesse introdotto la nozione di "codifica predefinita della piattaforma".

Punti importanti per i principianti:

  • c'è più di una codifica (set di caratteri).
  • Ci sono più caratteri di quelli utilizzati dalla lingua inglese. Ci sono anche diversi sets of digits (ASCII, larghezza intera, arabo-Indic, bengalese).
  • È necessario sapere quale codifica è stata utilizzata per generare i dati che si stanno elaborando.
  • È necessario conoscere la codifica da utilizzare per scrivere i dati che si stanno elaborando.
  • È necessario conoscere il modo corretto per specificare queste informazioni di codifica in modo che il programma successivo possa decodificare l'output (intestazione XML, meta tag HTML, commento di codifica speciale, qualunque sia).

I giorni di ASCII sono finiti.

+0

Questo avrebbe sofferto del problema che stava citando kdgregory? In base alla codifica predefinita della piattaforma, è possibile dividere un singolo carattere in due parti prive di significato. – user54729

+0

Si prega di non utilizzare "iso-8859-1". Usa "utf8". UTF8 gestisce praticamente tutto iso-8859-1 in un singolo byte, ma può scalare per gestire tutti i caratteri. Sì, sconosciuto, questo potrebbe dividere un singolo personaggio in due pezzi insignificanti ... o buttarli via, il che è ciò che farebbe l'iso-8859-1. –

+0

No, perché sto specificando la codifica "iso-8859-1" (che è Latin-1, cioè ASCII con Umlauts). Se la tua stringa contiene altri caratteri (sopra il codice 256), devi usare qualcos'altro qui, ma Latin-1 di solito è buono perché non cambia nulla. –

2

so che sono in ritardo, ma ero alla ricerca di una soluzione me e poi hanno trovato la mia risposta come migliore risposta:

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

Esempio:

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

uscita:

abced 
fghij 
klmno 
pqrst 
uvwxy 
z 
+0

Questa è una risposta molto utile ... grazie @Alan Deep .. – Kushal

+1

Sono contento. @Kushal –