smallnewer / bugs

18 stars 4 forks source link

golang的字符串 #93

Open smallnewer opened 8 years ago

smallnewer commented 8 years ago

在golang和js中的字符串是有些不一样的。在服务器编程中尤其需要注意这点。js中的字符串更符合人类的认知。

在js中部分常见字符串操作:

var str = "abc汉字"
str.length;  // 5
str[3];    // 汉

golang中

var str = "abc汉字"
len(str);  // 9 字节长度,utf8下汉字许多是3个字节
str[3];    // 230 按照字节访问

服务器编程中,基本上都提供了对字节进行编程的能力。这块实际上是js缺失的。因此在nodejs中也提供了该能力的Buffer类。看:

> new Buffer("汉")
<Buffer e6 b1 89>
> parseInt('e6', 16)
230

可以看到由三个字节组成,第一个16进制数字转为10进制后成为了230,跟上例golang中的表现一致。

因为在我们的业务逻辑中,很少涉及到底层处理字节的问题。主要对类似js访问字符串的能力更加依赖。所以主要看下这些JS转golang比较痛苦的地方。

获取字符串的真实长度

上例中len(str)取到的是字节长度,要获取字符串长度的话,首先用到rune数组。 golang中rune更像js中的字符串。

str := "abc汉字"
a := []rune(str)  // 转换成rune数组
len(a)    // 5
a[3]    // 27721 这里返回的是unicode编码
string(a[3])   // "汉" 需要用这种方式,转换成真实的汉字

rune提供了更类似的js对字符的操作能力。因此在开发中应该比string使用的更多。

但我个人对rune转换性能有疑问,做了个简单的测试

// 7. 获取字符串长度性能
    str := "laoYu老虞laoYu老虞laoYu老虞laoYu老虞laoYu老虞"

    // 方式1,700ns
    //    5倍长度后,7us
    st := time.Now()
    a := []rune(str) // 700ns 慢
    fmt.Println("rune time", time.Since(st), len(a))

    str1 := "laoYu老虞laoYu老虞laoYu老虞laoYu老虞laoYu老虞"
    // 方式2,400ns
    //    5倍长度后,700ns
    st1 := time.Now()
    len1 := 0
    no := 0
    for i, _ := range str1 {
        len1++
        no = i
    }
    fmt.Println("range time", time.Since(st1), len1, no)

结论有几点:

  1. 越长的字符串,转换成rune数组越耗时。
  2. 尽量避免频繁的格式转换。譬如用缓存等等。
  3. 如果仅仅只是为了获取字符串的长度,可以考虑用方式二处理。