变量和常量
变量
标准声明
var 变量名 变量类型 复制代码
举个栗子🌰
var age int var name string var isOk bool 复制代码
批量声明
var ( 变量名 类型 变量名 类型 变量名 类型 ) 复制代码
举个栗子🌰
var ( age int name string isOk bool ) 复制代码
变量的初始化
Go语言声明变量的时候,每个变量其实会被初始化成该类型的默认值。
- 整型和浮点型变量为
0
- 字符串变量为
空字符串
- 布尔型变量为
false
- 切片、函数、指针变量默认为
nil
声明并指定初始值的标准格式:
var 变量名 类型 = 表达式 复制代码
举个栗子🌰
var name string = "张三" var age int = 18 var isOk bool = true 复制代码
或者还可以这样
var name, age, isOk = "张三", 12, true 复制代码
类型推导
有时候可以选择性省略变量的类型,编译器会根据等号右边的值来推导变量的类型来完成初始化。
举个栗子🌰
var name = "张三" var age = 12 var isOk = true 复制代码
短变量声明
在函数内部
,还可以用:=
方式声明并完成变量的初始化。
举个栗子🌰
package main import "fmt" var m = 100 // 全局声明变量m func main() { n := 10 m := 200 // 局部声明变量m fmt.Println(m,n) } 复制代码
匿名变量
多重赋值,忽略某个值,匿名变量用_
表示。匿名变量不会占用命名空间,不会分配内存。
举个栗子🌰
func foo() (int, string) { return 12, "张三" } func main() { age,_ := foo() _,name := foo() fmt.Println(name, age) } 复制代码
注意事项
- 函数外的每个语句都必须以关键字开始(var、const、func等)
- := 不能在函数外使用
- _多用于占位,表示忽略
常量
Go中的常量与JavaScript中对常量的定义相似,指的是恒定不变的值,都是用const
声明,有一点不同是,Go中的常量在定义的时候必须赋值。
const HOME_ADDRESS = "我滴家在东北~松花江上昂昂" const MY_GENDER = "男" 复制代码
多个常量可以一起声明:
const ( HOME_ADDRESS = "我滴家在东北~松花江上昂昂" MY_GENDER = "男" ) 复制代码
const同时声明常量的时候,如果省略了值,则表示和上面一行的值相同:
const ( HOME_ADDRESS = "我滴家在东北~松花江上昂昂" HIS_HOME_ADDRESS MY_GENDER = "男" HIS_GENDER ) 复制代码
HIS_HOME_ADDRESS
值为 我滴家在东北~松花江上昂昂
,HIS_GENDER
值为 男
iota
常量计数器,JavaScript中没有。
iota
只能在常量的表达式中使用,变量不行的!
iota
在const关键字出现的时候立马被重置为0。const中每新增一行常量声明,将会使iota
计数一次(可理解为0~正无穷 const语句块中的索引)。
举个栗子🌰
const ( n1 = iota n2 n3 n4 n5 ) fmt.Println(n1,n2,n3,n4,n5) // 0 1 2 3 4 复制代码
常见的iota示例
使用_
跳过某些值
const ( n1 = iota _ n2 _ n3 n4 ) fmt.Println(n1,n3,n4,n5) // 0 2 4 5 复制代码
iota
声明中 插队操作
const ( n1 = iota //0 n2 = 1000 // 1000 n3 n4 = 9999 // 9999 n5 = iota // 4 n6 // 5 ) const n7 = iota fmt.Println(n1,n2,n3,n4,n5,n6,n7) 复制代码
基于李文周的示例改造了一点儿,结构发现n3
算错了(以为是iota的值,也就是索引),n3
在const声明块中因为没有被赋值,所以和他上面的值相同,也就是1000,直到n5
重新赋值iota
,n6
没有赋值,等于n5
,也就是等于iota
,iota
自增为6,n7
重新遇到const关键字,iota
重置为0
。
数据类型
整型
浮点型
复数
布尔值
字符串
字符串转义
转义符 | 含义 |
\r | 回车符(返回首行) |
\b | 换行符(直接调到下一行的同列位置) |
\t | 制表符 |
' | 单引号 |
" | 双引号 |
\ | 反斜杠 |
举个栗子🌰
package main import "fmt" func main() { fmt.Println("str := \"c:\\code\\day01\\main.go") } 复制代码
多行字符串
定义多行字符串的时候,用反引号
s1 := `第一行 第二行 第三行 ` 复制代码
字符串常用操作
方法 | 介绍 |
len(str) | 求长度 |
+ | 拼接字符串 |
strings.Split | 分割 |
strings.Contains | 是否包含 |
strings.HasPrefix, strings.HasSuffix | 前缀,后缀判断 |
strings.Index(), strings.LastIndex | 子串出现的位置,下标,索引 |
strings.join | join操作 |
举个栗子🌰
// ----------- len ------------ s1 := "abcd" fmt.Println(len(s1)) // 4 // ----------- + ------------ s1 := "ab" s2 := "cd" s3 := s1 + s2 fmt.Println(s3) // abcd // ----------- strings.Split ------------ 参数1:待分割的字符串 参数2:分割符 返回值:返回一个字符串切片 s1 := "u&me&she&he" s1_slice := strings.split(s1, "&") fmt.Println("result:", s1_slice) // [u me she he] fmt.Println("len:", len(s1_slice)) // 4 fmt.Println("cap:", cap(s1_slice)) // 4 注意事项: 1. 字符串中不包含分隔符时,将原字符串转换成一个len&cap为1的字符串切片。 // [ u&me&she&he ] len:1 cap:1 2. 当分隔符是空字符串时,将每个字符切割成一个单独的元素。//[u & m e & s h e & h e] len:1 cap:1 // ----------- strings.Contains ------------ s1 := "u&me&he&she" s2 := "me" s3 := "haha" fmt.Println(strings.Contains(s1, s2)) // true fmt.Println(strings.Contains(s1, s3)) // false // ----------- strings.HasPrefix, HasSuffix ------------ 参数1:待检测的字符串 参数2:指定的前缀/后缀 返回值:返回一个布尔值 s1 := "goodday.png" fmt.Println(strings.HasPrefix(s1, "goodday")) // true fmt.Println(strings.HasPrefix(s1, "goodday")) // true fmt.Println(strings.HasSuffix(s1, "png")) // true fmt.Println(strings.HasSuffix(s1, "jpg")) // false // ----------- strings.Index, LastIndex ------------ s1 := "abcd" fmt.Println(strings.Index(s1, "a")) // 0 fmt.Println(strings.Index(s1, "b")) // 3 fmt.Println(strings.Index(s1, "e")) // -1 // ----------- strings.Join ------------ s := []string{"11", "12", "13"} res := strings.Join(s, "|") fmt.Println(res) // 11|12|13 复制代码
byte和rune类型
Go语言的字符有两种:
byte
,代表ASCII码
的一个字符rune
,代表一个UTF-8
字符 处理复合字符时,需要用rune
类型
Go语言的字符串底层是一个byte数组,中文字符在utf-8下占3个字节,Go的默认编码是utf-8。
s := "hello啊!树哥" for i := 0; i < len(s); i++ { fmt.Println("%v(%c)", s[i], s[i]) } fmt.Println() for _, r := range s{ fmt.Printf("%v(%c)", r, r) } 复制代码
获取字符串的长度
如果想拿到一个字符串的真实长度,可以通过一下两种办法:
fmt.Println(len([]rune(s))) //9 fmt.Println(len(utf8.RuneCountInString(s))) // 9 复制代码
修改字符串
要修改字符串,首先将其转化为rune
或者byte
,修改完成之后再转换成string
,无论哪种转换,都会重新分配内存,并复制字节数组。
s1 := "helloa!shugge!" byteS1 := []byte(s1) byteS1[0] = 'w' fmt.Println(string(byteS1)) // welloa!shugge! s2 := "hello啊!树哥!" runeS2 := []rune(s2) runeS2[7] = '涛' fmt.Println(string(runeS2)) // hello啊!涛哥! 复制代码
字符串的操作这里与JavaScript大相径庭,在JavaScript字符串的修改是可以任意修改的,而且单个字符可以用''
也可以用""
,也没有对不同字符类型的特殊处理这一步。
关于字符编码
程序中所有的信息都是通过二进制的形式存储在计算机底层的,将字符转换成二进制的过程叫做编码
,将二进制转化成字符的过程叫做解码
,字符集
相当于一个枚举映射表。
ASCII码
:早期计算机应用在欧洲等发达国家,所以这个字符集只存在拉丁字母和阿拉伯数字。非ASCII码
, 因为ASCII码不能完整表示其他国家语言,所以各个国家都基于ASCII码做了拓展,比如我国的GB 类
的字符集GB2312
,GBK
,但是问题出现了,国与国之间谁也不懂谁的编码,于是有了Unicode
Unicode
:延伸出了UTF-32
、UTF-16
、UTF-8
类型转换
Go语言中只有强制类型转换,没有隐式类型转换。
强制类型转换的基本语法如下:
T(表达式) 复制代码
T
代表的要转换的类型。表达式包括变量、复杂算子和函数返回值等等。
数据类型练习题
- 编写代码分别定义一个整型、浮点型、布尔型、字符串型变量,使用
fmt.Printf()
搭配%T
分别打印出上述变量的值和类型。
s := "hello树先生" n := 12 b := true f := 3.1415 fmt.Printf("%v:%T", n,n) fmt.Println() fmt.Printf("%v:%T", b,b) fmt.Println() fmt.Printf("%v:%T", f,f) fmt.Println() fmt.Printf("%v:%T", s,s) 还有两种获取变量和类型的方法 1. reflect.Typeof(s) 2. reflect.Typeof(s).Name() 关于Printf 1.%T 打印变量的类型 2.%v 以默认的方式打印变量的值 复制代码
- 编写代码统计出字符串
"hello!树先生!"
中汉字的数量。
// 我的解法,因为Go默认使用 UTF-8解码,所以一个中文字符是3 str := "hello!树先生!" runeStr := []rune(str) count := 0 for _, r := range runeStr { if len(string(r)) >= 3 { count++ } } fmt.Printf("str中汉字有%v个", count) // 一个好的解法 // unicode 包提供了一些测试Unicode码点属性的数据和函数. // Is 返回该符文是否在指定范围的表中。 // Han 汉字的表范围 str := "hello!树先生!" count := 0 for _,item := range str { if unicode.Is(unicode.Han, item) { count++ } } fmt.Printf("str中汉字有%v个", count)