注意:这里要区分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])
}