¿Cómo convertir una matriz de bytes terminada en cero a una cadena?


502

Necesito leer [100]bytepara transferir un montón de stringdatos.

Debido a que no todos los strings tienen exactamente 100 caracteres de largo, la parte restante del mismo byte arrayse rellena con 0s.

Si convierto [100]bytea stringby:, string(byteArray[:])los 0s de cola se muestran como ^@^@s.

En la C stringse terminará con 0, por lo que me pregunto ¿cuál es la mejor manera de convertir esta byte arraya stringen Golang.


3
@ AndréLaszlo: En el patio de recreo ^@no se muestra, pero hubiera estado allí si lo probaras en la terminal o algo similar. La razón de esto es que Go no deja de convertir la matriz de bytes en una cadena cuando encuentra un 0. len(string(bytes))en su ejemplo es 5 y no 1. Depende de la función de salida, si la cadena está totalmente (con ceros) impresa o no.
nemo

8
Para el cuerpo de respuesta http, use string(body).
Ivan Chau

Respuestas:


513

Los métodos que leen datos en segmentos de bytes devuelven el número de bytes leídos. Debe guardar ese número y luego usarlo para crear su cadena. Si nes el número de bytes leídos, su código se vería así:

s := string(byteArray[:n])

Para convertir la cadena completa, esto se puede usar:

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

Esto es equivalente a:

s := string(byteArray)

Si por alguna razón no lo sabe n, puede usar el bytespaquete para encontrarlo, suponiendo que su entrada no tenga un carácter nulo incrustado.

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

O como señaló icza, puede usar el siguiente código:

n := bytes.IndexByte(byteArray, 0)

2
Sé que llego un año tarde, pero debo mencionar que la mayoría de los métodos devuelven el número de bytes leídos. Por ejemplo, binary.Read () puede leer en un byte [32], pero no sabe si ha llenado los 32 bytes o no.
Eric Lagergren

77
Debe usar bytes.IndexByte()qué búsquedas para un solo en bytelugar de bytes.Index()con un segmento de bytes que contiene 1 byte.
icza

56
En realidad cadena (bytearray) hará también y se ahorrará una creación rebanada
throws_exceptions_at_you

3
Sin embargo, para ser claros, esto está convirtiendo una secuencia de bytes en algo que es de esperar una cadena UTF-8 válida (y no digamos, Latin-1, etc., o alguna secuencia UTF-8 malformada). Ir no comprobará esto por ti cuando juegues.
Cameron Kerr

¿Qué pasa si su matriz de bytes está en el orden inverso, también conocido como little endian?
Sir

374

¿Qué pasa?

s := string(byteArray[:])

3
La forma más limpia de convertir la matriz de bytes con seguridad. Me pregunto si las cadenas. ¿Trim ayudaría a eliminar los bytes nulos? golang.org/pkg/strings/#example_Trim
andyvanee

24
la pregunta dice específicamente que string(byteArray[:])contiene ^@caracteres
Robert

24
¿Cuál es la diferencia para string(byteArray)? ¿Por qué necesitas copiar la matriz usando [:]?
Robert Zaremba

77
@RobertZaremba> una cadena es en efecto una porción de bytes de solo lectura. No puede convertir la matriz de bytes directamente en una cadena, por lo tanto, primero corte y luego cadena.
ferhat elmas

3
@RobertZaremba Para los segmentos de bytes, no necesita agregar [:], para los conjuntos de bytes, sí.
Drew LeSueur el

68

Solución simplista:

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

Sin embargo, no estoy seguro de cuán eficiente es esto.


17

Por ejemplo,

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

Salida:

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

8

El siguiente código busca '\ 0', y bajo los supuestos de la pregunta, la matriz se puede considerar ordenada ya que todos los que no son '\ 0' preceden a todos '\ 0'. Esta suposición no se mantendrá si la matriz puede contener '\ 0' dentro de los datos.

Encuentre la ubicación del primer byte cero usando una búsqueda binaria, luego corte.

Puede encontrar el byte cero de esta manera:

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

Puede ser más rápido escanear ingenuamente la matriz de bytes en busca del byte cero, especialmente si la mayoría de sus cadenas son cortas.


8
Su código no se compila e, incluso si lo hizo, no funcionará. Un algoritmo de búsqueda binaria encuentra la posición de un valor especificado dentro de una matriz ordenada. La matriz no está necesariamente ordenada.
peterSO

@peterSO Tienes razón, y de hecho nunca está ordenada ya que representa un montón de nombres significativos.
Derrick Zhang

3
Si todos los bytes nulos están al final de la cadena, funciona una búsqueda binaria.
Paul Hankin

66
No entiendo los votos negativos. El código se compila y es correcto, suponiendo que la cadena no contiene \ 0 excepto al final. El código está buscando \ 0, y bajo los supuestos de la pregunta, la matriz se puede considerar 'ordenada', ya que todos los que no son \ 0 preceden a todos \ 0 y eso es todo lo que el código está comprobando. Si los votantes negativos pueden encontrar una entrada de ejemplo en la que el código no funciona, entonces eliminaré la respuesta.
Paul Hankin

1
Da un resultado incorrecto si la entrada es []byte{0}. En este caso, FirstZero()debería regresar 0así cuando el resultado de corte sería "", pero en su lugar, regresa 1y los resultados de corte en "\x00".
icza

3

Cuando no conoce la longitud exacta de bytes no nulos en la matriz, puede recortarla primero:

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


1
a) bytes.Trimtoma un segmento, no una matriz (lo que necesitaría arr[:]si arr es realmente a [100]bytecomo dice la pregunta). b) bytes.Trimes la función incorrecta para usar aquí. Para una entrada como []byte{0,0,'a','b','c',0,'d',0}esta devolverá "abc \ x00d" en lugar de "" c) ya existe una respuesta correcta que utiliza bytes.IndexByte, la mejor manera de encontrar el primer byte cero.
Dave C

1

¿Por qué no esto?

bytes.NewBuffer(byteArray).String()

1
Porque a) la pregunta dice una matriz, por lo que necesitarías, byteArray[:]ya que bytes.NewBuffertoma a []byte; b) la pregunta decía que la matriz tiene ceros finales con los que no se trata; c) si, en cambio, su variable es a []byte(la única forma en que se compilará su línea), entonces su línea es solo una forma lenta de hacerlo string(v).
Dave C

1

Solo se usa para ajuste de rendimiento.

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: No estoy seguro de si esta es una respuesta seria, pero definitivamente no desea invocar la reflexión y el código inseguro solo para convertir una porción de byte en una cadena
Austin Hyde

1
Una palabra de advertencia: usar inseguro para convertir un segmento de bytes en un stringpuede tener serias implicaciones si luego se modifica el segmento de bytes. stringlos valores en Go se definen como inmutables, sobre los cuales se basan todo el tiempo de ejecución y las bibliotecas de Go. Te teletransportarás a la mitad de los errores y errores de tiempo de ejecución más misteriosos si sigues este camino.
icza

Editado, porque esto está en contra del uso del puntero (tiene el mismo comportamiento que la conversión directa, en otras palabras, el resultado no será recolectado como basura). Lea el párrafo (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • Use rebanadas en lugar de matrices para leer. por ejemplo, io.Readeracepta un segmento, no una matriz.

  • Use rebanado en lugar de cero relleno.

Ejemplo:

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

Los datos están escritos por otros y por otro lenguaje C, y solo pude leerlos, por lo que no puedo controlar la forma en que están escritos.
Derrick Zhang

1
Ah, entonces corte la matriz de bytes usando un valor de longitud s := a[:n]o s := string(a[:n])si necesita una cadena. Si nno está directamente disponible, debe calcularse, por ejemplo, buscando un byte específico / cero en el búfer (matriz) como sugiere Daniel.
zzzz


0

Aunque no es extremadamente eficiente, la única solución legible es

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

Ejemplo completo para tener una matriz de bytes de 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)
}

Ejemplo completo para tener una cadena de estilo ir excluyendo los 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)
}

Esto asigna una porción de porción de bytes. Por lo tanto, vigile el rendimiento si se usa en gran medida o repetidamente.


-1

Aquí está el código para comprimir la matriz de bytes a una cadena

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

Aquí está la forma más rápida:

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 próxima vez lea primero la pregunta (y las respuestas existentes). (Además, si realmente desea imprimir una porción de byte fmt, es más rápido hacer fmt.Printf("%s", bytes)que usar string(bytes)).
Dave C

-7

Yo cuando con una solución 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 qué te gusta una solución recursiva?
peterSO

El caso de prueba fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")debe imprimir true, imprime false.
peterSO

1
Pregunta pregunta cuál es la mejor manera. Esto es tan malo como puede ser: difícil de entender y extremadamente lento, además no convierte a [100]bytesino a []byte, y no elimina '\x00'bytes. Su velocidad (depende de la entrada) es más lenta en múltiples órdenes de magnitud en comparación con la velocidad de la respuesta aceptada.
icza
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.