作者:@apocelipes
本文为作者原创,转载请注明出处:https://www.cnblogs.com/apocelipes/p/9798413.html


string我们每天都在使用,可是对于string的细节问题你真的了解吗?

今天我们先以一个问题开篇。

你能猜到下面代码的输出吗?

package main

import (
"fmt"
) func main() {
s := "测试"
fmt.Println(s)
fmt.Println(len(s))
fmt.Println(s[0])
for _, v := range s {
fmt.Println(v)
}

谜底揭晓:

是不是觉得很奇怪?明明是2个汉字,为啥长度是6?为啥s[0]是个数字,又为啥长度是6却只循环了两次,而且输出的也是数字?

别急,我们一个个地说明。

string的真实长度

要知道string的长度,首先要知道string里到底存了什么,我们看下官方的文档:

type string string
string is the set of all strings of 8-bit bytes, conventionally but not
necessarily representing UTF-8-encoded text. A string may be empty, but not
nil. Values of string type are immutable.

是的,没看错,在string里存储的是字符按照utf8编码后的“8-bit bytes”二进制数据,再说得明确点,就是我们熟悉的byte类型:

type byte = uint8
byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
used, by convention, to distinguish byte values from 8-bit unsigned integer
values.

我们都知道,utf8在表示中文时需要2个字节以上的空间,这里我们一个汉字是3字节,所以总长度就是我们直接用len得到的6。

从string中索引到的值

从string里使用索引值得到的数据也是byte类型的,所以才会输出数字,最好的证据在于此(最后还会有证明代码),还记得byte的文档吗:

type byte = uint8

如果看不懂,没关系,这是golang的type alias语法,相当于给某个类型起了个别名,而不是创建了新类型,所以byte就是uint8。

所以,输出uint8类型的数据,那么自然会看到数字。

range string时发生了什么?

那么range的情况呢,长度是,为什么只循环两次?

首先我们可以排除byte了,uint8怎么可能会有20000的值。

然后我们来看一下官方文档,其中有这么一段:

For strings, the range does more work for you, breaking out individual
Unicode code points by parsing the UTF-8. Erroneous encodings consume
one byte and produce the replacement rune U+FFFD.
(The name (with associated builtin type) rune is Go terminology for a single Unicode code point. See the language specification for details.) The loop

有点长,大致意思就是range会把string里的byte重新转换成utf8字符,对于错误的编码就用一字节的占位符替代,这下清楚了,range实际上和如下代码基本等价:

for _, v := range []rune(s)

我们是字符串正好是2个utf8字符,所以循环输出两次。我们再看看看看rune的文档:

type rune = int32
rune is an alias for int32 and is equivalent to int32 in all ways. It is
used, by convention, to distinguish character values from integer values.

rune是int32的别名,它的值是Unicode码点,所以当我们println时就看到了数字。

代码验证

虽然没什么必要,但我们还是可以通过代码不算太严谨地验证一下我们得到的结论,想获取变量的类型,使用reflect.TypeOf即可(无法获取别名,所以“不严谨”):

package main

import (
"fmt"
"reflect"
) func main() {
s := "测试"
fmt.Println("s type:", reflect.TypeOf(s))
fmt.Println("s[index] type:", reflect.TypeOf(s[0]))
for _, v := range s {
fmt.Println("range value type:", reflect.TypeOf(v))
}
}

与我们预想的一样,uint8是byte,int32是rune,虽然TypeOf无法输出类型别名,但我们还是可以粗略判断出它的类型名称。

[转]全面认识golang string的更多相关文章

  1. golang string和[]byte的对比

    golang string和[]byte的对比 为啥string和[]byte类型转换需要一定的代价?为啥内置函数copy会有一种特殊情况copy(dst []byte, src string) in ...

  2. 全面认识golang string

    string我们每天都在使用,可是对于string的细节问题你真的了解吗? 今天我们先以一个问题开篇. 你能猜到下面代码的输出吗? package main import ( "fmt&qu ...

  3. golang string转json的一些坑

    先带来点冷知识,不知道大家知不知道,反正我刚知道... 大佬们都知道怎么在string中给string类型赋值带双引号的字符串,没错就是用反斜杠,如下: msg := "{\"na ...

  4. golang string int int64转换

    #string到int int,err:=strconv.Atoi(string) #string到int64 int64, err := strconv.ParseInt(string, 10, 6 ...

  5. golang string、int、int64 float 互相转换

    #string到int int,err := strconv.Atoi(string) #string到int64 int64, err := strconv.ParseInt(string, 10, ...

  6. Golang中string和[]byte的对比

    golang string和[]byte的对比 为啥string和[]byte类型转换需要一定的代价? 为啥内置函数copy会有一种特殊情况copy(dst []byte, src string) i ...

  7. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...

  8. golang cgo 使用总结

    原文地址 CGO 提供了 golang 和 C 语言相互调用的机制.某些第三方库可能只有 C/C++ 的实现,完全用纯 golang 的实现可能工程浩大,这时候 CGO 就派上用场了.可以通 CGO ...

  9. golang 字符串与整数, 布尔转换 strconv

    strconv 是golang对于字符串和基本数据类型之间的转换字符串转整数testStr := "1000" testInt, err := strconv.Atoi(testS ...

随机推荐

  1. pipenv管理python开发环境

    简介 简单说,pipenv就是把pip和virtualenv包装起来的一个便携工具. 它不会在你的项目文件夹里生成一大堆东西,只有两个文本文件: Pipfile, 简明地显示项目环境和依赖包. Pip ...

  2. python处理孤立的异常点

    假设有一个列表,a = [61, 40, 70, 80, 86, 50, 88, 33, 76, 64],保存的是设备的状态值随时间的变化,超过60即为异常,但是对于孤立的异常点,我们需要将其忽略,只 ...

  3. 笔记4:WEB服务器

    web服务器 1 HTTP协议 http:超文本传输协议,基于tcp的方式,会更稳当更安全.协议就是规定了怎样去请求服务器,服务器如何返回信息.如下图红色方框标记所示: 打开浏览器电商广告原理: 我们 ...

  4. 02微信小程序-轮播的宽度100%显示和轮播的基础配置

    1==>如何让轮播的宽度100%显示? 你先给swiper 外面添加一个大盒子,给大盒子一个类 . <view class='lunbobox'> 然后wxss 里面设置 image ...

  5. sed-grep命令

    sed -i '$a IPADDR=192.168.1.199' /etc/sysconfig/ifcfg-eth0work-scripts/ifcfg-eth0 #追加ip地址. sed -i '$ ...

  6. day8_7.8 文件操作

    一.文件模式 1.在文件的操作中,也有很多补充 的模式可使用 1.r+,可读可写模式,在此模式中,操作权限时可读可写,这里的写与write模式不一样的是,不再清空文件内容,写的内容添加到文件的后面,而 ...

  7. 02-numpy-笔记-amin

    >>> a = np.arange(4).reshape((2,2)) >>> a array([[0, 1], [2, 3]]) >>> np. ...

  8. Django简介(MVC、MTV)

    Django简介 MVC Model(模型)- 应用程序中处理数据逻辑部分且与数据库交互,用于存取数据的部分 View(视图)- 用于处理后的数据界面展示,且视图通常是由模型数据创建的,是用户看到并与 ...

  9. DLinNLP

    2015蒙特利尔深度学习暑期学校之自然语言处理篇 用户1737318 8月3日至8月12日在蒙特利尔举办的深度学习署期学校中,来自不同领域的深度学习顶尖学者 (Yoshua Bengio, Leon ...

  10. JDOJ 1946 求最长不下降子序列个数

    Description 设有一个整数的序列:b1,b2,…,bn,对于下标i1<i2<…<im,若有bi1≤bi2≤…≤bim 则称存在一个长度为m的不下降序列. 现在有n个数,请你 ...