Wie konvertiere ich ein nullterminiertes Byte-Array in einen String?

502

Ich muss lesen [100]byte, um eine Reihe von stringDaten zu übertragen.

Da nicht alle strings genau 100 Zeichen lang sind, wird der verbleibende Teil von s byte arraymit 0s aufgefüllt .

Wenn ich [100]bytenach stringby konvertiere string(byteArray[:]), werden die Tailing 0s als ^@^@s angezeigt .

In C die stringenden auf 0, so frage ich mich , was der beste Weg, dies zu konvertieren , byte arrayum stringin Golang.

Derrick Zhang
quelle
3
@ AndréLaszlo: Auf dem Spielplatz wird das ^@nicht angezeigt , aber es wäre da gewesen, wenn du es im Terminal oder ähnlichem getestet hättest. Der Grund dafür ist, dass Go die Konvertierung des Byte-Arrays in eine Zeichenfolge nicht beendet, wenn eine 0 len(string(bytes))in Ihrem Beispiel 5 und nicht 1 gefunden wird. Es hängt von der Ausgabefunktion ab, ob die Zeichenfolge vollständig (mit Nullen) gedruckt ist oder nicht.
Nemo
8
Verwenden Sie für den http-Antworttext string(body).
Ivan Chau

Antworten:

513

Methoden, die Daten in Byte-Slices lesen, geben die Anzahl der gelesenen Bytes zurück. Sie sollten diese Nummer speichern und dann zum Erstellen Ihrer Zeichenfolge verwenden. Wenn ndie Anzahl der gelesenen Bytes angegeben ist, sieht Ihr Code folgendermaßen aus:

s := string(byteArray[:n])

Um die vollständige Zeichenfolge zu konvertieren, kann Folgendes verwendet werden:

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

Dies entspricht:

s := string(byteArray)

Wenn Sie es aus irgendeinem Grund nicht wissen n, können Sie das bytesPaket verwenden, um es zu finden, vorausgesetzt, in Ihre Eingabe ist kein Nullzeichen eingebettet.

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

Oder wie icza betonte, können Sie den folgenden Code verwenden:

n := bytes.IndexByte(byteArray, 0)
Daniel
quelle
2
Ich weiß, dass ich ein Jahr zu spät bin, aber ich sollte erwähnen, dass die meisten Methoden die Anzahl der gelesenen Bytes zurückgeben. Zum Beispiel kann binary.Read () in ein [32] Byte einlesen, aber Sie wissen nicht, ob Sie alle 32 Bytes gefüllt haben oder nicht.
Eric Lagergren
7
Sie sollten verwenden bytes.IndexByte(), um nach einem einzelnen zu suchen, byteanstatt bytes.Index()mit einem Byte-Slice, das 1 Byte enthält.
icza
56
Tatsächlich reicht auch String (byteArray) aus und speichert eine Slice-Erstellung
throw_exceptions_at_you
3
Um jedoch klar zu sein, wird eine Folge von Bytes in etwas umgewandelt, das hoffentlich eine gültige UTF-8-Zeichenfolge ist (und nicht etwa Latin-1 usw. oder eine fehlerhafte UTF-8-Folge). Go wird dies beim Wirken nicht für Sie überprüfen.
Cameron Kerr
Was ist, wenn sich Ihr Byte-Array in umgekehrter Reihenfolge befindet, auch bekannt als Little Endian?
Sir
374

Wie wäre es mit?

s := string(byteArray[:])
matten
quelle
3
Der sauberste Weg, um das Byte-Array sicher zu konvertieren. Ich frage mich, ob Strings.Trim helfen würde, die Null-Bytes zu entfernen? golang.org/pkg/strings/#example_Trim
andyvanee
24
Die Frage besagt ausdrücklich, dass Zeichen string(byteArray[:])enthält^@
Robert
24
Was ist der Unterschied zu string(byteArray)? Warum müssen Sie das Array mit kopieren [:]?
Robert Zaremba
7
@RobertZaremba> Ein String ist praktisch ein schreibgeschütztes Byte-Slice. Sie können das Byte-Array nicht direkt in einen String konvertieren, also zuerst Slice, dann String.
Ferhat Elmas
3
@RobertZaremba Für Byte-Slices müssen Sie nicht das hinzufügen [:], für Byte-Arrays tun Sie dies.
Drew LeSueur
68

Einfache Lösung:

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

Ich bin mir nicht sicher, wie performant das ist.

marcusljx
quelle
17

Zum Beispiel,

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

Ausgabe:

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

Der folgende Code sucht nach '\ 0', und unter den Annahmen der Frage kann das Array als sortiert betrachtet werden, da alle Nicht-'\ 0' vor allen '\ 0' stehen. Diese Annahme gilt nicht, wenn das Array '\ 0' in den Daten enthalten kann.

Suchen Sie die Position des ersten Null-Bytes mithilfe einer binären Suche und schneiden Sie dann.

Sie finden das Null-Byte folgendermaßen:

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

Es kann schneller sein, das Byte-Array naiv nach dem Null-Byte zu durchsuchen, insbesondere wenn die meisten Ihrer Zeichenfolgen kurz sind.

Paul Hankin
quelle
8
Ihr Code wird nicht kompiliert und funktioniert auch dann nicht. Ein binärer Suchalgorithmus findet die Position eines bestimmten Werts innerhalb eines sortierten Arrays. Das Array ist nicht unbedingt sortiert.
PeterSO
@peterSO Sie haben Recht, und tatsächlich wird es nie sortiert, da es eine Reihe von bedeutungsvollen Namen darstellt.
Derrick Zhang
3
Wenn sich alle Nullbytes am Ende der Zeichenfolge befinden, funktioniert eine binäre Suche.
Paul Hankin
6
Ich verstehe die Abstimmungen nicht. Der Code wird kompiliert und ist korrekt, vorausgesetzt, die Zeichenfolge enthält außer am Ende kein \ 0. Der Code sucht nach \ 0, und unter den Annahmen der Frage kann das Array als 'sortiert' betrachtet werden, da alle Nicht-0 vor allen \ 0 stehen und das ist alles, was der Code überprüft. Wenn Downvoter eine Beispieleingabe finden könnten, bei der der Code nicht funktioniert, werde ich die Antwort entfernen.
Paul Hankin
1
Gibt ein falsches Ergebnis, wenn die Eingabe erfolgt []byte{0}. In diesem Fall FirstZero()sollte dies so zurückkehren 0, wenn das Slicing-Ergebnis wäre "", aber stattdessen kehrt es zurück 1und das Slicing führt zu "\x00".
icza
3

Wenn Sie die genaue Länge der Nicht-Null-Bytes im Array nicht kennen, können Sie sie zuerst zuschneiden:

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

zach
quelle
1
a) bytes.Trimnimmt ein Slice, kein Array (Sie würden es brauchen, arr[:]wenn arr tatsächlich ein ist, [100]bytewie in der Frage angegeben). b) bytes.Trimist hier die falsche Funktion. Für Eingaben []byte{0,0,'a','b','c',0,'d',0}, die "abc \ x00d" anstelle von "" c) zurückgeben, gibt es bereits eine korrekte Antwort, die bytes.IndexByteden besten Weg zum Finden des ersten Null-Bytes verwendet.
Dave C.
1

Warum nicht das?

bytes.NewBuffer(byteArray).String()
Bhagya Prasad NR
quelle
1
Weil a) die Frage ein Array sagt, das Sie benötigen würden, byteArray[:]da es bytes.NewBufferein benötigt []byte; b) Die Frage besagt, dass das Array nachgestellte Nullen hat, mit denen Sie sich nicht befassen. c) Wenn Ihre Variable stattdessen a ist []byte(die einzige Möglichkeit, wie Ihre Zeile kompiliert wird), ist Ihre Zeile nur eine langsame Methode string(v).
Dave C
1

Nur zur Leistungsoptimierung verwenden.

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
quelle
1
-1: Ich bin mir nicht sicher, ob dies eine ernsthafte Antwort ist, aber Sie möchten mit ziemlicher Sicherheit keine Reflexion und keinen unsicheren Code aufrufen, nur um ein Byte-Slice in einen String umzuwandeln
Austin Hyde
1
Ein Wort der Warnung: Die Verwendung von unsicher zur Konvertierung eines Byte-Slice in ein stringkann schwerwiegende Folgen haben, wenn das Byte-Slice später geändert wird. stringWerte in Go werden als unveränderlich definiert, auf denen die gesamte Go-Laufzeit und die Bibliotheken aufbauen. Sie werden sich in die Mitte der mysteriösesten Fehler und Laufzeitfehler teleportieren, wenn Sie diesen Weg gehen.
icza
Bearbeitet, da dies gegen die Verwendung von Zeigern ist (es hat das gleiche Verhalten wie direktes Casting, mit anderen Worten, das Ergebnis wird nicht durch Müll gesammelt). Lesen Sie den Absatz (6) golang.org/pkg/unsafe/#Pointer
Laevus Dexter
0
  • Verwenden Sie zum Lesen Slices anstelle von Arrays. zB io.Readerakzeptiert ein Slice, kein Array.

  • Verwenden Sie das Schneiden anstelle der Nullpolsterung.

Beispiel:

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
quelle
Die Daten werden von anderen und in einer anderen C-Sprache geschrieben, und ich musste sie nur lesen, sodass ich die Art und Weise, wie sie geschrieben werden, nicht kontrollieren kann.
Derrick Zhang
1
Oh, dann schneiden Sie das Byte-Array mit einem Längenwert s := a[:n]oder s := string(a[:n])wenn Sie eine Zeichenfolge benötigen. Wenn nes nicht direkt verfügbar ist, muss es berechnet werden, z. B. indem nach einem bestimmten / Null-Byte im Puffer (Array) gesucht wird, wie Daniel vorschlägt.
zzzz
0

Ich habe einige Male einige Methoden ausprobiert, als ich in Panik geriet:

Laufzeitfehler: Slice-Grenzen außerhalb des Bereichs.

Aber das hat endlich funktioniert.

string(Data[:])

Gaurav Singh
quelle
3
Dies fügt nicht viele Informationen hinzu und wiederholt im Wesentlichen die Antwort von 2013: stackoverflow.com/a/18615786/349333 .
Jochem Schulenklopper
0

Obwohl nicht extrem performant, ist die einzige lesbare Lösung

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

Vollständiges Beispiel für ein Byte-Array im C-Stil:

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

Vollständiges Beispiel für eine Go-Zeichenfolge ohne Nullen:

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

Dadurch wird ein Stück Byte zugewiesen. Behalten Sie also die Leistung im Auge, wenn sie stark oder wiederholt verwendet wird.

manikawnth
quelle
-1

Hier ist der Code zum Komprimieren des Byte-Arrays in einen 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
quelle
-2

Hier ist der schnellere Weg:

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
quelle
Lesen Sie das nächste Mal zuerst die Frage (und die vorhandenen Antworten). (Wenn Sie tatsächlich ein Byte-Slice über drucken möchten, fmtist dies schneller fmt.Printf("%s", bytes)als die Verwendung string(bytes)).
Dave C
-7

Ich wenn mit einer rekursiven Lösung.

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
quelle
Warum magst du eine rekursive Lösung?
PeterSO
Der Testfall fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")sollte gedruckt werden true, er wird gedruckt false.
PeterSO
1
Frage fragt, was der beste Weg ist. Das ist so schlimm wie es nur geht: schwer zu verstehen und extrem langsam, außerdem konvertiert es nicht a, [100]bytesondern a []byteund entfernt keine '\x00'Bytes. Seine Geschwindigkeit (abhängig von der Eingabe) ist um mehrere Größenordnungen langsamer als die Geschwindigkeit der akzeptierten Antwort.
icza