Sıfır sonlu bayt dizisini dizeye nasıl dönüştürebilirim?


502

[100]byteBir sürü stringveri aktarmak için okumam gerekiyor .

Tüm strings'ler tam olarak 100 karakter uzunluğunda olmadığından, kalan kısmı s byte arrayile doldurulur 0.

Ben dönüştürürseniz [100]byteiçin stringölçütü: string(byteArray[:]), atık 0ler olarak görüntülenir ^@^@s.

C stringfeshedilecektir 0, bu yüzden bu dönüştürmek için en iyi yolu ne var merak byte arrayetmek stringGolang içinde.


3
@ AndréLaszlo: Oyun alanında ^@gösterilmez, ancak terminalde veya benzer bir şeyde test ederseniz orada olurdu. Bunun sebebi Go'nun, len(string(bytes))örneğinizde 0 değil, 1 olduğunu 0 bulduğunda bayt dizisini bir dizeye dönüştürmeyi durdurmamasıdır . Dizenin tamamen (sıfırlarla) yazdırılıp yazdırılmadığı çıkış işlevine bağlıdır. ya da değil.
nemo

8
Http yanıt kuruluşu için kullanın string(body).
Ivan Chau

Yanıtlar:


513

Verileri bayt dilimlerine okuyan yöntemler okunan bayt sayısını döndürür. Bu sayıyı kaydetmeli ve sonra dizenizi oluşturmak için kullanmalısınız nOkunan bayt sayısı ise, kodunuz şöyle görünecektir:

s := string(byteArray[:n])

Tam dizeyi dönüştürmek için bu kullanılabilir:

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

Bu şuna eşittir:

s := string(byteArray)

Herhangi bir nedenle bilmiyorsanız n, bytesgirdinizin içinde boş bir karakter bulunmadığını varsayarak paketi bulmak için kullanabilirsiniz .

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

Veya icza'nın işaret ettiği gibi, aşağıdaki kodu kullanabilirsiniz:

n := bytes.IndexByte(byteArray, 0)

2
Bir yıl geç kaldığımı biliyorum, ancak çoğu yöntemin okunan bayt sayısını döndürdüğünü belirtmeliyim . Örneğin, binary.Read () bir [32] bayta okuyabilir, ancak 32 baytın tümünü doldurup doldurmadığınızı bilmiyorsunuzdur.
Eric Lagergren

7
1 bayt içeren bir bayt dilimi yerine bytes.IndexByte()tek bir aramayı kullanmalısınız . bytebytes.Index()
icza

56
Aslında string (byteArray) da yapar ve bir dilim oluşturma
kaydeder

3
Sadece berrak olsa olmak gerekirse, bu birşeyleri bayt dizisi döküm olduğunu umarım (Latin-1 vs ve söyleme, ya da bazı hatalı biçimlendirilmiş UTF-8 dizisini) geçerli UTF-8 dizesi. Go, yayınladığınızda bunu sizin için kontrol etmez.
Cameron Kerr

Bayt diziniz ters sırada, yani küçük endiansa ne olur?
Efendim

374

Ne dersin?

s := string(byteArray[:])

3
Bayt dizisini kesin olarak dönüştürmenin en temiz yolu. Acaba dizeleri sıfırlama bayt çıkarmak yardımcı olur? golang.org/pkg/strings/#example_Trim
andyvanee

24
soru özellikle karakterler string(byteArray[:])içerdiğini söylüyor^@
Robert

24
Farkı ne string(byteArray)? Diziyi neden kullanarak kopyalamanız gerekiyor [:]?
Robert Zaremba

7
@RobertZaremba> bir dize aslında salt okunur bir bayt dilimidir. Bayt dizisini doğrudan dizeye dönüştüremezsiniz, bu nedenle ilk dilimden sonra dizeye.
ferhat elmas

3
@RobertZaremba Bayt dilimleri [:]için bayt dizileri eklemeniz gerekmez.
Drew LeSueur

68

Basit çözüm:

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

Bunun nasıl bir performans gösterdiğinden emin değilim.


17

Örneğin,

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

Çıktı:

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

8

Aşağıdaki kod '\ 0' değerini arar ve sorunun varsayımları altında dizi, '\ 0' olmayan tüm '\ 0' öğesinden önce geldiğinden sıralanabilir. Dizi veri içinde '\ 0' içerebilirse bu varsayım geçerli olmaz.

İkili bir arama kullanarak ilk sıfır baytın konumunu bulun, sonra dilimleyin.

Sıfır baytı şu şekilde bulabilirsiniz:

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

Özellikle dizelerin çoğu kısaysa, sıfır baytı arayan bayt dizisini saf olarak taramak daha hızlı olabilir.


8
Kodunuz derlenmez ve olsa bile çalışmaz. İkili arama algoritması, sıralı bir dizide belirtilen değerin konumunu bulur. Dizi mutlaka sıralanamaz.
peterSO

@peterSO Haklısınız ve aslında bir sürü anlamlı ismi temsil ettiği için asla sıralanmamış.
Derrick Zhang

3
Tüm boş baytlar dizenin sonundaysa, bir ikili arama çalışır.
Paul Hankin

6
Downvotes anlamıyorum. Kod, dizenin sonunda dışında \ 0 içermediği varsayılarak derlenir ve doğrudur. Kod \ 0 arıyor ve sorunun varsayımları altında dizi 'sıralanmış' olarak kabul edilebilir, çünkü tüm \ 0 olmayan tüm \ 0'dan önce gelir ve kodun tamamı budur. Downvoters kodun çalışmadığı bir örnek giriş bulabilirse, cevabı kaldıracağım.
Paul Hankin

1
Giriş ise yanlış sonuç verir []byte{0}. Bu durumda , dilimleme sonucu olurken FirstZero()geri dönmelidir , ancak bunun yerine döndürür ve dilimleme sonuçları . 0""1"\x00"
icza

3

Dizideki sıfır olmayan baytların tam uzunluğunu bilmiyorsanız, önce kesebilirsiniz:

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


1
a) bytes.Trimbir diziyi değil, bir diziyi alır ( arr[:]eğer arr aslında [100]bytesoru durumları olarak a ise ihtiyacınız olacaktır ). b) bytes.Trimburada kullanılacak yanlış işlevdir. []byte{0,0,'a','b','c',0,'d',0}Bunun gibi girişler için "" yerine "abc \ x00d" döndürür c) bytes.IndexByteilk sıfır baytını bulmanın en iyi yolu olan doğru bir cevap zaten vardır .
Dave C

1

Neden olmasın?

bytes.NewBuffer(byteArray).String()

1
Soru Size gereken tek şey bu kadar bir dizi diyor) bir Çünkü byteArray[:]bu yana bytes.NewBufferbir alır []byte; b) soru, dizinin ilgilenmediğiniz sondaki sıfırları olduğunu söyledi; c) bunun yerine değişkeniniz bir []byte(çizginizin derlenmesinin tek yolu) ise, çizginiz bunu yapmanın yavaş bir yoludur string(v).
Dave C

1

Yalnızca performans ayarı için kullanın.

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: Bunun ciddi bir cevap olup olmadığından emin değilim, ama neredeyse kesinlikle bir bayt dilimini dizeye dönüştürmek için yansıma ve güvensiz kodu çağırmak istemiyorsunuz
Austin Hyde

1
Uyarı kelimesi: bir bayt dilimini bir dönüştürmek için güvensiz kullanılması string, daha sonra bayt dilimi değiştirilirse ciddi sonuçlar doğurabilir. stringGo'daki değerler, tüm Go çalışma zamanının ve kitaplıkların üzerine inşa edildiği değişmez olarak tanımlanır. Bu yolda ilerlerseniz kendinizi en gizemli böceklerin ve çalışma zamanı hatalarının ortasına ışınlayacaksınız.
icza

Düzenlendi, çünkü bu işaretçi kullanımına karşıdır (doğrudan dökümle aynı davranışa sahiptir, başka bir deyişle sonuç çöp toplanmaz). Paragrafı okuyun (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • Okumak için diziler yerine dilimler kullanın. örneğin io.Readerbir diziyi değil bir dilimi kabul eder.

  • Sıfır dolgu yerine dilimleme kullanın.

Misal:

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

Veriler başkaları ve diğer C dilleri tarafından yazılmıştır ve sadece okumam gerekiyor, bu yüzden yazılma şeklini kontrol edemiyorum.
Derrick Zhang

1
Oh, sonra bir uzunluk değeri kullanarak s := a[:n]veya s := string(a[:n])bir dizeye ihtiyacınız varsa bayt dizisini dilimleyin . Eğer ndoğrudan kullanılamaz, Daniel anlaşılacağı gibi tampon (dizi) spesifik / sıfır bayt bakarak, örneğin bilgisayarlı olmalıdır.
zzzz


0

Son derece yüksek performans göstermese de okunabilir tek çözüm

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

C stili bayt dizisine sahip olmak için tam örnek:

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

Null'lar hariç go stili bir dizeye sahip olmak için tam örnek:

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

Bu bir dilim dilim bayt ayırır. Bu nedenle, yoğun veya tekrar tekrar kullanılıyorsa performansa dikkat edin.


-1

Bayt dizisini dizeye sıkıştırmak için kod

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

İşte daha hızlı yol:

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

Bir dahaki sefere önce soruyu (ve mevcut cevapları) okuyun. (Dahası, aslında üzerinden bir bayt dilim yazdırmak istiyorsanız fmtbunu yapmak hızlıdır fmt.Printf("%s", bytes)kullanımına daha string(bytes)).
Dave C

-7

Ben özyinelemeli bir çözüm ile.

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

Neden özyinelemeli bir çözümü seviyorsunuz?
peterSO

Test kutusu fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")yazdırılmalı true, yazdırılmalıdır false.
peterSO

1
Soru en iyi yolun ne olduğunu soruyor . Bu olabildiğince kötü: anlaşılması zor ve son derece yavaş, ayrıca bir dönüştürmez, [100]byteancak a []byteve '\x00'baytları ayırmaz . Hızı (girdiye bağlı olarak) kabul edilen cevabın hızına kıyasla birden fazla büyüklükte yavaşlar.
icza
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.