Как преобразовать массив байтов с нулевым завершением в строку?
Я столкнулся с проблемой чтения массива байтов <code>[100]byte</code>
, чтобы передать многострочные данные типа <code>string</code>
. Однако не все строки имеют длину ровно 100 символов, и оставшаяся часть массива байтов заполняется нулями (<code>0</code>
).
Когда я пытаюсь преобразовать <code>[100]byte</code>
в <code>string</code>
, используя следующий код: <code>string(byteArray[:])</code>
, оставшиеся нули отображаются как <code>^@^@</code>
.
В C строка завершается по нулю, поэтому как лучше всего преобразовать этот массив байтов в строку в Go?
5 ответ(ов)
Методы, которые считывают данные в байтовые срезы, возвращают количество считанных байт. Вам следует сохранить это число, а затем использовать его для создания строки. Если n
— это количество считанных байт, ваш код будет выглядеть следующим образом:
s := string(byteArray[:n])
Для преобразования всей строки можно использовать:
s := string(byteArray[:len(byteArray)])
Это эквивалентно:
s := string(byteArray[:])
Если по какой-то причине вы не знаете n
, вы можете воспользоваться пакетом bytes
, чтобы найти его, при условии, что во входных данных нет встроенного нулевого символа.
n := bytes.Index(byteArray[:], []byte{0})
Или, как заметил icza, можно использовать следующий код:
n := bytes.IndexByte(byteArray[:], 0)
Чтобы преобразовать массив байтов в строку в Go, можно использовать следующий код:
s := string(byteArray[:])
Здесь byteArray
— это массив байтов, и мы создаем строку s
, используя преобразование массива байтов в строку. Этот метод эффективно копирует данные из массива байтов и создает новую строку, которая содержит те же символы. Учтите, что если у вас есть необработанные данные или значения, выходящие за пределы диапазона байтов, это может привести к недопустимым символам в строке.
Простой способ решения:
str := fmt.Sprintf("%s", byteArray)
Однако я не уверен в производительности этого подхода.
В данном примере вы видите, как работает преобразование данных из формата C в формат Go с помощью функции CToGoString
. Функция принимает срез байтов c
(представляющий строку, заканчивающуюся нулевым байтом) и возвращает строку Go, обрезанную до первого нулевого байта.
Вот краткая разборка кода:
package main
import "fmt"
// CToGoString преобразует срез байтов из C в строку Go
func CToGoString(c []byte) string {
n := -1 // переменная для хранения индекса последнего байта, не равного нулю
for i, b := range c {
if b == 0 { // если встретили нулевой байт, выходим из цикла
break
}
n = i // обновляем индекс n
}
return string(c[:n+1]) // возвращаем строку с обрезкой до первого нулевого байта
}
func main() {
c := [100]byte{'a', 'b', 'c'} // создаем массив байтов, который содержит символы a, b и c
fmt.Println("C: ", len(c), c[:4]) // выводим длину массива и его первые 4 элемента
g := CToGoString(c[:]) // преобразуем массив байтов в строку Go
fmt.Println("Go:", len(g), g) // выводим длину строки и её значение
}
В main
происходит следующее:
- Создаётся массив
c
из 100 байтов, инициализированный тремя символами: 'a', 'b', 'c'. - Затем выводится длина массива и его первые 4 элемента (т.е.
[97 98 99 0]
, где 97, 98 и 99 — это ASCII-коды символов 'a', 'b' и 'c', а 0 — нулевой байт). - Функция
CToGoString
преобразует срезc[:]
в строку Go, правильно обрезая её до первого нулевого байта. - В конце выводится длина результирующей строки и её значение.
Вывод программы:
C: 100 [97 98 99 0]
Go: 3 abc
Таким образом, функция CToGoString
полезна для корректной работы с C-строками в Go, обеспечивая правильное обрезание строк до первого нулевого байта.
Вы привели интересный пример кода на Go, который ищет первую нулевую байтовую метку ('\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))
}
Функция FirstZero
выполняет следующий алгоритм:
- Устанавливаются начальные значения
min
иmax
, гдеmin
- индекс начала массива, аmax
- длина массива. - В цикле проверяется условие окончания, если
min + 1
равноmax
, это означает, что мы достигли границы поиска. В таком случае возвращаемmax
, что указывает на позицию первого нулевого байта. - Вычисляется индекс
mid
, который представляет собой середину текущего диапазона. - Если байт на позиции
mid
равен '\0', то мы продолжаем искать в левой части (уменьшаемmax
). - Если байт не равен '\0', то продолжаем искать в правой части (увеличиваем
min
).
Наконец, функция возвращает индекс первой нулевой метки.
Тем не менее, в большинстве случаев может оказаться быстрее просто наивно пройтись по массиву в поисках нулевого байта, особенно если ваши строки короткие. Бинарный поиск эффективен, когда массив действительно отсортирован, но для малых массивов или в ситуациях, когда производительность не критична, линейный поиск может быть более простым и эффективным вариантом.
Существует ли цикл foreach в Go?
Как сопоставить любой символ на нескольких строках в регулярном выражении?
Отформатировать строку в Go без вывода?
Как присвоить строку массиву байтов
Есть ли способ получить доступ к приватным полям структуры из другого пакета?