Golang []byte与string转换的一个误区

743

在实现[]byte转换string的过程中,发现了一个很容易理解错误的地方。
注意:这里要区分0'\0''0'的区别。其中前两者等价,是内存中实际的值。而'0'是显示的值,其在内存中实际是48,也即0x30

在C语言中,字符串的结尾是'\0',也即字节为0的字符。

#include <stdio.h>
#include <string.h>

int main() {
    char s[] = {'a', 'b', 'c', '\0', '\0', 'd'};
    printf("%d %d %s\n", strlen(s), sizeof(s), s);
   
    return 0;
}


这个程序输出的内容是3 6 abc






而在Go语言中,字符串仅仅把对应字节为0的字符认为是空字符,不显示但是仍然占用长度。

package main

import (
    "fmt"
)

func main() {
    b := []byte{'a', 'b', 'c', 0, 0, 'd'}
    s := string(b)
    fmt.Printf("%d %s %s\n", len(s), s, b)
}


这个程序输出的内容是6 abcd abcd


0在C语言以及Go语言不同的表现在大部分情况下不会造成问题,但是当使用io.Reader(b []byte)时,如果传入的字节数组b本身长度大于reader可读到的长度,则会导致末尾被0补齐。当直接使用string(b)强制类型转换时会导致显示上看似无问题,但是实际上字符串并不相同。
要解决这个问题需要对[]bytestring的转换过程进行一个封装。

这里实现了针对两种情况的解决方案,前者是遇到0就结束转换,后者则是忽略所有的0并将剩余部分拼接

// String 将 `[]byte` 转换为 `string`
func String(b []byte) string {
    for idx, c := range b {
        if c == 0 {
            return string(b[:idx])
        }
    }
    return string(b)
}

// StringWithoutZero 将 `[]byte` 转换为 `string`
func StringWithoutZero(b []byte) string {
    s := make([]rune, len(b))
    offset := 0
    for i, c := range b {
        if c == 0 {
            offset++
        } else {
            s[i-offset] = rune(c)
        }
    }
    return string(s[:len(b)-offset-1])
}
发布评论
  • 点击查看/关闭被识别为广告的评论