目次
基础语法
语法框架
- package main
- import "fmt"
- func main() {
- fmt.Println("Hello Go!")
- }
复制代码 基本使用,首先学会打印,目前介绍两个即可- println("hello word")
- fmt.Println("hello word") //这个属于fmt包的,使用的时候需要引入import "fmt"
复制代码 数据范例
- 单引号:rune类型
- 双引号:string类型
- 数字型:int , uint uint32
- bool型:true , false
复制代码 范例转换
- //相同类型转换 ,比如整数类型,直接
- var int_num int = 111
- var f64_num float64 = float64(int_num)
- //不同类型的转换
- //借助"strconv"包
- //int转string
- str1 := strconv.Itoa(int_num)
- //以后遇到了再慢慢补充
复制代码 变量var定义
- //第一种,比较完整的定义与赋值
- var a int
- var a int = 123
- //简短声明,这种就是忽略了你var作为变量的意思还有去掉了类型,直接通过值来自动帮你搞定
- a := 123
- //多个变量声明,这个和python无二
- #正常声明
- var a,b int
- #正常声明加赋值
- var a,b int = 1,2
- #简短多变量
- a,b := 1,2
- //匿名变量
- #首先说明一下:在go中,如果你定义了变量不使用他会报错,所以匿名变量就出现了,让你定义暂存可以不用
- _,a := 1,2
复制代码 _下划线匿名变量,存储起来。
在go中,如果你定义了变量而不消就会报错,所以匿名变量存储是一个比较好的解决方式。- //比如当你使用for r循环的时候会有一个下标需要你接受,但是你又不想用他
- for _, value := range arr{
- fmt.Println(value)
- }
复制代码 常量
常量用const,且不受使用影响的,定义了可以不使用- const S int = 123 //常规定义常量
- const S = 123 //可以不用指定类型,会隐式指定合适的类型
- //定义多个常量,使用括号包裹
- const (
- a = 1
- s = "s"
- b = true
- )
复制代码 iota 枚举
- const (
- aas = iota //指定变量名等于iota后,往后的变量都不用给值,变量名随意,默认这里从0开始
- bbs
- ccs
- dds
- )
- fmt.Println("这里是iota的常量值:")
- fmt.Println(aas, bbs, ccs, dds)
复制代码
数组
在go中你会发现啥都反过来定义了,包括数组。- var arr = [len]int{元素1,元素2}
- 在平时是int arr[],在go中就比较恶心,需要时间适应一下
复制代码 初始化数组- #正常初始化,固定长度
- var arr = [2]int{1,2}
- #简短初始化,固定长度
- arr := [2]int{1,2}
- #自动计算长度
- arr := [...]int{1,2,3,4,5}
- len(arr) //计算数组长度
复制代码 遍历数组(就是正常编程语言都有的for,后面条件控制再细讲for)- for i := 0; i < len(arr); i++{
- fmt.Println(arr[i])
- }
- for index, value := range arr {
- fmt.Println(index,value)
- }
- //不想使用下标的话可以用_匿名变量接受就可以不用管他了。
- for _, value := range arr {
- fmt.Println(value)
- }
复制代码 数组中是不能使用append的,由于固定了长度。
切片
就是一个动态数组,可以对他进行空间上的操纵,雷同于c语言中new一个空间那样
所以切片解决了无法append添加的障碍,使用append必要用变量接受- var arr = []int{1, 2, 3, 4, 5, 6, 777, 999} //不加数组
- fmt.Println(arr)
- arr = append(arr,99999999)
- //注意:make的时候长度要小于等于容量,容量为了让你添加元素的时候大于容量了用来扩容的
- //在项目中给的容量要合适,否则大量扩容开销会很大。
- arr := make(数组类型, 长度, 容量)
- arr := make(数组类型,长度) //容量默认和长度一样
- cap(arr) //计算数组容量
复制代码
append- arr = append(list,1,2,3,4,5,6) //切片想要增加多少元素自行添加,没有个数限制,但是记得添加完成后保存返回的数组
复制代码 结构体
格式:- type 结构体名字 struct{
- 变量1 类型
- 变量2 类型
- }
复制代码 示例:- type Person struct{
- name string
- age int
- sex int
- }
- //结构体有点奇怪,和之前的普通类型赋值有点区别,人家int是这样的:var i int = 1,类型在变量名后面,等号前面
- //而结构体这里就不是,变量名后面不用类型了,直接等于号,然后才是结构体类型。
- var p = Person{name:"张三",age:18,sex:1}
- //打印结构体中的变量的时候可以使用.来指定
- fmt.Println(p.age)
复制代码
结构体方法
在golang中的特色,由于golang没有类,所以就不能类.方法名,所以就有告终构体方法这种来解决- func (var *Struct_Name) FuncName( var0, var1... ) return type { }
- (var *Struct_Name↑↑↑↑↑是接收器,每一个方法只能有一个接收器)
- 比如:
- type Person struct{
- xxx xxx
- }
- func (p *Person) toString() string{
- return p.xxx
- }
- 使用的时候就可以直接:
- p := Person{
- xxx:xxx,
- }
- fmt.Println(p.toString())
复制代码 指针
和其他语言差不多,按照c语言的解释- *指针类型
- &取地址
- var p *int //这个就是定义指针变量,如果不给值的话默认=nil,这是golang中的空值
- var num int = 123
- p = &num
- fmt.Println(p) //直接拿到的就是地址值
- fmt.Println(*p) //*不在定义的时候使用就是解引用,将地址的值取出来
复制代码
打印地址与地址值- p是一个指针
- var p *int = nil
- var n2 int = 10
- p = &n2
- println(p) //出来是地址
- fmt.Prinrtln(p) //出来也是地址
复制代码 fmt.Println打印地址与地址值对结构体打印的时候就有差别- type test struct {
- s1 int
- s2 string
- }
- func main(){
- st := &test{ //st简短赋值,st是指针来的,因为赋值的是给他结构体指针
- s1: 1000,
- s2: "ok",
- }
- println(st)
- fmt.Println(st)
-
- //打印结构体内的变量,指针依旧是用.号来指定(这与c语言有点不同,c是需要结构体指针指向的时候用->符号)
- fmt.Println(st.s1)
- }
- //go真挺奇怪的,结构体这里又不给你打印地址,给你加个&,但是一些int,string等等都是会直接地址出来的
复制代码
map
格式:- map[键的类型]值的类型
- 定义的时候正常定义就行
- var m = map[string]string
- m := map[string]string
复制代码 例子:(值的范例可以是很多)- var m = map[string]Person{
- "a":1, //,逗号一定要有,不管有多少个,一定要添加上逗号,否则会报错
- }
- fmt.Println(m)
- type Person struct{
- name string
- age int
- }
- m = map[string]Person
- fmt.Println(m)
复制代码 删除- m := map[string]int{
- "name":123
- "fff":222
- }
- delete(m, "name") //删除键为name的元素
复制代码 范例转换
在go中范例转换也是一样的操纵- var a float64 = 3.14
- var i int = int(a) //强制转成int类型,那就是去掉小数点(不作四舍五入)
复制代码 这里有个很好的例子- var s string = "hello"
- var b []byte = []byte(s)
- var s2 string = string(b)
复制代码 如果要把int转string的话不可以直接string(int_type)
他会把你这个数字当成ascii来转成字符的- func main() {
- fmt.Println("打印ascii,通过string转换为字符:")
- fmt.Println(string(65))
- fmt.Println(string(66))
- fmt.Println(string(67))
- }
复制代码
导入包
这里和python导入包差不多,同样可以命名别名- import "fmt" //导入单个
- import ( //导入多个
- "fmt"
- "math"
- )
- //给包起别名
- import f "fmt"
- //用的时候就可以不用fmt.Println了,可以f.Println使用你起的别名
复制代码 字符串strings包
字符拼接
反引号: Hello, World! ,⽤于多⾏字符串和包含特殊字符的字符串
意思是你用反引号就可以原封不动输入你反引号内的东西,包括tab键回车换行等等。
特别是处理一些http请求返来的数据或者xml格式的数据非常好用
Contains
匹配字符是否存在- strings.Contains(字符串1, "要匹配的字符串2") //如果匹配的字符串2存在字符串1中的话返回true,否则false
复制代码 Replace
- fmt.Println(strings.Replace("hello world", "world", "Go", 1)) //hello go
复制代码 ReplaceAll就是更换所有了
更多函数解释
- 函数 函数作⽤
- Contains 检查字符串是否包含⼦字符串
- ContainsAny 检查字符串是否包含任何⼀个给定字符
- ContainsRune 检查字符串是否包含指定的 rune 字符
- Count 计算⼦字符串在字符串中出现的次数
- EqualFold 忽略⼤⼩写⽐较两个字符串是否相等
- Fields 将字符串按空⽩字符分割成切⽚
- HasPrefix 检查字符串是否以指定前缀开头
- HasSuffix 检查字符串是否以指定后缀结尾
- Index 返回⼦字符串在字符串中第⼀次出现的位置
- IndexAny 返回字符串中任意⼀个字符第⼀次出现的位置
- IndexByte 返回指定字节在字符串中第⼀次出现的位置
- IndexRune 返回指定 rune 字符在字符串中第⼀次出现的位置
- Join 将字符串切⽚⽤指定分隔符连接成⼀个字符串
- LastIndex 返回⼦字符串在字符串中最后⼀次出现的位置
- LastIndexAny 返回字符串中任意⼀个字符最后⼀次出现的位置
- Repeat 重复字符串指定次数
- Replace 替换字符串中的⼦字符串
- ReplaceAll 替换字符串中的所有⼦字符串
- Split 将字符串按指定分隔符分割成切⽚
- SplitAfter 将字符串按指定分隔符分割成切⽚,保留分隔符
- SplitAfterN 将字符串按指定分隔符分割成切⽚,保留分隔符,最多分割 N 次
- SplitN 将字符串按指定分隔符分割成切⽚,最多分割 N 次
- Title 将字符串的每个单词的⾸字⺟⼤写
- ToLower 将字符串转换为⼩写
- ToUpper 将字符串转换为⼤写
- Trim 去除字符串两端的指定字符
- TrimSpace 去除字符串两端的空⽩字符
- TrimPrefix 去除字符串前缀
- TrimSuffix 去除字符串后缀
- NewReplacer 创建⼀个字符串替换器
复制代码 输入输出
字符串格式化
常见的格式化占位符- %v:值的默认格式表示
- %+v:类似%v,在结构体中会添加字段名
- %T:值的类型
- % %:百分号本身
- %t:单词true或false
- %b:整数以⼆进制方式显示
- %o:整数以八进制方式显示
- %d:整数以十进制方式显示
- %x:整数以十六进制方式显示
- %c:相应Unicode码点表示的字符
- %f:float类型输出,保留小数点几位可控,比如保留两位:%.2f
- %e:科学计数法,如-1234.456e+78
- %E:科学计数法,如-1234.456E+78
- %s:解析变量,直接打印值,这个打印字符串常用,在你读取byte的时候,若byte为字符串也可以直接帮你打印字符串值
- %q:打印值的时候加上双引号
复制代码 fmt:Scanf、Scan、Scanln
Scanf
接收输入内容并输出,这个必要加格式化符号接收,雷同C语言,与后续两个不一样,但都是必要给地址- var name string
- fmt.Scanf("%s",&name) //接收用户输入的数据就要用取地址符号,跟c语言一样。
- //这里的scanf双引号内不能有其他字符干扰,比如不能:"请输入字符:%s",这是接收不到用户输入的数据到name中的。
- //PS:使用Scanf函数的时候他会返回两个值,第一个是是否接收到了数据:接收到了返回1,没有就返回0;第二个是返回错误信息(string)
- //所以我们也可以这样子写接受的数据用户判断:_, err := fmt.Scanf("%s", &name)
- //输出:name赋值成功后就可以正常输出了,按照对应的格式输出即可(使用fmt.Printf函数输出)
- fmt.Printf("%s",name)
复制代码 tips:- Scanf如果是单次获取多个值,使⽤空格隔开,并⾮回⻋,因为fmt.Scanf 的格式化字符串中,逗号(,)会被视为输⼊的⼀部分。
复制代码 Scan
输入的时候空格或者回车隔开
(由于他会等候你输入完成符合接收的个数,如果你接受一个,那么你空格隔开后只接受第一个空格前面作为值)
在使用Scan的时候,用户输入完成之后,会返回两个值:用户输入的个数,错误信息。
注意:使用他,如果你要输入两个值,就必须输入两个,如果要他输入三个值,就必须是三个。否则会不停壅闭。
其实说实话这个就有点扯了,毕竟你都要求输入几个了,还返回啥多少个个数呢是吧,一般人想的都是我不知道输入几个才会想着去打印知道我输入了几个。- var s string
- var num int
- count , err := fmt.Scan(&s,&num)
- if err != nil{
- fmt.Println("错误信息:",err)
- }else {
- fmt.Println("输入个数:",count)
- }
复制代码 Scanln
输入的时候用空格隔开,回车表示输入竣事- var name, desc string
- count, err := fmt.Scanln(&name, &desc)
- if err != nil {
- fmt.Println("发生错误:", err)
- }
- fmt.Println("name:", name, "count:", count)
复制代码
fmt:Println、Print、Printf、Sprintf
Println
- fmt.Println("1",2,name) //输出并且在结尾加上回车换行,多个值可以用逗号隔开,不可以使用格式化字符串
复制代码 Print
不接受可以格式化字符,对每一个操纵数都相当于用了 %v(自动转合适的范例输出)。
不会回车换行Printf
打印特别式化的字符串- fmt.Printf("%s",str)
- fmt.Printf("%d",123)
复制代码 Sprintf
格式化并返回一个字符串,不输出内容- fmt.Sprintf("%s",str)
- fmt.Sprintf("%d",num)
复制代码 条件控制
if else
- if 条件 {
- ...
- } else if{ //注意,else一定要在if的{后面接,如果回车换行写会报错。
- ...
- } else {//注意,else一定要在if的{后面接,如果回车换行写会报错。
- ...
- }
复制代码 比如接着上面输入输出的代码进一步判定- var (
- name string
- )
- _, err := fmt.Scanf("%s", &name)
- if err != nil {
- fmt.Println("没有接受到输入数据。")
- } else {
- fmt.Printf("name = %s", name)
- }
复制代码 练习:要求用户输入姓名、性别、年龄- package main
- import (
- "fmt"
- )
- func main() {
- var (
- name string
- age int
- score float64
- )
- fmt.Println("请输入姓名、年龄、分数:(例如:张三 25 89.5):")
- _, err := fmt.Scanf("%s %d %f", &name, &age, &score)
- if err != nil {
- fmt.Println("输出错误")
- }
- fmt.Printf("姓名:%s\n年龄:%d\n分数:%.2f", name, age, score)
- }
复制代码 Switch
格式示例- color := "blue"
- switch day {
- case "red", "深红": //可以多个条件算一个case
- fmt.Println("红色")
- case "blue", "深蓝":
- fmt.Println("蓝色")
- case "black":
- fmt.Println("黑色")
- default:
- fmt.Println("不知道")
复制代码 练习:区分工作日和周末- var (
- day string
- )
- fmt.Println("请输入星期几:(星期一 or 1)")
- fmt.Scanf("%s", &day)
- switch day {
- case "星期一", "1", "星期二", "2", "星期三", "3", "星期四", "4", "星期五", "5":
- fmt.Println("工作日")
- case "星期六", "6", "星期日", "7":
- fmt.Println("周末")
- default:
- fmt.Println("给我干哪了,这还是地球么?")
- }
复制代码 For
条件循环- //打印0-9
- for i := 0; i < 10; i++{
- fmt.Println(i)
- }
复制代码 死循环- //死循环
- for ;; {
- fmt.Println("666")
- }
复制代码 range提取循环- var arr = []int{1,2,3,4,5,6,7,8888}
- for index, value := range arr{
- fmt.Printf("index: %d, value:%d\n", index,value)
- }
- //不想使用下标的话可以用_匿名变量接受就可以不用管他了。
复制代码 tips:- range使用的时候,切记要记得 :=赋值, for _,value := range arr
复制代码 函数
格式
在上面学习中其实已经相识了函数是长啥样了,这里正式说一下, 同时必要注意一些细节- func 函数名([参数名]) {
- ...
- }
- 比如最常见的:
- func main(){
- fmt.Println("123")
- }
复制代码 可数的参数变量函数
- func f(name string, age int){
- ...
- }
复制代码 可变参数相同范例参数变量函数
- func f(arr ...int){
- for _,i range arr{
- fmt.Println(i) //接收多个参数为一个数组,然后遍历即可。
- }
- }
复制代码 返回一个值
- 格式:
- func f()返回的值类型{
- ...
- }
- func f()(返回的值类型){
- ...
- }
- 例如:
- func f()int{
- return 1111
- }
复制代码 返回多个值
- 格式:
- func f()(返回的值1类型,返回的值2类型...){
-
- }
- 例如:
- func f()(int,string){
- return 1, "123"
- }
复制代码 匿名函数
- 格式:
- func(a int, s string){
- fmt.Println(a,s)
- }(123,"123")
复制代码 初始化函数
初始化函数会在main函数运行之前运行,比如你项目必要在运行之前做一些前置初始化动作可以放在init函数实行。- func init() {
- fmt.Println("我是初始化init函数。")
- }
复制代码 指定变量名作为返回值
- /**
- 需要注意的点:
- 在返回值的地方先定义了一个变量,然后在函数内就不用定义该变量了,直接赋值即可。
- 不可以和传入的参数名相同,因为也是在做定义变量的操作。
- **/
- func fff(a int, b int) (x int, y int, c int) {
- x = a
- y = b
- c = x + y
- return
- }
- func main(){
- x, y, c := fff(4, 4)
- fmt.Println(x, y, c)
- }
- //输出:4 4 8
复制代码 重载方法Methods
方法接受者。
由于go中没有类这种说法,所以只能是在结构体中作文章,那么go中就提供了这种叫做方法接受者,就是雷同于 类.方法 ,方法前面跟一个结构体(可以是指针,指针是不消拷贝)
用法:- func (变量名 结构体)方法名 (){}
- func (变量名 *结构体)方法名 (){}
- 在方法名前面加上↑↑↑,就表示这个结构体实现了这个方法,后续使用的时候就可以结构体.方法使用这个方法了
- 方法的其他返回值或者接受的参数的方式都没变,只是在方法名前作为结构体实现的方法而已。
复制代码 简朴例子:- package main
- import "fmt"
- type Person struct {
- name string
- age int
- }
- func (p Person) getName() string {
- return p.name
- }
- func main() {
- p := Person{
- name: "zhangsan",
- age: 12,
- }
- fmt.Println(p.getName())
- }
复制代码 ⽅法表达式
方法表达式使用 结构体.方法 、(*结构体).方法
两种方式其实都可以,使用指针的就是必要对你结构体中某个变量进行修改就要用到指针去接收方法表达式
使用表达式:
- 方法名(结构体变量,参数1,参数2,,,,)
- 方法名(&结构体变量,参数1,参数2,,,,)
- type Circle struct {
- radius float64
- }
- func (c Circle) Area() float64 {
- //计算圆的面积:π * r的平方
- return (math.Pi * c.radius * c.radius)
- }
- func (c *Circle) Scale(f float64) {
- c.radius = c.radius * f //缩放
- }
- func main() {
- c := Circle{
- radius: 2,
- }
- f := Circle.Area
- fmt.Printf("圆的面积是:%.2f\n", f(c))
- f2 := (*Circle).Scale
- f2(&c, 2) //半径扩大两倍
- fmt.Printf("缩放2倍后的面积为:%.2f\n", f(c))
- }
复制代码 defer(很重要,对理解通道和遍历通道)
现总结:
通道后面会解释,这里先临时知道通道这个词即可...
如果你要对通道进行遍历,一定要在遍历之前记性关闭通道,通常在协程中defer进行关闭,意思是当进步行竣事就会关闭通道,那么这就恰恰符合了协程ok后才彻底关闭通道,否则你主函数和go协程这两个不是同一个东西,协程操纵了通道,你又要在主函数中进行关闭的话就会报错。
遍历通道解决办法就是:
使用了go协程:可以利用defer直接在go协程中关闭,或者使用sync.WaitGroup,但是你要是用sync.WaitGroup关闭的话也要在主进程中- go func() {
- wg.Wait() // 等待
- close(ch) // 关闭通道
- }()
复制代码 上面这段在后面也会提到。
然后如果直接在主进程中进行遍历的话直接close通道就行,由于没有协程必要等候。- 理解:
- defer你只需要理解为帮助函数后执行。
- 比如:当你打开文件,打开文件后需要关闭,但是中间操作的时候报错了就产生了资源浪费,这时候就可以给defer f.close()进行关闭,可以理解为main函数中就算中途报错结束了,defer也会帮你关闭f句柄。
- 多个 defer 的话会反过来执行,比如
- defer fmt.Println(1)
- defer fmt.Println(2)
- defer fmt.Println(3)
- 输出
- 3
- 2
- 1
复制代码 下面经典举例子:
下面的函数和其他技能在后面都会提到,这里只是作为一个例子先放上来作为理解的知识点,主要还是方便复习,初学可以忽略,学完基础再返来理解也是挺不错的。- func start_Ch_scan_port() {
- var (
- ch = make(chan int)
- count int = 0
- )
- var Ch_scan_port = func(hostname string) {
- defer close(ch) //如果你不在这里添加关闭通道,那么你在主函数中关闭通道的话,你是无法对通道关闭然后遍历的。
- //因为你在主进程关闭通道的话就会发生报错,因为协程经常会还没执行完成就被抢占过去然后close就会导致一些还没执行完成的进程执行失败。
- for i := 0; i < 65535; i++ {
- //扫描到开放端口放到ch通道中即可,主函数一直等待读取通道信息
- address := fmt.Sprintf("%s:%d", hostname, i) //ip端口
- conn, err := net.DialTimeout("tcp", address, 2*time.Second) //2秒
- if err == nil {
- conn.Close()
- ch <- i
- }
- }
- runtime.Goexit()
- }
- go Ch_scan_port("127.0.0.1")
- runtime.Gosched() //等待go协程执行完
- //正常思维都是在这里go协程执行完成后就在这里进行close,因为你后面要对通道进行遍历了,但是在这里进行close(ch)的话就会发生报错,因为Gosched之后,在go协程中最后那几个可能还没彻底执行完,所以就会报错。
- for i := range ch {
- fmt.Printf("open: %d\n", i)
- count++
- }
- fmt.Printf("------------Open ports: %d-------------\n", count)
- fmt.Println("------------Scan——Done------------")
- }
复制代码 可以参考的屎山代码如下:- /*
- 根据⽤户输⼊的内容,实现切换编解码的功能.
- 支持base64 url hex编解码
- 功能1:选择编码还是解码
- --->功能1.2:选择编解码的类型
- ------->功能1.2.3:输入内容进行编解码
- 功能2:exit才是推出
- //编码
- to := dongle.Encode.FromString(x).ByBase64().ToString()
- //解码
- to1 := dongle.Decode.FromString(q).ByBase64().ToString()
- */
复制代码
简朴图书馆
功能需求可以参考的屎山代码如下:- 添加新书到图书馆。 查找书籍信息。
- 列出所有书籍。 更新书籍信息。 删除书籍。
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |