ゼロで終了するバイト配列を文字列に変換する方法は?


502

[100]byte大量のstringデータを転送するために読み取る必要があります。

すべてstringのが正確に100文字の長さではないため、の残りの部分にはs byte arrayが埋め込まれ0ます。

by に変換する[100]byteと、テーリングsはsとして表示されます。stringstring(byteArray[:])0^@^@

Cにstringすると終了します0ので、私はこれを変換するための最良の方法だかしらbyte arraystringGolangに。


3
@AndréLaszlo:プレイグラウンドで^@はは表示されませんが、ターミナルなどでテストすると表示されます。これは、Goが0 len(string(bytes))を検出したときにバイト配列の文字列への変換を停止しないためです。この例では、1ではなく5です。これは、文字列が完全に(ゼロで)印刷されているかどうか、出力関数によって異なりますか否か。
nemo 2013年

8
http応答本文には、を使用しますstring(body)
Ivan Chau

回答:


513

データをバイトスライスに読み取るメソッドは、読み取ったバイト数を返します。その数を保存し、それを使用して文字列を作成する必要nがあります。が読み取ったバイト数の場合、コードは次のようになります。

s := string(byteArray[:n])

完全な文字列を変換するには、これを使用できます。

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

これは次と同等です。

s := string(byteArray)

何らかの理由でわからないn場合は、bytesパッケージを使用してパッケージを検索できます。入力にnull文字が埋め込まれていないと想定します。

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

または、iczaが指摘したように、以下のコードを使用できます。

n := bytes.IndexByte(byteArray, 0)

2
私は1年遅れていることはわかっていますが、ほとんどのメソッドは読み取ったバイト数を返すことを述べておきます。たとえば、binary.Read()は[32]バイトを読み取ることができますが、32バイトすべてを埋めたかどうかはわかりません。
Eric Lagergren、2015年

7
1バイトを含むバイトスライスではなく、bytes.IndexByte()どちらを検索するかを使用する必要があります。bytebytes.Index()
icza

56
実際にはstring(byteArray)も実行し、スライスの作成を保存します
throws_exceptions_at_you

3
ただ、明確にかかわらずように、これは何かのバイトシーケンスをキャストしているうまくいけば、有効なUTF-8文字列(と言うではない、ラテン-1など、またはいくつかの不正なUTF-8シーケンス)。Goはキャスト時にこれをチェックしません。
Cameron Kerr

バイト配列が逆順、つまりリトルエンディアンになっている場合はどうなりますか?
サー

374

どう?

s := string(byteArray[:])

3
確かにバイト配列を変換する最もクリーンな方法。strings.Trimはnullバイトを取り除くのに役立ちますか?golang.org/pkg/strings/#example_Trim
andyvanee

24
質問には、文字がstring(byteArray[:])含まれていると具体的に記載されています^@
Robert

24
はどう違いstring(byteArray)ますか?なぜアレイをコピーする必要があるの[:]ですか?
Robert Zaremba、2015年

7
@RobertZaremba>文字列は事実上、読み取り専用のバイトスライスです。バイト配列を文字列に直接変換することはできないため、最初にスライスしてから文字列にします。
ferhat elmas

3
@RobertZarembaバイトスライスの場合はを追加する必要はあり[:]ません。バイト配列の場合は追加します。
Drew LeSueur 2017

68

単純なソリューション:

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

これがどれほどのパフォーマンスであるかはわかりません。


17

例えば、

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

出力:

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

8

次のコードは '\ 0'を探しており、質問の前提の下では、すべての '\ 0'がすべての '\ 0'の前にあるため、配列はソート済みと見なすことができます。配列がデータ内に '\ 0'を含むことができる場合、この仮定は成り立ちません。

バイナリ検索を使用して最初のゼロバイトの場所を見つけ、次にスライスします。

次のようにゼロバイトを見つけることができます:

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

特にほとんどの文字列が短い場合は、単純にバイト配列をスキャンしてゼロバイトを探す方が速い場合があります。


8
コードがコンパイルされず、コンパイルされたとしても機能しません。バイナリサーチアルゴリズムは、ソートされた配列内の指定された値の位置を見つけます。配列は必ずしもソートされていません。
peterSO 2013年

@peterSO正解です。実際、意味のある名前の集まりを表しているため、並べ替えられることはありません。
デリックチャン

3
すべてのnullバイトが文字列の末尾にある場合、バイナリ検索が機能します。
ポールハンキン、2013年

6
私は反対票を理解していません。コードはコンパイルされて正しく、文字列に最後を除いて\ 0が含まれていないと想定しています。コードは\ 0を探しており、質問の仮定の下では、すべての非\ 0がすべての\ 0の前にあり、すべてのコードがチェックしているため、配列は「ソート済み」と見なすことができます。ダウンボーターがコードが機能しない入力例を見つけることができた場合は、答えを削除します。
ポールハンキン、2013

1
入力がの場合、誤った結果が返されます[]byte{0}。この場合にFirstZero()返すべき0スライス結果であろうときに""、その代わりにそれは返し1およびスライス結果"\x00"
icza

3

配列のnil以外のバイトの正確な長さがわからない場合は、最初に切り取ります。

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


1
a)bytes.Trim配列ではなくスライスをarr[:]取得します(arrが実際にaである場合は[100]byte、質問のとおりに必要です)。b)bytes.Trimここで使用するのは間違った関数です。このような入力の場合[]byte{0,0,'a','b','c',0,'d',0}、「」ではなく「abc \ x00d」が返されます。c)を使用する正しい答えがすでにありbytes.IndexByteます。これは、最初のゼロバイトを見つけるための最良の方法です。
Dave C

1

なぜこれではないのですか?

bytes.NewBuffer(byteArray).String()

1
)の質問は、あなたが配列を必要とするだろうとのように、と言うのでbyteArray[:]以来bytes.NewBufferとります[]byte。b)配列には、処理しない末尾のゼロが含まれているとの質問。c)代わりに変数が[]byte(行がコンパイルされる唯一の方法)である場合、行は実行の遅い方法ですstring(v)
Dave C、

1

パフォーマンス調整にのみ使用します。

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:これが深刻な答えであるかどうかは不明ですが、バイトスライスを文字列に変換するためだけにリフレクションおよび安全でないコードを呼び出すことはほぼ間違いありません
Austin Hyde

1
警告:安全でないバイトスライスをバイトスライスに変換すると、string後でバイトスライスが変更された場合に深刻な影響が生じる可能性があります。stringGoの値は、不変であると定義されており、Goランタイム全体とライブラリがその上で構築されます。このパスをたどると、最も神秘的なバグとランタイムエラーの真ん中にテレポートします。
icza

これはポインターの使用法に反するため編集されました(直接キャストと同じ動作をします。つまり、結果はガベージコレクションされません)。パラグラフ(6)を読むgolang.org/pkg/unsafe/#Pointer
Laevus Dexter

0
  • 読み取りには、配列の代わりにスライスを使用してください。たとえばio.Reader、配列ではなくスライスを受け入れます。

  • ゼロパディングの代わりにスライスを使用します。

例:

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

データは他の人や他のC言語で書かれており、私はそれを読むだけで、その書き方を制御できません。
Derrick Zhang、

1
ああ、次に、長さの値を使用して、s := a[:n]またはs := string(a[:n])文字列が必要な場合は、バイト配列をスライスします。n直接利用できない場合は、たとえば、ダニエルが示唆するように、バッファ(配列)内の特定/ゼロのバイトを探すことによって、計算する必要があります。
zzzz


0

非常に高性能ではありませんが、唯一の読みやすいソリューションは

  //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スタイルのバイト配列の完全な例:

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を除いたgoスタイル文字列の完全な例:

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

これにより、バイトのスライスのスライスが割り当てられます。したがって、頻繁にまたは繰り返し使用する場合は、パフォーマンスに注意してください。


-1

バイト配列を文字列に圧縮するコードは次のとおりです

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

これはより速い方法です:

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

次回は、最初に質問(および既存の回答)を読んでください。(さらに、実際にバイトスライスを印刷する場合fmtfmt.Printf("%s", bytes)、を使用するよりも高速ですstring(bytes))。
Dave C

-7

再帰的な解決策があるとき。

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

なぜ再帰的なソリューションが好きですか?
peterSO 2013

テストケースをfmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")印刷する必要がありtrue、それが印刷さfalse
peterSO 2013

1
質問は、最善の方法は何かを尋ねます。また、それは変換しません、理解するのは難しいと非常に遅い:それは得ることができますので、これは悪いようです[100]byteが、[]byte、および取り除くしない'\x00'バイトを。その速度(入力に依存します)は、受け入れられた回答の速度と比較して、数桁遅くなります。
icza
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.