Làm thế nào để chuyển đổi một mảng byte kết thúc bằng không thành chuỗi?


502

Tôi cần đọc [100]byteđể chuyển một loạt stringdữ liệu.

Bởi vì không phải tất cả các strings đều dài chính xác 100 ký tự, phần còn lại của phần byte arrayđược đệm bằng 0s.

Nếu tôi chuyển đổi [100]byte thành string: string(byteArray[:]), các đuôi 0được hiển thị dưới dạng ^@^@s.

Trong C stringsẽ chấm dứt khi0 , vì vậy tôi tự hỏi đâu là cách tốt nhất để chuyển đổi nó byte arraythành stringGolang.


3
@ AndréLaszlo: Trong sân chơi ^@không hiển thị, nhưng nó sẽ ở đó nếu bạn kiểm tra nó trong thiết bị đầu cuối hoặc một cái gì đó tương tự. Lý do cho điều này là vì Go không ngừng chuyển đổi mảng byte thành chuỗi khi tìm thấy 0. len(string(bytes))trong ví dụ của bạn là 5 chứ không phải 1. Nó phụ thuộc vào hàm đầu ra, liệu chuỗi có được in đầy đủ (có số không) không hay không.
nemo

8
Đối với cơ thể phản hồi http, sử dụng string(body).
Ivan Châu

Câu trả lời:


513

Các phương thức đọc dữ liệu thành các lát byte trả về số byte đã đọc. Bạn nên lưu số đó và sau đó sử dụng nó để tạo chuỗi của bạn Nếu nlà số byte được đọc, mã của bạn sẽ trông như thế này:

s := string(byteArray[:n])

Để chuyển đổi chuỗi đầy đủ, điều này có thể được sử dụng:

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

Điều này tương đương với:

s := string(byteArray)

Nếu vì lý do nào đó bạn không biết n, bạn có thể sử dụng bytesgói để tìm gói, giả sử đầu vào của bạn không có ký tự null được nhúng trong đó.

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

Hoặc như icza đã chỉ ra, bạn có thể sử dụng mã dưới đây:

n := bytes.IndexByte(byteArray, 0)

2
Tôi biết tôi trễ một năm, nhưng tôi nên đề cập rằng hầu hết các phương thức đều trả về số byte đã đọc. Chẳng hạn, binary.Read () có thể đọc thành một byte [32], nhưng bạn không biết liệu bạn đã điền tất cả 32 byte hay chưa.
Eric Lagergren

7
Bạn nên sử dụng bytes.IndexByte()các tìm kiếm cho một bytethay vì bytes.Index()một lát byte chứa 1 byte.
icza

56
trên thực tế, chuỗi (byteArray) cũng sẽ làm như vậy và sẽ lưu một phần tạo lát
throws_exceptions_at_you

3
Mặc dù vậy, rõ ràng, điều này đang tạo ra một chuỗi byte cho thứ gì đó hy vọng là một chuỗi UTF-8 hợp lệ (và không nói, Latin-1, v.v., hoặc một chuỗi UTF-8 không đúng định dạng). Go sẽ không kiểm tra điều này cho bạn khi bạn cast.
Cameron Kerr

Điều gì xảy ra nếu mảng byte của bạn theo thứ tự ngược hay còn gọi là endian nhỏ?
Thưa ngài,

374

Thế còn?

s := string(byteArray[:])

3
Cách sạch nhất để chuyển đổi mảng byte cho chắc chắn. Tôi tự hỏi nếu chuỗi.Trim sẽ giúp loại bỏ các byte null? golang.org/pkg/strings/#example_Trim
andyvanee

24
câu hỏi đặc biệt nói rằng string(byteArray[:])có chứa các ^@ký tự
Robert

24
Sự khác biệt là string(byteArray)gì? Tại sao bạn cần phải sao chép mảng bằng cách sử dụng [:]?
Robert Zaremba

7
@RobertZaremba> một chuỗi có hiệu lực là một lát byte chỉ đọc. Bạn không thể chuyển đổi mảng byte trực tiếp thành chuỗi để lát đầu tiên sau đó chuỗi.
Ferhat elmas

3
@RobertZaremba Đối với các lát byte bạn không cần thêm [:], cho các mảng byte, bạn làm.
Drew LeSueur

68

Giải pháp đơn giản:

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

Tôi không chắc chắn làm thế nào biểu diễn này là mặc dù.


17

Ví dụ,

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

Đầu ra:

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

8

Đoạn mã sau đang tìm kiếm '\ 0' và theo các giả định của câu hỏi, mảng có thể được xem là được sắp xếp vì tất cả không - '\ 0' đứng trước tất cả '\ 0'. Giả định này sẽ không giữ nếu mảng có thể chứa '\ 0' trong dữ liệu.

Tìm vị trí của byte 0 đầu tiên bằng cách sử dụng tìm kiếm nhị phân, sau đó cắt.

Bạn có thể tìm thấy byte không như thế này:

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

Có thể nhanh hơn chỉ cần quét một cách ngây thơ mảng byte tìm kiếm byte không, đặc biệt nếu hầu hết các chuỗi của bạn ngắn.


8
Mã của bạn không biên dịch và, ngay cả khi nó đã làm, nó sẽ không hoạt động. Một thuật toán tìm kiếm nhị phân tìm thấy vị trí của một giá trị được chỉ định trong một mảng được sắp xếp. Các mảng không nhất thiết phải được sắp xếp.
peterSO

@peterSO Bạn nói đúng, và thực tế nó không bao giờ được sắp xếp vì nó đại diện cho một loạt các tên có ý nghĩa.
Derrick Zhang

3
Nếu tất cả các byte null nằm ở cuối chuỗi, một tìm kiếm nhị phân hoạt động.
Paul Hankin

6
Tôi không hiểu các downvote. Mã biên dịch và đúng, giả sử chuỗi không chứa \ 0 ngoại trừ ở cuối. Mã đang tìm kiếm \ 0 và theo các giả định của câu hỏi, mảng có thể được coi là 'được sắp xếp', vì tất cả không phải là \ 0 trước tất cả \ 0 và đó là tất cả mã đang kiểm tra. Nếu downvoters có thể tìm thấy một ví dụ đầu vào mà mã không hoạt động, thì tôi sẽ xóa câu trả lời.
Paul Hankin

1
Cho kết quả sai nếu đầu vào là []byte{0}. Trong trường hợp này FirstZero()nên trả về 0vì vậy khi kết quả cắt sẽ được "", nhưng thay vào đó, nó trả về 1và cắt kết quả "\x00".
icza

3

Khi bạn không biết độ dài chính xác của các byte không có trong mảng, bạn có thể cắt nó trước:

chuỗi (byte.Trim (mảng, "\ x00"))


1
a) bytes.Trimlấy một lát, không phải là một mảng (bạn cần arr[:]nếu mảng thực sự là một [100]bytetrạng thái câu hỏi). b) bytes.Trimlà chức năng sai để sử dụng ở đây. Đối với đầu vào như thế, []byte{0,0,'a','b','c',0,'d',0}nó sẽ trả về "abc \ x00d" thay vì "" c) đã có câu trả lời đúng sử dụng bytes.IndexByte, cách tốt nhất để tìm byte 0 đầu tiên.
Dave C

1

Tại sao không phải điều này?

bytes.NewBuffer(byteArray).String()

1
Bởi vì a) câu hỏi nói một mảng như vậy tại bạn cần byteArray[:]từ bytes.NewBuffermất []byte; b) câu hỏi cho biết mảng có các số 0 ở cuối mà bạn không xử lý; c) nếu thay vào đó biến của bạn là một []byte(cách duy nhất dòng của bạn sẽ biên dịch) thì dòng của bạn chỉ là một cách làm chậm string(v).
Dave C

1

Chỉ sử dụng để điều chỉnh hiệu suất.

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: Không chắc đây có phải là một câu trả lời nghiêm túc hay không, nhưng bạn gần như chắc chắn không muốn gọi mã phản chiếu và mã không an toàn chỉ để chuyển một lát byte thành chuỗi
Austin Hyde

1
Một lời cảnh báo: sử dụng không an toàn để chuyển đổi một lát byte thành một stringcó thể có ý nghĩa nghiêm trọng nếu sau đó lát cắt byte được sửa đổi. stringcác giá trị trong Go được xác định là không thay đổi, theo đó toàn bộ thời gian chạy và thư viện Go được xây dựng. Bạn sẽ dịch chuyển bản thân vào giữa những lỗi bí ẩn nhất và lỗi thời gian chạy nếu bạn đi xuống con đường này.
icza

Đã chỉnh sửa, vì điều này chống lại việc sử dụng con trỏ (nó có hành vi tương tự như truyền trực tiếp, nói cách khác, kết quả sẽ không được thu gom rác). Đọc đoạn văn (6) golang.org/pkg/unsafe/#Pulum
Laevus Dexter

0
  • Sử dụng lát thay vì mảng để đọc. ví dụ: io.Readerchấp nhận một lát cắt, không phải là một mảng.

  • Sử dụng cắt thay vì không đệm.

Thí dụ:

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

Dữ liệu được viết bởi người khác và bằng ngôn ngữ C khác, và tôi chỉ phải đọc nó, vì vậy tôi không thể kiểm soát cách viết.
Derrick Zhang

1
Ồ, sau đó cắt mảng byte bằng giá trị độ dài s := a[:n]hoặc s := string(a[:n])nếu bạn cần một chuỗi. Nếu nkhông có sẵn trực tiếp, nó phải được tính toán, ví dụ bằng cách tìm kiếm một byte cụ thể / không trong bộ đệm (mảng) như Daniel gợi ý.
zzzz

0

Tôi đã thử vài phương pháp vài lần tôi thấy hoảng loạn:

lỗi thời gian chạy: giới hạn lát ngoài phạm vi.

Nhưng điều này cuối cùng đã làm việc.

string(Data[:])


3
Điều này không thêm nhiều thông tin và về cơ bản lặp lại câu trả lời từ năm 2013: stackoverflow.com/a/18615786/349333 .
Joool Schulenklopper

0

Mặc dù không thực sự hiệu quả, giải pháp duy nhất có thể đọc được là

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

Ví dụ đầy đủ để có một mảng byte kiểu 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)
}

Ví dụ đầy đủ để có chuỗi kiểu đi không bao gồm null:

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

Điều này phân bổ một lát byte. Vì vậy, hãy theo dõi hiệu suất nếu nó được sử dụng nhiều hoặc lặp đi lặp lại.


-1

Đây là mã để nén mảng byte thành chuỗi

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

Đây là cách nhanh hơn:

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

Lần sau hãy đọc câu hỏi (và câu trả lời hiện có) trước. (Ngoài ra, nếu bạn thực sự muốn in một lát byte thông qua fmtnó sẽ nhanh fmt.Printf("%s", bytes)hơn để sử dụng string(bytes)).
Dave C

-7

Tôi khi với một giải pháp đệ quy.

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

Tại sao bạn thích một giải pháp đệ quy?
peterSO

Các trường hợp thử nghiệm fmt.Println(CToGoString([]byte("ctogo\x00\x00"), "") == "ctogo")nên in true, nó in false.
peterSO

1
Câu hỏi hỏi cách tốt nhất là gì . Điều này tệ đến mức có thể nhận được: khó hiểu và cực kỳ chậm, cũng không chuyển đổi[100]byte nhưng một []byte, và không loại bỏ '\x00'byte. Tốc độ của nó (phụ thuộc vào đầu vào) chậm hơn theo nhiều bậc so với tốc độ của câu trả lời được chấp nhận.
icza
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.