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.

Derrick Zhang
sumber
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)
Daniel
sumber
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[:])
mattes
sumber
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.

marcusljx
sumber
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
peterSO
sumber
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.

Paul Hankin
sumber
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"))

zach
sumber
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()
Bhagya Prasad NR
sumber
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))
}
Yuen
sumber
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
zzzz
sumber
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

Saya mencoba beberapa metode beberapa kali saya panik:

kesalahan runtime: iris batas di luar jangkauan.

Tapi ini akhirnya berhasil.

string(Data[:])

Gaurav Singh
sumber
3
Ini tidak menambah banyak informasi, dan pada dasarnya mengulangi jawaban dari 2013: stackoverflow.com/a/18615786/349333 .
Jochem Schulenklopper
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.

manikawnth
sumber
-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
}
Lalit Sharma
sumber
-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
Arturo Morales Rangel
sumber
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, ""))
}
Romel Campbell
sumber
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