2015-07-20 10 views
9

Ho una stringa grande in Go e mi piacerebbe dividerlo in blocchi più piccoli. Ogni chunk dovrebbe essere al massimo 10kb. I pezzi dovrebbero essere divisi su rune (non nel mezzo di una runa).Divide una stringa in blocchi da 10kb in Go

Qual è il modo idiomatico di farlo in go? Dovrei semplicemente fare il giro del range dei byte di stringa? Mi mancano alcuni pacchetti stdlib utili?

risposta

3

Il modo idiomatico di dividere una stringa (o qualsiasi slice o matrice) è usando slicing. Dato che vuoi dividere per runa, dovresti scorrere l'intera stringa poiché non sai in anticipo quanti byte ogni slice conterrà.

slices := []string{} 
count := 0 
lastIndex := 0 
for i, r := range longString { 
    count++ 
    if count%10001 == 0 { 
     slices = append(slices, longString[lastIndex:i]) 
     lastIndex = i 
    } 
} 

Avviso: non ho eseguito o testato questo codice, ma trasmette i principi generali. Fare il ciclo su un loop di corde sulle rune e non sui byte, automatically decoding the UTF-8 per te. E utilizzando l'operatore slice []represents your new strings as subslices di longString significa che non è necessario copiare alcun byte dalla stringa.

Si noti che i è l'indice di byte nella stringa e può essere incrementato di più di 1 in ogni iterazione del ciclo.

EDIT:

Spiacente, non ho visto che si voleva limitare il numero di byte, non punti di codice Unicode. Puoi implementarlo anche relativamente facilmente.

slices := []string{} 
lastIndex := 0 
lastI := 0 
for i, r := range longString { 
    if i-lastIndex > 10000 { 
     slices = append(slices, longString[lastIndex:lastI]) 
     lastIndex = lastI 
    } 
    lastI = i 
} 

A working example at play.golang.org, che si occupa anche dei byte rimanenti alla fine della stringa.

+0

Ricorda che, poiché non vengono copiati byte dalla stringa, GC non libera la stringa mentre si mantiene un riferimento ad almeno un blocco. –

+0

Se la runa inizia in un blocco e termina in un altro, il codice lo interromperà. –

+0

@AlexAtNet 'i' è sempre l'indice di byte del primo byte nella runa, quindi la sottocategoria immediatamente prima di qualsiasi' i' non dovrebbe interrompere alcuna rune. Non ho ancora testato il codice e potrebbero esserci dei bug. –

8

Utilizzare RuneStart per cercare un limite di runa. Taglia la stringa al limite.

var chunks []string 
for len(s) > 10000 { 
    i := 10000 
    for i >= 10000 - utf8.UTFMax && !utf8.RuneStart(s[i]) { 
     i-- 
    } 
    chunks = append(chunks, s[:i]) 
    s = s[i:] 
} 
if len(s) > 0 { 
    chunks = append(chunks, s) 
} 

Utilizzando l'approccio, l'applicazione esamina alcuni byte ai limiti del chunk anziché all'intera stringa.

Il codice è scritto per garantire il progresso quando la stringa non è una codifica UTF-8 valida. Potresti voler gestire questa situazione come un errore o dividere la stringa in un modo diverso.

playground example

+0

Non stai tenendo conto dei caratteri da 1 byte alla fine della stringa (quelli che iniziano con 0 ... in formato binario). – thwd

+1

Hai ragione, ho ripassato il codice ed è corretto e molto conciso, mi piace molto. – thwd

0

Partenza this code:

package main 

import (
    "fmt" 
    "math/rand" 
    "time" 
) 

func init() { 
    rand.Seed(time.Now().UnixNano()) 
} 

var alphabet = []rune{ 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 
    'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'æ', 'ø', 'å', 'A', 'B', 'C', 
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Æ', 'Ø', 'Å', 
} 

func randomString(n int) string { 
    b := make([]rune, n, n) 
    for k, _ := range b { 
     b[k] = alphabet[rand.Intn(len(alphabet))] 
    } 
    return string(b) 
} 

const (
    chunkSize int = 100 
    lead4Mask byte = 0xF8 // must equal 0xF0 
    lead3Mask byte = 0xF0 // must equal 0xE0 
    lead2Mask byte = 0xE0 // must equal 0xC0 
    lead1Mask byte = 0x80 // must equal 0x00 
    trailMask byte = 0xC0 // must equal 0x80 
) 


func longestPrefix(s string, n int) int { 
    for i := (n - 1); ; i-- { 
     if (s[i] & lead1Mask) == 0x00 { 
      return i + 1 
     } 
     if (s[i] & trailMask) != 0x80 { 
      return i 
     } 
    } 
    panic("never reached") 
} 

func main() { 
    s := randomString(100000) 
    for len(s) > chunkSize { 
     cut := longestPrefix(s, chunkSize) 
     fmt.Println(s[:cut]) 
     s = s[cut:] 
    } 
    fmt.Println(s) 
} 

sto usando l'alfabeto danese/norvegese per generare una stringa casuale di 100000 rune.

Quindi, la "magia" si trova in longestPrefix. Per aiutarvi con la parte bit-shifting, fare riferimento al seguente grafico:

enter image description here

Il programma stampa i rispettivi lunghi pezzi possibili < = chunksize, uno per riga.

+0

Avevi ragione, c'era un errore di battitura e uno fuori-da-uno. Grazie! – thwd

+1

Perché muck in giro con dettagli intimi di UTF-8 quando 'utf8.RuneStart' fa tutto il necessario? –

+0

Per me è stato più di un esercizio e di aggiornamento su UTF-8, lo lascerò per chiunque altro voglia di imparare. – thwd

Problemi correlati