七叶笔记 » golang编程 » 学习Go每日一练4:不额外申请内存反转含汉字的byte数组

学习Go每日一练4:不额外申请内存反转含汉字的byte数组

要求:

test := “Happy every day,我是中国人!”

将其转成byte数组:

 t1 := []byte(test)  

然后执行反转函数:

 ReverseByte(&t1)  

再打印其内容:

 fmt.Println(string(t1))  

要求输出内容如下:

 !人国中是我,yad yreve yppaH  

分析:

byte数组原地反转是很容易实现的,但汉字占3个byte,原地反转后会转成乱码,难点就在于反转后还要处理utf8编码的字符。

代码:

 func ReverseByte(b *[]byte) {
ln := len(*b)
for i, j := 0, ln-1; i < j; i, j = i+1, j-1 {
(*b)[i], (*b)[j] = (*b)[j], (*b)[i]
}

isUtf := func(n byte) int {
switch {
// utf8字符首字节前4位是1,第5位是0,若屏蔽后3位结果是0b11110000,那么要连读4个字节,返回长度4
case n&0b11111000 == 0b11110000:
return 4
// utf8字符首字节前3位是1,第4位是0,若屏蔽后4位结果是0b11100000,那么要连读3个字节,返回长度3
case n&0b11110000 == 0b11100000:
return 3
// utf8字符首字节前2位是1,第3位是0,若屏蔽后5位结果是0b11000000,那么要连读2个字节,返回长度2
case n&0b11100000 == 0b11000000:
return 2
case n&0b11000000 == 0b10000000:
return -1
default: // 其他情况判断为非utf8字符,一次读1字节,返回长度1
return 1
}
}

for i, n := 0, 0; i < ln; i++ {
switch isUtf((*b)[i]) {
case 1: // 判断为普通字符
continue
case -1: // 判断为utf字符的尾部
n++
default: // 判断为utf字符的头部
for x, y := i-n, i; x < y; x, y = x+1, y-1 {
(*b)[x], (*b)[y] = (*b)[y], (*b)[x]
}
n = 0
}
}
}  

思路:

第一次反转后,需要再次遍历byte数组,检查utf8字符首字节和后续字节顺序颠倒的状况,对于utf8后续字节是0b10开头,遇到后做累加计数。首字节有3种情况,分别是0b110对应2字节的utf8编码、0b1110对应3字节的utf8编码、0b1111对应4字节的utf8编码。最后再根据计时器将utf8首字节与后续字节顺序还原。

小结:

对于utf8编码的字符串要熟练掌握,处理含中文字符串可以写出更省内存更高效的函数。

代码重构

对于字符串反转来说,不需要判断分别到底是几个字节的utf字符,只要分辨是头字符还是尾字符即可。这样的话我们减少判断精细度,大大减少代码量。

相关文章