字符、字符串以及布尔类型
因为字符与布尔类型内容相对来说较少,就和字符串类型放在了一起(凑篇幅)哈哈,话不多说,接下来一个个看。
字符类型
在Go语言中,实际上字符类型是并不存在的,个人猜测是因为Go语言崇尚精简,为了省下几个关键字。而实际上,Go语言的byte类型其实就是int类型。接下来我们通过例子来看:
var char1 byte='0'
char2:='风'
fmt.Printf("char1=%c,char1=%T\n",char1,char1)
fmt.Printf("char2=%c,char2=%T\n",char2,char2)
代码中我们分别输出了char1和char2的值和类型,来看运行结果:
char1=0,char1=uint8
char2=风,char2=int32
可以看到,char1的类型为uint8,而char2的类型为int32,由此我们可以看出byte类型实际上就是int类型。归纳一下,可以说:当存放单个字符(比如上面的char1)时,使用的是unit8类型,而存放中文、日文等字符时(上面的char2),使用的就是int32类型了,而这里的int32类型其实是另一种整形rune类型,该类型几乎可以等价于int32类型,而官方对它的描述是:
翻译一下就是:
rune是int32的别名,在所有方面都等同于int32,按照惯例,用于区分字符值和整数值,从类型上看我们也能知道,它是一个基于int32的自定义类型(这个后面会讲),实际用途还是用于区分字符和整数。
其实也很好理解为什么字符和中文字符使用的类型不一样,因为中文字符的长度已经超出了uint8所能描述的长度。而Go中的字符有几个比较特殊的地方:
- 字符和字符串使用的都是UTF-8编码,这样再也不会出现编码问题了,相信你一定碰到过自己机器上写好的项目去另一台机器上打开就乱码的问题。
- 一个字符占1个字节,这点不用说,而一个中文字符占3个字符(因为使用了UTF-8编码嘛)
当我们以数值的格式输出字符时,就会输出它们的码值(ASCII):
fmt.Printf("char1=%d,char2=%d\n",char1,char2)
运行:
char1=48,char2=39118
而我们如果对一个字符进行运算,则会出现:
fmt.Printf("加和=%d,a的编码值=%d\n",100+var1,var1)
运行:
加和=197,a的编码值=97
可以看见字符的运算实际上是码值间的运算。
字符串类型
字符串可以说是平时用的最多的类型了,Go中的字符串和其他语言差不多,接下来看。
关键字还是string,声明一个:
var string1 string
string1 ="风间天道\n"
在Go中,我们还可以用反引号的方法,表示将其中的内容以它原有的格式输出,在js中我记得好像是模板字符串的用法。比如我们将上面字符类型的代码文件以原格式输出:
var string2 =`package main
import "fmt"
func main() {
var char1 byte='0'
char2 := '风'
// var char3 byte='风' 字符为中文时会推断为rune类型,其实质是int32,因为byte的长度不足以容纳
fmt.Printf("char1=%d,char2=%d\n",char1,char2)
//直接输出是输出的码值,输出该字符使用%c输出
fmt.Printf("char2=%c,char2=%T\n",char2,char2)
var1:='a'
//求和时是按照码值运行,因为字符类型本质是int
fmt.Printf("加和=%d,a的编码值=%d\n",100+var1,var1)
}`
fmt.Printf("string1=%T,string2=%T\n", string1, string2)
fmt.Printf("string1=%v,string2=%v\n", string1, string2)
分别输出string1和string2的类型、值,结果如下:
string1=string,string2=string
string1=风间天道
,string2=package main
import "fmt"
func main() {
var char1 byte='0'
char2 := '风'
// var char3 byte='风' 字符为中文时会推断为rune类型,其实质是int32,因为byte的长度不足以容纳
fmt.Printf("char1=%d,char2=%d\n",char1,char2)
//直接输出是输出的码值,输出该字符使用%c输出
fmt.Printf("char2=%c,char2=%T\n",char2,char2)
var1:='a'
//求和时是按照码值运行,因为字符类型本质是int
fmt.Printf("加和=%d,a的编码值=%d\n",100+var1,var1)
}
可以看到反引号中的内容的确以原来的格式输出了。
接下来提几种常用的字符串操作。
输出当前字符串的长度
使用Go中内置的函数len()输出字符串的长度:
fmt.Println(len(string2))
结果:
499
有耐心的朋友可以去数数看。。
字符串拼接
字符串拼接在java中是一个老生常谈的问题,(string、stringbuffer、stringbuilder三兄弟),在Go中我们也有好几种方式。因为Go中的所有类型都是值类型(后面再说),因此各种拼接方法肯定性能有所不同,这里提几种:
- 首先肯定是最简单的,使用运算符拼接:
fmt.Println("风间"+"天道")
从性能的角度,这种方式肯定是最差的,因为string是值类型,使用拼接的方式相当于在原有的基础上又要开辟空间来存储新的变量,无用的string接着被GC。
- 其次是内置函数Sprint(),该函数类似于我们常用的Print,该方法的注释上大概说的是:
Sprint使用其操作数的默认格式,并返回结果字符串。如果两个操作数都不是字符串,则在操作数之间添加空格。
也就是说该函数会返回一个拼接好的字符串
_:=fmt.Sprint("风间","天道")
- 使用strings包中的Join()方法
strings是专门用于操作字符串的包,其中提供了许多方法,比如我们用来拼接字符串的Join方法,该方法的注释是这样写的:
Join连接第一个参数的元素以创建单个字符串,字符串sep放置在结果字符串中的元素之间。
举例:
fmt.Println(strings.Join([]string{"风","间"},"天道3"))
从方法的参数可以看到,传入的是一个字符串数组,以及我们需要拼接进这个数组的字符串,最后返回的结果会将这个插入的字符串放在中间:
风天道3间
- 使用bytes包中Buffer的WriteString()+String()方法,bytes包与strings包类似,用于操作byte类型,而其中的buffer则是用于封装简单的byte类型,使用WriteString先将参数里的字符串放入缓冲区,然后通过String()方法生成字符串:
var buffer bytes.Buffer
buffer.WriteString("风间")
buffer.WriteString("天道")
buffer.WriteString("4")
fmt.Println(fmt.Println(buffer.String()))
遍历字符串
遍历字符串首先获取该字符串的长度,接着使用for循环对其中的字符进行挨个输出即可:
var string3="hello,风间天道"
string3Len:=len(string3)
for index:=0;index<string3Len;index++{
fmt.Printf("%s--编码值=%d,值=%c\n",string3,string3[index],string[index])
}
结果:
hello,风间天道--编码值=104,值=h
hello,风间天道--编码值=101,值=e
hello,风间天道--编码值=108,值=l
hello,风间天道--编码值=108,值=l
hello,风间天道--编码值=111,值=o
hello,风间天道--编码值=44,值=,
hello,风间天道--编码值=233,值=é
hello,风间天道--编码值=163,值=£
hello,风间天道--编码值=142,值=
hello,风间天道--编码值=233,值=é
hello,风间天道--编码值=151,值=
hello,风间天道--编码值=180,值=´
hello,风间天道--编码值=229,值=å
hello,风间天道--编码值=164,值=¤
hello,风间天道--编码值=169,值=©
hello,风间天道--编码值=233,值=é
hello,风间天道--编码值=129,值=
hello,风间天道--编码值=147,值=
代码中我们遍历输出了字符串中每个字符的值以及码值,但发现到输出中文字符时就乱码了,原因其实是因为字符串类型本身的问题,查看string类型的源码注释,是这样写的:
string is the set of all strings of 8-bit bytes(string是8位字节的所有字符串的集合),上面在说byte类型就有说到,byte存储单个字符采用uint8,而存储中文等字符则采用int32(rune),而遍历输出时将每个字符都当做了uint8类型处理,于是到中文时就乱码了,明白了原因,那么解决起来就很方便了。下面就说说两种解决方式吧。
- 见招拆招,既然unit8对中文乱码,那么我们将整个字符串提前先转为rune类型切片,这样长度肯定就够了:
stringRune=[]rune(string3)
stringLen:=len(stringRune)
for index:=0;index<stringLen;index++{
fmt.Pringf("%s--编码值=%d,值=%c,类型%T\n",string3,stringRune[index],stringRune[index],stringRune[index])
}
再看结果:
hello,风间天道--编码值=104,值=h,类型=int32
hello,风间天道--编码值=101,值=e,类型=int32
hello,风间天道--编码值=108,值=l,类型=int32
hello,风间天道--编码值=108,值=l,类型=int32
hello,风间天道--编码值=111,值=o,类型=int32
hello,风间天道--编码值=44,值=,,类型=int32
hello,风间天道--编码值=39118,值=风,类型=int32
hello,风间天道--编码值=38388,值=间,类型=int32
hello,风间天道--编码值=22825,值=天,类型=int32
hello,风间天道--编码值=36947,值=道,类型=int32
此时乱码问题完美解决,但在Go中我们还可以使用一种更好的办法
- 使用Go语言中独有的for循环方式for range,这种方式我们也会经常用到的,这里就不多提,到后面聊流程控制时我们再细讲:
for _,value:=range string3{
fmt.Printf("%s--编码值=%d,值=%c\n,类型=%T\n",string3,value,value,value)
}
结果:
hello,风间天道--编码值=104,值=h,类型=int32
hello,风间天道--编码值=101,值=e,类型=int32
hello,风间天道--编码值=108,值=l,类型=int32
hello,风间天道--编码值=108,值=l,类型=int32
hello,风间天道--编码值=111,值=o,类型=int32
hello,风间天道--编码值=44,值=,,类型=int32
hello,风间天道--编码值=39118,值=风,类型=int32
hello,风间天道--编码值=38388,值=间,类型=int32
hello,风间天道--编码值=22825,值=天,类型=int32
hello,风间天道--编码值=36947,值=道,类型=int32
和上面的结果一样。
布尔类型
布尔类型就不用多说了,用于做一些断言,有true和false两种值。声明一个布尔变量默认为false。之前在聊常量的时候也有说到,true和false是属于系统预编译的变量。这里就简单的举几个例子看看吧。
var bool1 bool
bool1=true
bool2:=(true==false)
fmt.Println(bool1,bool2)
boo1我们将它赋为了true,而bool2接收了true==false的运算结果,true肯定不等于false,输出结果:
true false
我们还可以将它运用在条件判断语句中:
if 3==4{
fmt.Println("false")
}else{
fmt.Println("true")
}
判断如果3==4,那么输出false,否则输出true,结果:
true