注意:这里要区分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)
强制类型转换时会导致显示上看似无问题,但是实际上字符串并不相同。
要解决这个问题需要对[]byte
和string
的转换过程进行一个封装。
这里实现了针对两种情况的解决方案,前者是遇到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]) }