Golang学习一:基础语法

483 阅读24分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

打印输出

  • 在程序中输出使用频率很高,使用输出语句可以在程序执行过程中把一些结果输出到控制台中,程序员通过控制台中输出结果判断是否符合预期
  • 在Go语言中有多种输出方式,不同的输出适用场景不同.归纳起来三种,每种还分为3种方式(原内容,原内容+ln,原内容+f)
    • PrintXX()
    • FprintXX()
    • SprintXX()

FprintXX

  • FprintXX在Go Web中使用比较多,把内容写到响应流中.
  • 以Fprintln()举例,源码如下
// Fprintln formats using the default formats for its operands and writes to w.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes  written and any write error encountered.
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
	p := newPrinter()
	p.doPrintln(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
  • 函数参数中第一个参数是输出流,后面参数是内容,表示把内容写入到输出流中
  • 第一个返回值表示输出内容长度(字节数),第二个返回值表示错误,如果没有错误取值nil
    • Fprintln()输出后会添加换行符,所以长度比内容多1个
    • Fprintln()源码中p.doPrintln(a)的源码
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *pp) doPrintln(a []interface{}) {
	for argNum, arg := range a {
		if argNum > 0 {
			p.buf.WriteByte(' ')
		}
		p.printArg(arg, 'v')
	}
	p.buf.WriteByte('\n')//此处多添加了换行
}
  • FprintXX()支持下面三种方式
    • os.Stdout 表示控制台输出流
func main() {
	fmt.Fprint(os.Stdout, "内容1")//向流中写入内容,多个内容之间没有空格
	fmt.Fprintln(os.Stdout, "内容2")//向流中写入内容后额外写入换行符,多个内容之间空格分割
	fmt.Fprintf(os.Stdout, "%s", "内容3")//根据verb格式向流中写入内容
}

PrintXX

  • PrintXX支持下面三种方式
func main() {
	fmt.Println("内容","内容")//输出内容后换行
	fmt.Print("内容","内容")//输出内容后不换行
	fmt.Printf("verb","内容")//根据verb输出指定格式内容
}
  • 以Println()举例,源码如下
// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}
  • 可以看出Println()底层实际是Fprintln(),返回值依然是内容长度和错误信息

SPrintXX

  • 以Sprintln()举例,和Println()主要的区别是:
    • Sprintln()把形成结果以字符串返回,并没有打印到控制台
    • Println()把结果打印到控制台,返回内容长度和错误
  • 所以从严格意义角度讲SprintXX不是打印输出,而更像字符串转换
  • 源码如下
// Sprintln formats using the default formats for its operands and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func Sprintln(a ...interface{}) string {
	p := newPrinter()
	p.doPrintln(a)
	s := string(p.buf)
	p.free()
	return s
}
  • 依然支持三种写法
func main() {
	fmt.Sprint("内容1", "内容12")
	fmt.Sprintln("内容2")
	fmt.Sprintf("%s", "内容3")
}

转义字符

  • 在Go语言中可以使用%+特定字母/+特定字母形成转义字符,这个表示特定含义的组合称为转义字符.

  • 在Go语言中又称为verb

  • 转义字符在Go语言中在以下两种情况使用频率较高

fmt.Printf("verb",内容)//输出
fmt.Scanf("verb",接收变量)//输入

进制简述

  • 进制数在编程中经常遇见,进制数分为二进制,八进制,十进制和十六进制.其中十进制就是平时我们接触最多的
  • X进制数都满足:
    • 满X进一位
    • 每位最大为X-1
    • 第N位代表的X的(N-1)次方
  • 其中十六进制中a=10,b=11,c=12,d=13,e=14,f=15
  • 举例
十进制 18
二进制 10010
八进制 22
十六进制 12

常用转义字符汇总

  • 以下为常用转义字符
verb含义
%d十进制整数
%x,%X大小写方式显示十六进制整数
%o八进制整数
%b二进制整数
%f,%g,%e浮点数
%t布尔值
%c字符
%s字符串
%q带双引号字符串
%v内置格式内容
%T类型
%p内存地址
%%字符%
\n换行
\t缩进

用户输入简介

  • 用户输入在平时练习中使用频率较高.
  • 程序运行时,运行到接收用户输入语句,程序阻塞,用户在控制台输入内容后,把内容赋值给对应的变量,程序继续运行.
  • 在Go语言中有多种接收用户输入的方式,其中使用fmt包的Scan函数使用最简单

接收用户输入的几种方式

  • 使用Scanln(&变量名,&变量名)的方式接收.
    • 输入的内容必须都在同一行
    • 每个内容之间使用空格分割
    • 回车换行后表示停止输入.
    • 如果希望接收3个值,而在控制台只输入2个值,回车后也停止接收
    • 如果希望接收2个值,而在控制台输入3个,回车后只能接收两个值
package main

import "fmt"

func main() {
	var name, age string //声明两个字符串变量,变量在本章节后面讲解
	fmt.Print("请输入姓名和姓名:")
	fmt.Scanln(&name, &age) //此处&变量名是地址.指针地址在后面章节境界
	fmt.Println("接收到内容为:", name, age)
}
  • 也可以使用fmt.Scanf(verb,&变量)按照特定的格式进行输入.
    • 下面例子演示的每次换行输入一个内容
package main

import "fmt"

func main() {
	var a,b string
	fmt.Scanf("%s\n%s",&a,&b);
	fmt.Printf("%s\n%s",a,b)
}
  • 需要注意,如果同行输入两个字符串,中间使用空格,否则编译器无法对输入内容拆分
package main

import "fmt"

func main() {
	var a string
	var b string
	//输入时必须输入: aaa bbb
	//如果中间没有空格则把所有内容都赋值给了a
	fmt.Scanf("%s%s",&a,&b)
	fmt.Println(a,b)
}

静态语言和动态语言

  • 静态类型语言和强类型语言是同一个意思,表示在编译期就确定了类型,而且类型是不可以改变的
  • 动态类型语言和弱类型语言是同一个意思,表示在运行过程中才能确定,且内容是什么类型变量类型跟随变化

Go语言变量简介

  • Go语言和其他语言一样都有基本存储容器.可以存储一个或多个值在程序中,方便程序中多次使用容器中内容,这个容器称为:变量
  • Go语言虽然是静态类型语言,但是支持动态类型语言语法,因为Go语言希望程序员少声明变量,增加GC效率

变量命名规则

  • 以字母或下划线开头(Go语言中多不以_开头)

  • 后面可以是任意数量的字符、数字和下划线

  • 区分大小写

  • 不能是关键字(关键字具备特定含义),下面是Go语言的关键字

    关键字如下
    breakdefaultfuncinterfaceselect
    casedefergomapstruct
    chanelsegotopackageswitch
    constfallthroughifrangetype
    continueforimportreturnvar
  • 可以是保留字,但是建议不使用保留字做为变量名,下面是Go语言的保留字

    保留字如下
    truefalseiotanilint
    int8int16int32int64unit
    unit8unit16unit32unitptrfloat32
    float64complex128complex64boolbyte
    runestringerrormakelen
    capnewappendcopyclose
    deletecomplexrealimagpanic
    recover
  • 在同一范围内不允许出现同名变量

  • Go语言要求变量声明后至少使用一次(赋值不属于使用)

单个变量声明及赋值

  • 先声明后赋值(声明后开辟内存,不同类型变量都有不同初值)
//语法:
//1. 声明
var 变量名 类型
//2. 赋值
变量名=值

//示例:
var smallming string
smallming = "英文名"
  • 声明并赋值(此方式不建议)
//语法:
var 变量名 类型 = 值

//示例
var smallming string = "英文名"
  • 声明并赋值(省略类型,变量类型取决于值的类型)
//语法:
var 变量名 = 值

//示例:
var smallming = "英文名"
  • 短变量(只能在函数内使用)
//语法:
变量名 := 值

//示例:
smallming := "英文名"

声明多个变量和赋值

  • 先声明后赋值
func main() {
	var a, b, c int
	a, b, c = 1, 2, 3
	fmt.Println(a, b, c)
}
  • 声明时赋值
func main() {
	var a, b, c, d = 1, 2, 3, false
	fmt.Println(a, b, c, d)
}
  • 声明并赋值,推荐方式
func main() {
	var (
		a = 1
		b = true
		c = "测试"
	)
	fmt.Println(a, b, c)
}
  • 使用短变量给多个变量赋值时,必须要保证至少有个变量是没有声明的
func main() {
	var (
		a = 1
		b = true
		c = "测试"
	)
	//短变量操作多个值时只要保证里面至少有一个新变量
	b, c, d := false, "smallming", 3
	fmt.Println(a, b, c, d)
}

整型

整型概述

  • 在Go语言中可以进行数学运算的类型分为整型和浮点型
  • 所有的整数数字存储到整型中就可以进行数学运算
    • 整型和整型运算的结果还是整型.(5/2=2)
    • 都是整型,但是int8和int16之间不可以进行运算,必须进行类型转换
  • 整型分为有符号整型和无符号整型
    • 有符号整型有正数和负数.其二进制最高位表示符号,0为正数1为负数.int和intx为有符号整型
//int8 举例
0000 0010=2
1000 0010=-2
  • 无符号整型只能取大于等于0的整数.其二进制最高位表示真实数字.unit和unitx为无符号整型
//uint8 举例
0000 0010=2
1000 0010=130
  • 整型取值范围和作用
    • 有符号整数统一公式为:-2的n-1次幂到2的n-1次幂减一
    • 无符号整数统一公式为:0到2的n次幂减一 | 类型 | 取值范围 | | ------: | :----------------------------------------------------------- | | int8 | [-128 , 127] | | int16 | [-32768 , 32767] | | int32 | [-2147483648 , 2147483647] Go语言中没有字符类型,所有字符都使用int32存储 | | int64 | [-9223372036854775808 , 9223372036854775807] | | int | 受限于计算机系统,系统是多少位,int为多少位 | | uint8 | [0 , 255] | | uint16 | [0 , 65535] | | uint32 | [0 , 4294967295] | | uint64 | [0 , 18446744073709551615] | | uint | 受限于计算机系统,系统是多少位,uint为多少位 | | rune | 与int32类似,常用在获取值的Unicode码 | | byte | 与uint8类似.强调值为原始数据.一个字节占用8个二进制 | | uintptr | 大小不确定,类型取决于底层编程 |

类型转换

  • Go语言是静态类型语言,并且不具备低精度向高精度自动转换功能,所以不同类型变量之间相互赋值需要进行类型转换.
  • 例如:
func main() {
	//声明3个类型变量
	var a int = 1
	var b int32 = 2
	var c int64 = 3
	fmt.Println(a, b, c)

	//把int32转换为int64
	a = int(b)
	fmt.Println(a, b)
	a = 1
	
	//把int64转换成int32
	b = int32(c)
	fmt.Println(b, c)
	b = 2
	
	//把int转换为int64
	c = int64(a)
	fmt.Println(a, c)
	c = 3
}

不同进制整数

  • 支持八进制,十进制,十六进制数字创建整型,最后由系统转换为十进制
  • 不支持二进制值
func main() {
	//默认表示十进制
	d := 17
	
	//0开头表示八进制
	o := 021
	
	//0x开头表示十六进制
	x := 0x11
	
	//e2表示10的2次方
	e := 11e2
	
	//输出
	fmt.Println(d, o, x, e)
	
	//把变量d中内容转换为二进制
	b := fmt.Sprintf("%b", d)
	fmt.Println(b)
}

字符

字符型概述

  • 字符型存放单个字母或单个文字
  • Go语言不支持字符类型,在Go语言中所有字符值都转换为对应的编码表中int32值
  • Go语言默认使用UTF-8编码

编码发展历史

  • 最开始使用8个可开合晶体管表示世界万物,这就是字节的由来.后来生产出可以控制字节的机器,出现了计算机
  • 把字节中32以下情况称为控制码,后在控制码基础上添加空格、标点符号、数字、大小写字母等直到127,最终形成了ANSI的ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码),这时已经较好的支持英文了
  • 随机计算机在全球的应用,在原ASCII基础上添加了新的字母、符号、横线竖线等直到255号,从128到255被称为扩展字符集但是此时计算机还是在只支持英文
  • 等中国开始使用计算机,为了支持中文,保留了前127号,称这些为半角,把后面的内容以两个字节拼接形成中文汉字和表单符号,这后半部分就是全角,其中汉字6763个满足中文大部分情况的要求,称这个编码为GB2312
  • 随着计算机在中国的普及,使用GB2312时有的汉字无法打出,又在GB2312基础上添加了近20000个汉字(包括繁体和符号等)形成GBK,后来为支持少数民族又在GBK基础上推出GB18080
  • 全球中每个国家都有着自己的一套编码,ISO国际为了统一标准,重新定义一套编码,希望包含全球所有的文字和符号,俗称Unicode
  • 随着互联网的快速发展,为解决Unicode网络传输中流浪浪费问题,出现了UTF编码,有UTF-8(8位传输)和UTF-16(16为传输)两种.其中UTF-8使用最多,为变长编码方式,中文占3个自己.

字符示例

  • 示例
func main() {
	//定义数字
	var i rune=0x5F20
	fmt.Println(i)

	//输出汉字张
	fmt.Printf("%c\n",i)
	
	//获取转换后的内容
	c:=fmt.Sprintf("%c",i)
	fmt.Println(c)
}
  • 也可以使用单引号表示一个字符类型,但是本质还是正数
func main() {
	c := '张'
	fmt.Println(c)      //24352
	fmt.Printf("%T", c) //int32
}

浮点型

浮点数概述

  • 浮点类型用于存储带有小数点的数字
  • 一个整数数值可以赋值给浮点类型但是一个整型变量不可以赋值给浮点类型
  • 浮点数进行运算的结果是浮点数
  • Go语言中浮点类型有两个
    • float32
    • float64

浮点数取值范围

  • float32和float64取值范围 | 类型 | 取值范围 | | :-----: | :--------------------------------------------: | | float32 | 3.40282346638528859811704183484516925440e+38 | | float64 | 1.797693134862315708145274237317043567981e+308 |

  • 可以通过math或中常量快速获取浮点数的最大值

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.MaxFloat32)
	fmt.Println(math.MaxFloat64)
}

浮点运算

  • float32和float64之间不可以相互运算,需要进行类型转换
func main() {
	var a float32 = 1.5
	var b float64 = 3.3         //默认是float64类型
	fmt.Println(a + float32(b)) //float64向float32转换
	fmt.Println(float64(a) + b) //float32向float64转换
}
  • 建议使用float64,虽然占用空间多,但是float32在累计运算时可能出现误差
  • 整型运算和浮点型运算结果类型为本身类型
func main() {
	var a, b int = 3, 2
	var c, d float64 = 3, 2
	fmt.Println(a / b) //结果为int,舍去小数部分(向下取整)
	fmt.Println(c / d) //结果为float64
}

布尔类型

布尔类型介绍

  • 布尔类型关键字bool
  • 布尔类型可取值只有两个
    • true :代表真,表示成立,二进制表示时1表示真
    • false:代表假,表示不成立,二进制表示时0表示假
  • 布尔类型不能与其他类型相互转换
  • 布尔类型占用1个byte
  • 布尔类型单独使用较少,多用在判断中

布尔类型代码示例

  • 创建bool类型变量
func main() {
	var a bool = true
	var b bool = false
	var c = true
	d := false
	fmt.Println(a, b, c, d)
}
  • 使用unsafe包下的Sizeof()可以查看类型占用字节
func main() {
	a := false
	fmt.Println(unsafe.Sizeof(a))
}
  • 虽然bool类型占用一个byte,但是bool不能和byte或int8相互转换
func main() {
	var a int8 = 1
	var b byte = 0
	var c bool = false
	fmt.Println(a, b, c)
	a = int8(c) //cannot convert c (type bool) to type int8
	b = byte(c) //cannot convert c (type bool) to type byte
	c = bool(a) //cannot convert a (type int8) to type bool
	c = bool(b) //cannot convert b (type byte) to type bool
	b = byte(a) //可以
}
  • 布尔类型除了直接赋值true或false以外,还是可以表达式赋值,借助比较运算符、逻辑运算符等
func main() {
	a := 5 > 3
	fmt.Println(a)      //输出:true
	fmt.Printf("%T", a) //输出:bool
}

运算符

运算符概述

  • Go语言支持运算符,算数运算符、比较运算符、逻辑运算符、赋值运算符、关系运算符、位运算符等
  • 不同的运算符使用场景不同,运算符一起使用时还需要注意优先级问题

算数运算符

  • 算数运算符对浮点类型和整型进行数学运算
  • 乘法、除法优先级高于取余,取余高于加法、减法 | 运算符 | 含义 | | -----: | :----------------------------- | | + | 加法 | | - | 减法 | | * | 乘法 | | / | 除法 | | % | 取余. 例如 12%5=2 8%4=0 | | ++ | 自增一,只能单独使用,没有返回值 | | -- | 自减一,和++语法相同 |

比较运算符

  • 比较运算符用于比较两个内容的大小,判断和分支中使用频率较高 | 运算符 | 含义 | | -----: | :------------------------------------- | | == | 是否等于,返回值为bool类型,判断是否成立 | | != | 不等于 | | > | 大于 | | >= | 大于等于 | | < | 小于 | | <= | 小于等于 |

逻辑运算符

  • 逻辑运算符在判断和分支中使用频率较高
    运算符含义
    !逻辑非,取结果的反
    &&逻辑与,只有两侧同时为真时总体结果为真
    ||逻辑或,两侧只要有一个为真时整体为真

赋值运算符

  • 赋值运算符目的给变量赋值
  • 提供了很多运算的简单方式 | 运算符 | 含义 | | -----: | :----------------------------------------- | | = | 右侧内容赋值给左侧变量.例如:A=B 把B赋值给A | | += | A+=B 相当于 A=A+B | | -+ | A-=B 相当于 A=A-B | | *= | A *= B相当于 A等于A乘以B | | /= | A/=B 相当于A=A/B | | %= | A%=B 相当于 A=A%B | | <<= | A<<=B 相当于 A=A<<B | | >>= | A>>=B 相当于 A=A>>B | | &= | A&=B 相当于 A=A&B | | |= | A|=B 相当于 A=A|B | | ^= | A^=B 相当于 A=A^B |

位运算符

原码、反码、补码

  • 位运算时,需要知道几个概念,原码、反码、补码.正数在计算机中二进制还是原值,负数在计算机中存储的是补码

  • 原码

    • 正数的原码还是正数
    • 负数的原码是负数绝对值的二进制表示
    • 以8位二进制方式表示
  //原码为:0000 0011
  var a int8=3
  //原码为:0000 0010
  var b int8=-2
  • 反码
    • 正数的反码还是正数
    • 负数的反码是所有位取反
  //反码为:0000 0011
  var a int8=3
  //反码为:1111 1101
  var b int8=-2
  • 补码
    • 正数的补码还是正数
    • 负数的补码为负数的反码加一 . 负数的补码就是负数在计算机中二进制表示方式
    • 想要知道负数二进制对应十进制数字就是补码-->反码-->原码-->添加负号
    • 如果是16位只需要在负数前面添加8个1 ,正数前面添加8个零
  //补码为:0000 0011
  var a int8=3
  //补码为:1111 1110
  var b int8=-2

Go语言中位运算符

  • 位运算在算法中使用频率较高
  • 区块链学习中密码学部分的很多算法都是使用的位运算
运算符含义
<<二进制左移
>>二进制右移
|位运算或OR,有1取1"
&位运算与AND.转换二进制,都为1取1
位运算异或XOR,二阶运算相同为0不同为1.一阶运算加一后正数交互(正数变负数,负数变正数)
&^位清空(AND NOT),如果右侧0左侧不变,如果右侧为1,左侧一定清零.a&^b=a&(^b)

字符串

字符串概述

  • 字符串是一段不可变的字符序列.内容是任意内容,可以是一段文字也可以是一串数字,但是字符串类型数字不能进行数学运算,必须转换成整型或浮点型
  • 字符串类型关键字:string
  • 创建字符串类型变量
var s string = "smallming"
s1 := "smallming"
  • 字符串类型的值使用双引号""扩上,内容支持转义字符串.两侧使用反单引号时原格式输出
func main() {
	a := "a\tbc"
	b := `a\tbc`
	fmt.Println(a) //输出:a	abc
	fmt.Println(b) //输出a\tabc
}

字符串和数值转换

  • 包strconv提供了字符串和其他类型相互转换的函数,下面以字符串和数值类型转换为例
  • int和string相互转换
package main

import (
	"fmt"
	"strconv"
)

func main() {
	s := "11"
	/*
	第一个参数:需要转换的字符串变量
	第二个参数:这个数字是几进制,常用取值:2,8,10,16
	第三个参数:认为这个数字的整数类型.可取值:0,8,16,32,64.
	但是由于方法最后返回值是int64,所以第三个参数无论设置什么最终结果都是int64
	*/
	i, _ := strconv.ParseInt(s, 10, 8)
	fmt.Println(i)
	fmt.Printf("%T\n", i)

	//简单写法,相当于strconv.ParseInt(s,10,64)
	i1, _ := strconv.Atoi(s)
	fmt.Println(i1)
}
  • Int转换成string
package main

import (
	"fmt"
	"strconv"
)

func main() {
	i := 11
	/*
	第一个参数:必须是int64类型
	第二个参数:进制数
	 */
	s := strconv.FormatInt(int64(i), 10)
	fmt.Println(s)        //输出:11
	fmt.Printf("%T\n", s) //输出:string

	/*
	由于平时常用int,且使用短变量时整数默认是int类型
	所以下面方式较常用,把int转换为string
	 */
	s1 := strconv.Itoa(i)
	fmt.Println(s1)      //输出:11
	fmt.Printf("%T", s1) //输出:string
}
  • string转换为floatXX类型
package main

import (
	"fmt"
	"strconv"
)

func main() {
	s := "1.5"
	/*
	把字符串转换为指定类型
	第一个参数:字符串
	第二个参数:可取值为32和64,分别表示float32和float64
	返回值是float64
	 */
	f, _ := strconv.ParseFloat(s, 64)
	fmt.Println(f)
	fmt.Printf("%T", f)
}
  • floatXX转换为string类型
package main

import (
	"fmt"
	"strconv"
)

func main() {
	f := 1.5
	/*
	把浮点型转换为字符串类型
	第一个参数:浮点型变量
	第二个参数:
		'f'(-ddd.dddd)
		'b'(-ddddp±ddd,指数为二进制)
		'e'(-d.dddde±dd,十进制指数)
		'E'(-d.ddddE±dd,十进制指数)
		'g'(指数很大时用'e'格式,否则'f'格式)
		'G'(指数很大时用'E'格式,否则'f'格式)
	第三个参数:小数点精度,精度不够使用0补全,超出精度四舍五入
	第四个参数:浮点型变量类型,64表示float64,32表示float32
	 */
	s := strconv.FormatFloat(f, 'g', 5, 64)

	fmt.Println(s)
}

字符串截取

  • 可以使用**len(字符串变量)**获取字符串的字节长度,其中英文占1个字节长度,中文占用3个字节长度
  • 可以使用**变量名[n]**获取到字符串第n+1个字节,返回这个字节对应的Unicode码值(uint8类型).注意n的取值范围是[0,长度)
func main() {
	s := "smallming张"
	a := s[0]
	fmt.Println(a)        //输出:115
	fmt.Printf("%T\n", a) //输出uint8
	b := fmt.Sprintf("%c", a)
	fmt.Printf("%T\n", b) //输出:string
	fmt.Println(b)        //输出s
}
  • 可以使用变量名[n:m]取出大于等于n小于m的字符序列
    • n和m都可以省略,省略时认为n为0,m为长度
    • 因为中文占用三个字节,如果没有把中文完整取出,会出现乱码
func main() {
	s := "smallming张"
	fmt.Println(len(s)) //输出:12,字节长度
	fmt.Println(s[1:4]) //输出:mal
	fmt.Println(s[:2])  //输出:sm
	fmt.Println(s[5:])  //输出:ming张
}
  • 可以通过把字符串转换为切片获取长度,并获取里面内容. 也可以直接使用for循环结合range获取
func main() {
	s := "smallming张"
	s1 := []rune(s)
	fmt.Println(len(s1))    //输出:10
	fmt.Println(s1[9])      //输出24352
	fmt.Printf("%c", s1[9]) //输出:张

	//遍历字符串中内容
	for i, n := range s {
		fmt.Println(i, n)
	}
}

常用函数

  • 在strings包中提供了字符串常用的函数
  • 常用函数整理如下
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "smallming"
	//第一次出现的索引
	fmt.Println(strings.Index(s, "l"))
	//最后一次出现的索引
	fmt.Println(strings.LastIndex(s, "l"))
	//是否以指定内容开头
	fmt.Println(strings.HasPrefix(s, "small"))
	//是否以指定内容结尾
	fmt.Println(strings.HasSuffix(s, "ming"))
	//是否包含指定字符串
	fmt.Println(strings.Contains(s, "mi"))
	//全变小写
	fmt.Println(strings.ToLower(s))
	//全大写
	fmt.Println(strings.ToUpper(s))
	//把字符串中前n个old子字符串替换成new字符串,如果n小于0表示全部替换.
	//如果n大于old个数也表示全部替换
	fmt.Println(strings.Replace(s, "m", "k", -1))
	//把字符串重复count遍
	fmt.Println(strings.Repeat(s, 2))
	//去掉字符串前后指定字符
	fmt.Println(strings.Trim(s, " ")) //去空格可以使用strings.TrimSpace(s)
	//根据指定字符把字符串拆分成切片
	fmt.Println(strings.Split(s, "m"))
	//使用指定分隔符把切片内容合并成字符串
	arr := []string{"small", "ming"}
	fmt.Println(strings.Join(arr, ""))
}

常量和iota

常量概述

  • 常量是一个固定值,在编译器就确定结果.声明时必须赋值且结果不可以改变.
  • 因为常量在编译器就确定,可以防止程序运行过程中意外修改
  • 常量关键字const
  • 常量定义完可以不使用
  • Go语言中常量定义没有明确语法要求,可以不全大写,驼峰即可.
  • 很多内容可以定义成常量
    • 人名
    • 圆周率
    • 电话号

常量定义

  • 定义常量时如果不是必须指定特定类型,可以省略类型,使用默认类型.且数值类型常量(不定义类型)可以直接进行运算
  • 常量的值可以是表达式,但是不允许出现变量
func main() {
	const a string = "smallming"
	const b = 123
	const c = 3*2 + 5//不要指定类型
	const d = 1.5//不要指定类型

	fmt.Printf("%T %T",c,d)//int float

	fmt.Println(c+d)//12.5

	//下面这种方式是错误的
	i := 3
	const e = i*2 + 5 //const initializer i * 2 + 5 is not a constant
}
  • 当定义多个常量时官方推荐的方式
	const (
		a = 1
		b = 2
		c = true
	)
  • 定义多常量时后一个常量如果没有赋值,与前一个常量值相同.
    • 第一个常量必须赋值
func main() {
	const (
		a = 1
		b
		c
	)
	fmt.Println(a,b,c)//输出:1 1 1
}

常量生成器

  • 当一组常量都是数值类型,可以使用常量生成器iota指定这组常量按照特定规则变化
  • iota起始值为0,每次增加1
func main() {
	const (
		a = iota
		b 
		c 
	)
	fmt.Println(a, b, c) //输出: 0 1 2

	const (
		d = iota << 1
		e 
		f 
	)
	fmt.Println(d, e, f) //输出:0 2 4
}
  • 无论是否使用iota,一组常量中每个的iota值是固定的,iota按照顺序自增1
  • 每组iota之间无影响
func main() {
	const (
		a = 5    //iota=0
		b = 3    //iota=1
		c = iota //iota=2
		d        //iota=3
	)
	fmt.Println(a, b, c, d) //输出5 3 2 3

	const (
		e = iota //iota=0
		f        //iota=1
		g = 10   //iota=2
		h        //iota=3
		i = iota //iota=4
		j        //iota=5
	)
	fmt.Println(e, f, g, h, i, j) // 0 1 10 10 4 5
}

指针

变量地址

  • 变量本质就是内存中一块数据的标记.把值存储到变量中实质是把值存储到内存中
  • 每次对变量重新赋值就是在修改变量地址中的内容
  • 在Go语言中可以通过 &+变量名 获取到变量地址值
  • 重新创建一个非引用型变量(即使是把已有变量直接赋值给新变量)也会新开辟内存地址.
func main() {
	a := 3
	fmt.Println(&a) //输出:地址
	a = 4
	fmt.Println(&a) //输出的地址不变

	b := a
	b = 5
	fmt.Println(&b, &a) //两个值不相同
	fmt.Println(b, a)   //输出:5 4
}

指针变量

  • 指针变量指向一个值的内存地址
  • 使用&+变量 返回值就是一个指针类型
  • 使用var 变量名 *类型 声明指针类型变量
  • 声明指针不会开辟内存地址,只是准备要指向内存某个空间,而声明变量会开辟内存地址,准备存放内容.所以指针类型变量都是把一个变量的地址赋值给指针变量
  • 使用*+指针能够获取内存地址中的值.所以*+指针就和直接使用变量是相同的
  • 应用指针可以实现多个地方操作同一个内存地址的值(在方法参数应用指针较多)
func main() {
	//创建变量
	a := 123
	var point *int
	point = &a //此时point和&a是相等的
	fmt.Println(point)
	*point = 3             //等价于a=3
	fmt.Println(*point, a) //*point和a是相当的
}

空指针

  • 指针目的就是指向内存中一块地址
  • 声明指针后指针不会指向任何内存地址,所以此时指针是空.在Go语言中空用nil表示
func main() {
	var a *int
	fmt.Println(a)        //输出:<nil>
	fmt.Println(a == nil) //输出true
}

new函数

  • 在上一小节中学习了指针,每次创建一个指针必须在额外创建一个变量,操作比较麻烦.
  • 可以通过new函数直接创建一个类型的指针
变量名:=new(Type)
  • 使用new函数创建的指针已有指向,可以使用*指针对象进行赋值.
func main() {
	a := new(int)
	fmt.Println(a) //输出:指针地址
	*a = 123
	fmt.Println(*a) //输出:123
}
  • 只声明的指针变量不能直接赋值.
func main() {
	var a *int
	*a = 123
	fmt.Println(*a)
}
  • 结果
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x48b576]

时间类型

时间类型概述

  • 时间类型在实际开发中使用频率很高,代表现实世界的时间
  • 在Go语言中时间类型不是关键字而是使用time包下Time结构体
  • 时间类型默认显示为UTC,所以经常需要把时间类型转换为字符串,显示成我们所熟悉的格式

Time的使用

  • 声明Time时,默认时间是无意义的
func main() {
	var t time.Time
	fmt.Println(t)//输出:0001-01-01 00:00:00 +0000 UTC
}
  • 可以通过time包下的Now()函数获取操作系统当前时间
    • +0800 CST 表示北京时间,东八区
	t := time.Now()
	fmt.Println(t) //输出:年-月-日 小时:分钟:秒.纳秒 +0800 CST m=+0.003012301
  • 也可以通过时间戳创建时间类型变量(距离1970年1月1日的纳秒差)
	/*
	1秒(s)=1000毫秒(ms)
	1秒(s)=1000000微秒(μs)
	1秒(s)=1000000000纳秒(ns)
	 */
	t := time.Now()
	t1 := time.Unix(0, t.UnixNano()) //根据时间戳创建时间.第二个值[0, 999999999]外合法
	fmt.Println(t.String())
	fmt.Println(t1)
  • 可以根据自己要求创建时间
	//time.Local取到本地时间位置对象,东八区
	t := time.Date(2020, 5, 6, 7, 8, 9, 0, time.Local)
	fmt.Println(t) //输出:2020-05-06 07:08:09 +0800 CST
  • 在time包下提供了大量的函数或方法获取时间的某一项
	t := time.Now()
	fmt.Println(t)
	fmt.Println(t.Year())       //年
	fmt.Println(int(t.Month())) //月
	fmt.Println(t.Day())        //日
	fmt.Println(t.Date())       //三个参数,分别是:年,月,日
	fmt.Println(t.Hour())       //小时
	fmt.Println(t.Minute())     //分钟
	fmt.Println(t.Second())     //秒
	fmt.Println(t.Clock())      //三个参数,分别是:小时,分钟,秒
	fmt.Println(t.Nanosecond()) //纳秒
	fmt.Println(t.Unix())       //秒差
	fmt.Println(t.UnixNano())   //纳秒差

时间和string相互转换

  • 时间转换为string
    • 参数必须是这个时间,为Go语言出现时间
	t := time.Now()
	//参数必须是这个时间,格式任意
	s := t.Format("2006-01-02 15:04:05", )
	fmt.Println(s)
  • string转换为Time
	s:="2022-02-04 22:02:04"
	t,err:=time.Parse("2006-01-02 15:04:05",s)
	fmt.Println(t,err)