Como converter uma matriz de bytes terminada em zero em string?


502

Eu preciso ler [100]bytepara transferir um monte de stringdados.

Como nem todos os strings têm exatamente 100 caracteres, a parte restante byte arrayé preenchida com 0s.

Se eu converter [100]bytepara stringpor string(byteArray[:]):, os caracteres finais 0s serão exibidos como ^@^@s.

Em C o stringcessará 0, então eu me pergunto qual é a melhor maneira de converter este byte arraypara stringnos golang.


3
@ AndréLaszlo: No playground, o ^@show não aparece, mas estaria lá se você testasse no terminal ou algo parecido. A razão para isso é que o Go não para de converter a matriz de bytes em uma cadeia de caracteres quando encontra um 0. len(string(bytes))no seu exemplo é 5 e não 1. Depende da função de saída, se a cadeia de caracteres é totalmente (com zeros) impressa ou não.
nemo

8
Para o corpo da resposta http, use string(body).
Ivan Chau

Respostas:


513

Métodos que lêem dados em fatias de bytes retornam o número de bytes lidos. Você deve salvar esse número e, em seguida, usá-lo para criar sua string. Se nfor o número de bytes lidos, seu código ficará assim:

s := string(byteArray[:n])

Para converter a string completa, isso pode ser usado:

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

Isso é equivalente a:

s := string(byteArray)

Se, por algum motivo, você não souber n, poderá usar o bytespacote para encontrá-lo, supondo que sua entrada não tenha um caractere nulo incorporado.

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

Ou, como o icza apontou, você pode usar o código abaixo:

n := bytes.IndexByte(byteArray, 0)

2
Sei que estou atrasado um ano, mas devo mencionar que a maioria dos métodos retorna o número de bytes lidos. Por exemplo, binary.Read () pode ler um byte [32], mas você não sabe se preencheu todos os 32 bytes ou não.
Eric Lagergren

7
Você deve usar as bytes.IndexByte()pesquisas de uma única em bytevez de bytes.Index()uma fatia de bytes contendo 1 byte.
Icza

56
realmente string (byteArray) vai fazer muito e você vai economizar uma criação fatia
throws_exceptions_at_you

3
Apenas para ficar claro, isso está convertendo uma sequência de bytes para algo que, esperançosamente, é uma string UTF-8 válida (e não digamos, Latin-1 etc., ou alguma sequência UTF-8 malformada). O Go não verificará isso quando você transmitir.
Cameron Kerr

E se a sua matriz de bytes estiver na ordem inversa, também conhecida como little endian?
Sir

374

A respeito?

s := string(byteArray[:])

3
Maneira mais limpa de converter a matriz de bytes, com certeza. Gostaria de saber se strings.Trim ajudaria a retirar os bytes nulos? golang.org/pkg/strings/#example_Trim
andyvanee

24
a pergunta diz especificamente que string(byteArray[:])contém ^@caracteres
Robert

24
Qual é a diferença string(byteArray)? Por que você precisa copiar a matriz usando [:]?
Robert Zaremba

7
@RobertZaremba> uma string é na verdade uma fatia de bytes somente leitura. Você não pode converter a matriz de bytes diretamente em string, então primeiro corte e depois string.
ferhat elmas

3
@RobertZaremba Para fatias de bytes, você não precisa adicionar as [:]matrizes de bytes.
Desenhou LeSueur

68

Solução simplista:

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

Não tenho certeza de quão bom é isso.


17

Por exemplo,

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

Resultado:

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

8

O código a seguir está procurando por '\ 0' e, sob as suposições da pergunta, a matriz pode ser considerada classificada, pois todos os que não são '\ 0' precedem todos os '\ 0'. Essa suposição não será válida se a matriz puder conter '\ 0' nos dados.

Encontre a localização do primeiro byte zero usando uma pesquisa binária e depois faça uma fatia.

Você pode encontrar o byte zero como este:

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

Pode ser mais rápido varrer ingenuamente a matriz de bytes procurando o byte zero, especialmente se a maioria de suas strings for curta.


8
Seu código não é compilado e, mesmo se o fizer, não funcionará. Um algoritmo de pesquisa binária encontra a posição de um valor especificado em uma matriz classificada. A matriz não é necessariamente classificada.
peterSO

@ Petereter Você está certo e, na verdade, nunca é classificado, pois representa um monte de nomes significativos.
Derrick Zhang

3
Se todos os bytes nulos estiverem no final da cadeia, uma pesquisa binária funcionará.
Paul Hankin

6
Eu não entendo os votos negativos. O código é compilado e está correto, assumindo que a sequência não contenha \ 0, exceto no final. O código está procurando por \ 0 e, sob as premissas da pergunta, a matriz pode ser considerada 'classificada', pois todos os não-0 precedem todos os \ 0 e é tudo o que o código está verificando. Se os downvoters puderem encontrar uma entrada de exemplo na qual o código não funcione, removerei a resposta.
Paul Hankin

1
Dá resultado errado se a entrada é []byte{0}. Nesse caso, FirstZero()deve retornar 0para quando o resultado da fatia seria "", mas, em vez disso, retorna 1e a fatia resulta "\x00".
icza 7/08/2015

3

Quando você não souber o comprimento exato de bytes diferentes de zero na matriz, poderá cortá-lo primeiro:

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


1
a) bytes.Trimpega uma fatia, não uma matriz (você precisaria arr[:]se arr fosse realmente [100]bytecomo a pergunta indica). b) bytes.Trimé a função errada a ser usada aqui. Para entradas como []byte{0,0,'a','b','c',0,'d',0}retornará "abc \ x00d" em vez de "" c) já existe uma resposta correta que usa bytes.IndexByte, a melhor maneira de encontrar o primeiro byte zero.
Dave C

1

Por que não isso?

bytes.NewBuffer(byteArray).String()

1
Porque a) a pergunta diz uma matriz que você precisa byteArray[:]desde que bytes.NewBufferleva a []byte; b) a pergunta dizia que a matriz possui zeros à direita com os quais você não lida; c) se sua variável é uma []byte(a única maneira de compilar sua linha), então sua linha é apenas uma maneira lenta de fazer string(v).
Dave C

1

Use apenas para ajuste de desempenho.

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: Não sei se isso é uma resposta séria, mas você quase certamente não quero invocar a reflexão eo código inseguro apenas para converter uma fatia byte para string
Austin Hyde

1
Uma palavra de aviso: o uso não seguro para converter uma fatia de bytes em a stringpode ter implicações sérias se posteriormente a fatia de bytes for modificada. stringos valores no Go são definidos para serem imutáveis, nos quais todo o tempo de execução e as bibliotecas do Go se baseiam. Você se teleportará para o meio dos bugs e erros de execução mais misteriosos se seguir esse caminho.
icza 4/09/18

Editado, porque isso é contra o uso do ponteiro (ele tem o mesmo comportamento da conversão direta, ou seja, o resultado não será coletado como lixo). Leia o parágrafo (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • Use fatias em vez de matrizes para leitura. por exemplo, io.Readeraceita uma fatia, não uma matriz.

  • Use fatias em vez de preenchimento zero.

Exemplo:

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

Os dados são escritos por outras pessoas e por outra linguagem C, e eu só tenho que lê-los, então não posso controlar a maneira como eles são escritos.
Derrick Zhang

1
Ah, fatie a matriz de bytes usando um valor de comprimento s := a[:n]ou s := string(a[:n])se você precisar de uma sequência. Se nnão estiver disponível diretamente, deve ser calculado, por exemplo, procurando um byte específico / zero no buffer (matriz), como Daniel sugere.
zzzz


0

Embora não seja de alto desempenho, a única solução legível é

  //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]

Exemplo completo para ter uma matriz de bytes no estilo 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)
}

Exemplo completo para ter uma string de estilo go excluindo os nulos:

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

Isso aloca uma fatia da fatia de bytes. Portanto, fique de olho no desempenho, se for usado intensamente ou repetidamente.


-1

Aqui está o código para compactar a matriz de bytes em string

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

Aqui está o caminho mais rápido:

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

Da próxima vez, leia a pergunta (e as respostas existentes) primeiro. (Além disso, se você realmente deseja imprimir uma fatia de bytes fmt, é mais rápido fazer fmt.Printf("%s", bytes)do que usar string(bytes)).
Dave C

-7

Eu quando com uma solução recursiva.

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, ""))
}

Por que você gosta de uma solução recursiva?
precisa

O caso de teste fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")deve imprimir true, ele imprime false.
precisa

1
Pergunta pergunta qual é o melhor caminho. Isso é o mais ruim possível: difícil de entender e extremamente lento, também não converte um [100]bytemas a []bytee não remove '\x00'bytes. Sua velocidade (depende da entrada) é mais lenta por ordem de magnitude múltipla em comparação com a velocidade da resposta aceita.
Icza
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.