Come convertire un array di byte con terminazione zero in stringa?


502

Devo leggere [100]byteper trasferire un mucchio di stringdati.

Poiché non tutte le strings sono esattamente lunghe 100 caratteri, la parte rimanente byte arrayè riempita con 0s.

Se converto [100]bytein stringper string(byteArray[:]):, 0i tailing s vengono visualizzati come ^@^@s.

In C il stringterminerà al momento 0, quindi mi chiedo qual è il modo migliore per convertire questo byte arraya stringin Golang.


3
@ AndréLaszlo: nel parco giochi ^@non si vede , ma sarebbe stato lì se lo avessi testato nel terminale o qualcosa di simile. La ragione di ciò è che Go non smette di convertire l'array di byte in una stringa quando trova uno 0. len(string(bytes))nell'esempio è 5 e non 1. Dipende dalla funzione di output, se la stringa è stampata completamente (con zeri) o no.
nemo,

8
Per il corpo di risposta http, utilizzare string(body).
Ivan Chau,

Risposte:


513

I metodi che leggono i dati in sezioni di byte restituiscono il numero di byte letti. Dovresti salvare quel numero e poi usarlo per creare la tua stringa Se nè il numero di byte letti, il tuo codice sarebbe simile al seguente:

s := string(byteArray[:n])

Per convertire l'intera stringa, è possibile utilizzare questo:

s := string(byteArray[:len(byteArray)])

Ciò equivale a:

s := string(byteArray)

Se per qualche motivo non lo sai n, puoi usare il bytespacchetto per trovarlo, supponendo che il tuo input non abbia un carattere nullo incorporato in esso.

n := bytes.Index(byteArray, []byte{0})

O come ha sottolineato icza, puoi usare il codice qui sotto:

n := bytes.IndexByte(byteArray, 0)

2
So di essere in ritardo di un anno, ma dovrei ricordare che la maggior parte dei metodi restituisce il numero di byte letti. Ad esempio, binary.Read () può leggere in un [32] byte, ma non sai se hai riempito tutti e 32 i byte.
Eric Lagergren,

7
Dovresti usare bytes.IndexByte()quali ricerche per un singolo byteinvece che bytes.Index()con una porzione di byte contenente 1 byte.
Icza,

56
effettivamente anche la stringa (byteArray) farà lo stesso e salverà la creazione di una fetta
throws_exceptions_at_you

3
Giusto per essere chiari, questo sta lanciando una sequenza di byte in qualcosa che si spera sia una stringa UTF-8 valida (e non diciamo, Latin-1 ecc., O qualche sequenza UTF-8 non valida). Go non verificherà questo per te quando esegui il cast.
Cameron Kerr,

E se il tuo array di byte si trova nell'ordine inverso, ovvero little endian?
Sir

374

Che dire?

s := string(byteArray[:])

3
Il modo più pulito per convertire l'array di byte di sicuro. Mi chiedo se stringhe. Trim aiuterebbe a eliminare i byte null? golang.org/pkg/strings/#example_Trim
andyvanee

24
la domanda dice specificamente che string(byteArray[:])contiene ^@personaggi
Robert

24
Qual è la differenza string(byteArray)? Perché è necessario copiare l'array usando [:]?
Robert Zaremba,

7
@RobertZaremba> una stringa è in effetti una porzione di byte di sola lettura. Non è possibile convertire l'array di byte direttamente in stringa, quindi prima fetta e poi stringa.
ferhat elmas,

3
@RobertZaremba Per le sezioni di byte non è necessario aggiungere [:], per le matrici di byte.
Ha disegnato LeSueur il

68

Soluzione semplicistica:

str := fmt.Sprintf("%s", byteArray)

Non sono sicuro di quanto sia performante.


17

Per esempio,

package main

import "fmt"

func CToGoString(c []byte) string {
    n := -1
    for i, b := range c {
        if b == 0 {
            break
        }
        n = i
    }
    return string(c[:n+1])
}

func main() {
    c := [100]byte{'a', 'b', 'c'}
    fmt.Println("C: ", len(c), c[:4])
    g := CToGoString(c[:])
    fmt.Println("Go:", len(g), g)
}

Produzione:

C:  100 [97 98 99 0]
Go: 3 abc

8

Il codice seguente sta cercando '\ 0' e, sotto i presupposti della domanda, l'array può essere considerato ordinato poiché tutti i non - '\ 0' precedono tutti '\ 0'. Questa ipotesi non sarà valida se l'array può contenere '\ 0' all'interno dei dati.

Trova la posizione del primo byte zero usando una ricerca binaria, quindi taglia.

Puoi trovare il byte zero in questo modo:

package main

import "fmt"

func FirstZero(b []byte) int {
    min, max := 0, len(b)
    for {
        if min + 1 == max { return max }
        mid := (min + max) / 2
        if b[mid] == '\000' {
            max = mid
        } else {
            min = mid
        }
    }
    return len(b)
}
func main() {
    b := []byte{1, 2, 3, 0, 0, 0}
    fmt.Println(FirstZero(b))
}

Potrebbe essere più veloce scansionare in modo ingenuo l'array di byte cercando il byte zero, specialmente se la maggior parte delle stringhe sono brevi.


8
Il tuo codice non viene compilato e, anche se lo facesse, non funzionerà. Un algoritmo di ricerca binaria trova la posizione di un valore specificato all'interno di un array ordinato. L'array non è necessariamente ordinato.
Pietro

@peterSO Hai ragione, e in effetti non viene mai ordinato poiché rappresenta un mucchio di nomi significativi.
Derrick Zhang,

3
Se tutti i byte null sono alla fine della stringa, funziona una ricerca binaria.
Paul Hankin,

6
Non capisco i voti negativi. Il codice viene compilato ed è corretto, supponendo che la stringa non contenga \ 0 tranne alla fine. Il codice sta cercando \ 0 e, sotto i presupposti della domanda, l'array può essere considerato 'ordinato', poiché tutti i non-\ precedono tutti \ 0 e questo è tutto il codice che sta controllando. Se i downvoter riuscissero a trovare un input di esempio su cui il codice non funziona, rimuoverò la risposta.
Paul Hankin,

1
Fornisce risultati errati se l'input è []byte{0}. In questo caso FirstZero()dovrebbe tornare 0così quando si taglia il risultato sarebbe "", ma invece restituisce 1e sezionando i risultati "\x00".
Icza,

3

Quando non si conosce la lunghezza esatta di byte diversi da zero nell'array, è possibile tagliarlo prima:

string (bytes.Trim (arr, "\ x00"))


1
a) bytes.Trimprende una porzione, non un array (avresti bisogno arr[:]se arr è effettivamente un [100]bytecome afferma la domanda). b) bytes.Trimè la funzione sbagliata da usare qui. Per input simili []byte{0,0,'a','b','c',0,'d',0}restituirà "abc \ x00d" invece di "" c) esiste già una risposta corretta che utilizza bytes.IndexByte, il modo migliore per trovare il primo byte zero.
Dave C,

1

Perché non questo?

bytes.NewBuffer(byteArray).String()

1
Perché a) la domanda dice un array, quindi a te ne avresti bisogno byteArray[:]dato che bytes.NewBufferprende un []byte; b) la domanda diceva che l'array ha zeri finali che non vengono trattati; c) se invece la tua variabile è un []byte(l'unico modo per compilare la tua linea) allora la tua linea è solo un modo lento di fare string(v).
Dave C,

1

Utilizzare solo per l'ottimizzazione delle prestazioni.

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func BytesToString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

func StringToBytes(s string) []byte {
    return *(*[]byte)(unsafe.Pointer(&s))
}

func main() {
    b := []byte{'b', 'y', 't', 'e'}
    s := BytesToString(b)
    fmt.Println(s)
    b = StringToBytes(s)
    fmt.Println(string(b))
}

1
-1: Non sono sicuro che questa sia una risposta seria, ma quasi sicuramente non vuoi invocare la riflessione e il codice non sicuro solo per convertire una porzione di byte in stringa
Austin Hyde

1
Un avvertimento: l'uso di unsafe per convertire una porzione di byte in a stringpuò avere serie implicazioni se successivamente la porzione di byte viene modificata. stringi valori in Go sono definiti immutabili, su cui si basano l'intero runtime Go e le librerie. Ti teletrasporterai nel mezzo dei bug più misteriosi e degli errori di runtime se segui questa strada.
Icza,

Modificato, perché questo è contro l'uso del puntatore (ha lo stesso comportamento del casting diretto, in altre parole il risultato non verrà raccolto in modo inutile). Leggi il paragrafo (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • Utilizzare le sezioni anziché le matrici per la lettura. ad esempio io.Readeraccetta una sezione, non una matrice.

  • Usa lo slicing anziché lo zero padding.

Esempio:

buf := make([]byte, 100)
n, err := myReader.Read(buf)
if n == 0 && err != nil {
        log.Fatal(err)
}

consume(buf[:n]) // consume will see exact (not padded) slice of read data

I dati sono scritti da altri e da un altro linguaggio C, e ho potuto solo leggerlo, quindi non posso controllare il modo in cui sono scritti.
Derrick Zhang,

1
Oh, quindi affetta l'array di byte usando un valore di lunghezza s := a[:n]o s := string(a[:n])se hai bisogno di una stringa. Se nnon è direttamente disponibile, deve essere calcolato, ad esempio cercando un byte specifico / zero nel buffer (array) come suggerisce Daniel.
zzzz,


0

Sebbene non estremamente performante, l'unica soluzione leggibile è

  //split by separator and pick the first one. 
  //This has all the characters till null excluding null itself.
  retByteArray := bytes.Split(byteArray[:], []byte{0}) [0]

  // OR 

  //If you want a true C-like string including the null character
  retByteArray := bytes.SplitAfter(byteArray[:], []byte{0}) [0]

Esempio completo per avere un array di byte in stile C:

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var byteArray = [6]byte{97,98,0,100,0,99}

    cStyleString := bytes.SplitAfter(byteArray[:],  []byte{0}) [0]
    fmt.Println(cStyleString)
}

Esempio completo per avere una stringa di stile go escludendo i null:

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var byteArray = [6]byte{97,98,0,100,0,99}

    goStyleString := string( bytes.Split(byteArray[:],  []byte{0}) [0] )
    fmt.Println(goStyleString)
}

Ciò alloca una fetta di fetta di byte. Quindi tieni d'occhio la performance se viene usata pesantemente o ripetutamente.


-1

Ecco il codice per comprimere l'array di byte in stringa

package main

import (
    "fmt"
)

func main() {
    byteArr := [100]byte{'b', 'y', 't', 'e', 's'}
    firstHalf := ToString(byteArr)
    fmt.Println("Bytes to str", string(firstHalf))
}
func ToString(byteArr [100]byte) []byte {
    arrLen := len(byteArr)
    firstHalf := byteArr[:arrLen/2]
    secHalf := byteArr[arrLen/2:]
    for {
        // if the first element is 0 in secondHalf discard second half
        if len(secHalf) != 0 && secHalf[0] == 0 {
            arrLen = len(firstHalf)
            secHalf = firstHalf[arrLen/2:]
            firstHalf = firstHalf[:arrLen/2]
            continue
        } else {
            for idx := 0; len(secHalf) > idx && secHalf[idx] != 0; idx++ {
                firstHalf = append(firstHalf, secHalf[idx])
            }
        }
        break
    }
    return firstHalf
}

-2

Ecco il modo più veloce:

resp, _ := http.Get("https://www.something.com/something.xml")
bytes, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(string(bytes)) //just convert with string() function

La prossima volta leggi prima la domanda (e le risposte esistenti). (Inoltre, se vuoi davvero stampare una porzione di byte tramite fmtè più veloce da fare fmt.Printf("%s", bytes)che da usare string(bytes)).
Dave C,

-7

Io quando con una soluzione ricorsiva.

func CToGoString(c []byte, acc string) string {

    if len(c) == 0 {
        return acc
    } else {
        head := c[0]
        tail := c[1:]
        return CToGoString(tail, acc + fmt.Sprintf("%c", head))
    }
}

func main() {
    b := []byte{some char bytes}
    fmt.Println(CToGoString(b, ""))
}

Perché ti piace una soluzione ricorsiva?
peterSO

Il test case fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")dovrebbe stampare true, stampa false.
peterSO,

1
La domanda chiede qual è il modo migliore . Questo è il più brutto che può ottenere: difficile da capire ed estremamente lento, inoltre non converte a [100]bytema a []bytee non toglie '\x00'byte. La sua velocità (dipende dall'input) è più lenta di più ordini di grandezza rispetto alla velocità della risposta accettata.
Icza,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.