Bagaimana cara mengubah byte array yang diakhiri nol ke string?


502

Saya perlu membaca [100]byteuntuk mentransfer banyakstring data.

Karena tidak semua stringkarakter memiliki panjang 100 karakter, bagian sisanya byte arraydiisi dengan0 s.

Jika saya mengonversi [100]byteke stringoleh string(byteArray[:]):, tailing 0akan ditampilkan sebagai^@^@ s.

Di C stringakan berakhir 0, jadi saya bertanya-tanya apa cara terbaik untuk mengubahnya byte arrayke stringGolang.


3
@ AndréLaszlo: Di taman bermain ^@itu tidak muncul, tetapi pasti sudah ada di sana jika Anda mengujinya di terminal atau yang serupa. Alasan untuk ini, adalah bahwa Go tidak berhenti mengubah array byte ke string ketika menemukan 0. len(string(bytes))dalam contoh Anda adalah 5 dan tidak 1. Itu tergantung pada fungsi output, apakah string sepenuhnya (dengan nol) dicetak atau tidak.
nemo

8
Untuk badan tanggapan http, gunakan string(body).
Ivan Chau

Jawaban:


513

Metode yang membaca data menjadi irisan byte mengembalikan jumlah byte yang dibaca. Anda harus menyimpan nomor itu dan kemudian menggunakannya untuk membuat string Anda. Jika njumlah byte dibaca, kode Anda akan terlihat seperti ini:

s := string(byteArray[:n])

Untuk mengonversi string penuh ini dapat digunakan:

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

Ini setara dengan:

s := string(byteArray)

Jika karena alasan tertentu Anda tidak tahu n, Anda bisa menggunakan bytespaket untuk menemukannya, dengan asumsi input Anda tidak memiliki karakter nol yang tertanam di dalamnya.

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

Atau seperti yang ditunjukkan icza, Anda dapat menggunakan kode di bawah ini:

n := bytes.IndexByte(byteArray, 0)

2
Saya tahu saya terlambat satu tahun, tetapi saya harus menyebutkan bahwa sebagian besar metode mengembalikan jumlah byte yang dibaca. Misalnya, binary.Read () dapat membaca byte [32], tetapi Anda tidak tahu apakah Anda telah mengisi semua 32 byte atau tidak.
Eric Lagergren

7
Anda harus menggunakan bytes.IndexByte()pencarian mana untuk satu bytealih-alih bytes.Index()dengan irisan byte yang berisi 1 byte.
icza

56
sebenarnya string (byteArray) akan melakukannya juga dan akan menyimpan kreasi slice
throws_exceptions_at_you

3
Hanya untuk memperjelas, ini memancarkan urutan byte ke sesuatu yang semoga string UTF-8 yang valid (dan tidak mengatakan, Latin-1 dll, atau beberapa urutan UTF-8 yang salah). Go tidak akan memeriksa ini untuk Anda ketika Anda melemparkan.
Cameron Kerr

Bagaimana jika array byte Anda berada dalam urutan terbalik alias little endian?
Sir

374

Bagaimana dengan?

s := string(byteArray[:])

3
Cara terbersih untuk mengonversi byte array pasti. Saya ingin tahu apakah strings.Trim akan membantu menghapus byte nol? golang.org/pkg/strings/#example_Trim
andyvanee

24
pertanyaannya secara spesifik mengatakan bahwa itu string(byteArray[:])berisi ^@karakter
Robert

24
Apa bedanya string(byteArray)? Mengapa Anda perlu menyalin array menggunakan [:]?
Robert Zaremba

7
@RobertZaremba> sebuah string pada dasarnya adalah sebuah byte read-only. Anda tidak dapat mengonversi byte array secara langsung ke string, jadi slice pertama lalu string.
ferhat elmas

3
@RobertZaremba Untuk irisan byte Anda tidak perlu menambahkan [:], untuk byte array, Anda harus melakukannya.
Drew LeSueur

68

Solusi sederhana:

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

Saya tidak yakin bagaimana performant ini.


17

Sebagai contoh,

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

Keluaran:

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

8

Kode berikut mencari '\ 0', dan di bawah asumsi pertanyaan array dapat dianggap diurutkan karena semua bukan - '\ 0' mendahului semua '\ 0'. Asumsi ini tidak akan berlaku jika array dapat berisi '\ 0' dalam data.

Temukan lokasi nol-byte pertama menggunakan pencarian biner, kemudian iris.

Anda dapat menemukan nol-byte seperti ini:

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

Mungkin lebih cepat hanya dengan memindai secara naif array byte mencari nol-byte, terutama jika sebagian besar string Anda pendek.


8
Kode Anda tidak dikompilasi dan, bahkan jika itu, itu tidak akan berfungsi. Algoritma pencarian biner menemukan posisi nilai yang ditentukan dalam array yang diurutkan. Array belum tentu diurutkan.
peterSO

@peterSO Anda benar, dan sebenarnya itu tidak pernah diurutkan karena mewakili sekelompok nama yang bermakna.
Derrick Zhang

3
Jika semua byte nol berada di akhir string, pencarian biner akan berfungsi.
Paul Hankin

6
Saya tidak mengerti downvotes. Kode mengkompilasi dan benar, dengan asumsi string berisi no \ 0 kecuali pada akhirnya. Kode mencari \ 0, dan berdasarkan asumsi pertanyaan, array dapat dianggap 'diurutkan', karena semua non- \ 0 mendahului semua \ 0 dan hanya itu yang diperiksa kode. Jika downvoter dapat menemukan contoh input yang kodenya tidak berfungsi, maka saya akan menghapus jawabannya.
Paul Hankin

1
Memberikan hasil yang salah jika input []byte{0}. Dalam hal ini FirstZero()harus kembali 0jadi ketika mengiris hasil akan "", tetapi sebaliknya mengembalikan 1dan mengiris hasil "\x00".
icza

3

Saat Anda tidak tahu panjang pasti byte non-nil dalam array, Anda bisa memotongnya terlebih dahulu:

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


1
a) bytes.Trimmengambil irisan, bukan array (Anda akan perlu arr[:]jika arr sebenarnya a [100]bytesebagai negara pertanyaan). b) bytes.Trimadalah fungsi yang salah untuk digunakan di sini. Untuk input seperti []byte{0,0,'a','b','c',0,'d',0}itu akan mengembalikan "abc \ x00d" bukannya "" c) sudah ada jawaban yang benar yang menggunakan bytes.IndexByte, cara terbaik untuk menemukan byte nol pertama.
Dave C

1

Kenapa tidak ini?

bytes.NewBuffer(byteArray).String()

1
Karena a) pertanyaannya mengatakan sebuah array sehingga Anda akan perlu byteArray[:]karena bytes.NewBuffermengambil []byte; b) pertanyaannya mengatakan array memiliki angka nol yang tidak Anda setujui; c) jika sebaliknya variabel Anda adalah []byte(satu-satunya cara baris Anda akan dikompilasi) maka baris Anda hanyalah cara lambat untuk melakukannya string(v).
Dave C

1

Hanya digunakan untuk penyempurnaan kinerja.

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: Tidak yakin apakah ini jawaban yang serius, tetapi Anda hampir pasti tidak ingin memohon refleksi dan kode tidak aman hanya untuk mengonversi byte slice menjadi string
Austin Hyde

1
Sebuah kata peringatan: menggunakan tidak aman untuk mengkonversi byte byte menjadi stringmungkin memiliki implikasi serius jika nanti byte byte diubah. stringnilai-nilai di Go didefinisikan tidak dapat diubah, yang menjadi dasar dibangunnya seluruh runtime dan pustaka Go. Anda akan memindahkan diri Anda ke tengah-tengah bug yang paling misterius dan kesalahan runtime jika Anda menyusuri jalan ini.
icza

Diedit, karena ini bertentangan dengan penggunaan pointer (ini memiliki perilaku yang sama dengan casting langsung, dengan kata lain hasil tidak akan dikumpulkan dengan sampah). Baca paragraf (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • Gunakan irisan alih-alih array untuk membaca. mis. io.Readermenerima irisan, bukan array.

  • Gunakan mengiris bukannya nol padding.

Contoh:

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

Data ditulis oleh orang lain dan oleh bahasa C lain, dan saya hanya harus membacanya, jadi saya tidak bisa mengendalikan cara itu ditulis.
Derrick Zhang

1
Oh, kemudian iris array byte menggunakan nilai panjang s := a[:n]atau s := string(a[:n])jika Anda membutuhkan string. Jika ntidak tersedia secara langsung, harus dihitung, misalnya dengan mencari byte spesifik / nol dalam buffer (array) seperti yang disarankan Daniel.
zzzz


0

Meskipun tidak terlalu berkinerja, satu-satunya solusi yang dapat dibaca adalah

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

Contoh lengkap untuk memiliki array byte gaya-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)
}

Contoh lengkap untuk memiliki string gaya go tidak termasuk nulls:

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

Ini mengalokasikan sepotong byte. Jadi perhatikan kinerja jika digunakan banyak atau berulang kali.


-1

Berikut adalah kode untuk mengompresi byte array ke 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

Inilah cara tercepat:

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

Lain kali baca pertanyaan (dan jawaban yang ada) terlebih dahulu. (Plus, jika Anda benar-benar ingin mencetak slice byte melalui fmtlebih cepat dilakukan fmt.Printf("%s", bytes)daripada menggunakan string(bytes)).
Dave C

-7

Saya ketika dengan solusi rekursif.

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

Mengapa Anda menyukai solusi rekursif?
peterSO

Kasing uji fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")harus dicetak true, dicetak false.
peterSO

1
Pertanyaan menanyakan apa cara terbaik . Ini seburuk yang bisa didapat: sulit dipahami dan sangat lambat, juga tidak mengonversi a [100]bytetetapi []byte, dan tidak menghapus '\x00'byte. Kecepatannya (tergantung pada input) lebih lambat oleh beberapa urutan besarnya dibandingkan dengan kecepatan jawaban yang diterima.
icza
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.