Java并修Go(初阶)| 青训营笔记

250 阅读13分钟

语法

1. 格式

1.1、包

package:main函数所在包,一定是main包

1.2、导包

import:导包

// 单个包import "fmt"
​
​
// 多个包import (
    "fmt"
    "time"
)
​

1.3、分号

go 每行结束,可写可不写 ;

推荐是不写

1.4、函数

定义函数, {  必须与函数名在同一行 

2. 变量声明

2.1、单变量声明

默认初始值

类型初始值
int0
float0
boolfalse
string""

声明方式

// 方法一:  带类型,无初始值
var a int 
​
​
// 方法二:  带类型,有初始值
var b int = 100
​
​
// 方法三:  不戴类型,有初始值
var c = 100
​
​
// 方法四:  省去var,指定初始值
d := 100

打印类型:%T

var a int
​
fmt.Printf("type of a = %T\n", a)    // type of a = int
​

2.2、多变量声明

// 单行写法
var xx, yy int = 100, 200
var kk, ll = 100, "Aceld"
​
​
​
// 多行写法
var (
  vv int  = 100
  jj bool = true
)
​

2.3、全局、局部变量声明

其他三种方式都支持

:= 方式不支持,不能声明全局变量 

3. 常量与iota

3.1、常量

常量使用 const 声明,只读,初始化后不能更改

可用于定义 枚举

​
const {
  BEIJING = 0
  SHANGHAI = 1
  SHENZHEN = 3
}
​

3.2、iota

iota 只能在 const 中使用 

iota 可以理解为 所在第几行,iota 初始值为0

iota 用于指定当前行,即以下行的常量的赋值规则 

const (
    //  每行的iota都会累加1, 第一行的iota的默认值是0
    BEIJING = iota      // iota = 0
    SHANGHAI            // iota = 1
    SHENZHEN            // iota = 2
)
​
​
const (
    a, b = iota+1, iota+2         // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
    c, d                          // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
    e, f                         // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4
​
    g, h = iota * 2, iota *3    // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9 
    i, k                        // iota = 4, i = iota * 2, k = iota * 3 , i = 8, k = 12
)
​

4. 函数

4.1、单返回值

返回值类型,指定在后面

func foo1(a string, b int) int {
    return 100
}
​

4.2、多返回值

有名的返回值 会被定义为 局部变量并赋上初始值

// 返回多个返回值,匿名的
func foo2(a string, b int) (int, int) {
    return 666, 777
}
​
​
// 返回多个返回值,有形参名称的
func foo3(a string, b int) (r1 int, r2 int) {
    
    // r1 r2 属于foo3的形参,初始化默认的值是0
    // r1 r2 作用域空间 是foo3 整个函数体的{}空间
    fmt.Println("r1 = ", r1) // 0
    fmt.Println("r2 = ", r2) // 0// 给有名称的返回值变量赋值
    r1 = 1000
    r2 = 2000return r1,r2
}
​
​
​
func foo4(a string, b int) (r1, r2 int) {
    // 给有名称的返回值变量赋值
    r1 = 1000
    r2 = 2000return r1,r2
}
​

4.3、匿名函数


func main() {
	res := func(n1 int, n2 int) int {
		return n1 * n2
	}(10, 20)

	fmt.Printf("res: %v\n", res)
}

5. 导包

5.1、包作用链

在导入包时

  • 会先解析其导入的子包
  • 解析全局常量
  • 调用 当前包的 init函数

image.png

注意:在导包时 要写清楚 包在 GOPATH (配置的go工程路径) 下的完整路径 


package lib1

import "fmt"

func init() {
	fmt.Println("init...")
}

func main() {
	fmt.Println("hello world!")
}



// 导包
package main

import "GoStudy/lib/lib1"			// %GOPATH/GoStudy/lib/lib1

func main() {
    
}

5.2、导包方式

Go 编译检查非常严格,导入的包必须使用,否则会出现语法报错

如果只想使用 包的 init 方法,而不想使用其中的函数,则需要使用匿名导包的方式


-   import _ "fmt":匿名导包

    给 fmt 包一个匿名, ⽆法使用该包的⽅法,但是会执行该包内部的 init() 方法



-   import aa "fmt":别名导包

    给 fmt 包起一个别名 aa,可以用别名直接调用:aa.Println()



-   import . "fmt":导入到当前

    将 fmt 包中的全部方法,导入到当前包的作用域中,全部方法可以直接调用,无需 fmt.API 的形式


6. 指针

Go 语言中也有指针、二级指针的概念

image-20230512130409714

image.png

7. defer

defer 定义的行,会在整个函数声明周期结束后执行,在 return 之后执行 

defer 的声明是 压栈 的方式,先声明的 defer 后执行 

image.png


// main::hello go
// main end

func main() {
    
	defer fmt.Println("main end")
    
	fmt.Println("main::hello go ")
}

8. if-else

  • 后面不需要括号,如果写了,编译器会自动去掉
  • if 后面需要直接跟 { , 不可让 if 与 判定后的业务语句同行

9. for

Go 中 没有 while、dowhile循环,只有唯一一种 for循环

9.1、for循环

  • 后面没有 ( )
  • 三个条件,任意一个都可以省略

9.2、遍历数组

类似于 Python


// 普通遍历
for i:=0; i<len(arr); i++{
    fmt.Println(arr[i])
}


// range 遍历
for index,value := range arr{
    fmt.Println("index ==>",index," value ==>",value)
}


// range 遍历任意一个都可以匿名,即不使用
for _,value := range arr{
    fmt.Println("index ==>",index," value ==>",value)
}

10. switch

  • 不加 ( )
  • 不需要加 break,匹配成功后会自动 break,不会出现 switch 穿透情况
  • switch 后面可以不知道当前匹配的变量,直接在 case 中指定匹配条件

image.png

11. 数组

数组大差不差,一般使用切片(动态数组)

11.1、关于类型

数组的 长度和类型 是一个整体,[4]int & [10]int 不是一个类型,在函数传参时 不能认为 [10]int > [4]int 就随意传递

11.2、关于传递

数组之间的传递是 值传递,传递的是值的副本 


func main() {

	var arr1 [4]int
	var arr2 = arr1

	arr2[0] = 2
	fmt.Println(arr1)		// 0 0 0 0
	fmt.Println(arr2)		// 2 0 0 0
}

12. 切片

切片与数组类似,不过在定义时不指定数组的大小,所以切片的大小是动态的

切片底层 有一个数组,切片的变量名 指向底层数组的首地址

12.1、关于传递

切片与数组不同,切片是 引用传递 


func main() {

	arr1 := []int{0,0,0,0}
	var arr2 = arr1

	arr2[0] = 2
	fmt.Println(arr1)
	fmt.Println(arr2)
}

12.2、关于声明


// 1 声明一个切片,并且初始化,默认值是1,2,3,长度是3
slice1 := []int{1, 2, 3} // [1 2 3]


// 2 声明一个切片,但是没有给它分配空间
var slice2 []int											 // slice2 == nil


// 开辟3个空间,默认值是0
slice2 = make([]int, 3) // [0 0 0]



// 3 声明一个切片,同时给slice分配3个空间,默认值是0
var slice3 []int = make([]int, 3) 							// [0 0 0]



// 4 声明一个切片,同时给slice分配3个空间,默认值是0,通过:=推导出slice是一个切片
slice4 := make([]int, 3) // [0 0 0]

12.3、关于空间

切片中有两个 空间的概念

  • 长度 len:合法空间
  • 容量 cap:总空间(可使用空间)

image.png 当声明一个切片,且不指定 cap时 ,cap默认等于 len 

当追加后超出原 cap,切片会扩容为原来的 2倍,类似 Java 的 HashMap(不过 Java 是根据随机因子)

  注意:追加完 将结果赋值回原切片 


// len = 3 , cap = 5 
var numbers = make([]int, 3, 5)


// 当 追加后 len <= cap 时,直接追加
numbers = append(numbers, 3)



// 当追加后 len > cap, 切片会扩容为原来的 2倍
numbers = append(numbers, 3)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

12.4、关于截取

截取 切片,与 Python 的序列截取类似,左闭右开

 但是截取是浅拷贝,子切片改变,也会导致 原切片改变 


func main() {
	
	numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}


	fmt.Println("numbers[1:4] ==", numbers[1:4])

	fmt.Println("numbers[:3] ==", numbers[:3])

	fmt.Println("numbers[4:] ==", numbers[4:])


	numbers3 := numbers[2:5]
	fmt.Println(numbers3)
}

12.5、关于拷贝

上述 直接截取是浅拷贝

Go 提供了一种 切片的深拷贝方式 -> copy

注意:此处的 des、src,与 Java 是反着的


slice1 := []int{1, 2, 3}

slice2 := make([]int, 3)

copy(slice2, slice1)


slice2[0] = 10
fmt.Println(slice1) // [1 2 3]

13. map

13.1、声明方式

①:使用时,再分配空间


// 声明一种map类型 key - string,value - string
var myMap1 map[string]string

fmt.Println(myMap1 == nil) // true


// 使用map前,需要先用make给map分配数据空间
myMap1 = make(map[string]string, 10)

myMap1["one"] = "java"
myMap1["two"] = "c++"
myMap1["three"] = "python"

②:声明时就 分配空间


myMap2 := make(map[int]string)

myMap2[1] = "java"
myMap2[2] = "c++"
myMap2[3] = "python"

fmt.Println(myMap2)

③:声明时,初始化空间


myMap3 := map[string]string {
  "one":   "php",
  "two":   "c++",
  "three": "python",
}

fmt.Println(myMap3)

13.2、CRUD

  • 删除:delete( map , key )

func main() {
    
	cityMap := make(map[string]string)
	
        // 添加
	cityMap["China"] = "Beijing"
	cityMap["Japan"] = "Tokyo"
	cityMap["USA"] = "NewYork"
	
        // 删除
	delete(cityMap, "China")
	
	// 修改
	cityMap["USA"] = "DC"
    
        // 遍历
        for key, value := range cityMap {
            fmt.Println("key = ", key+", value = ", value)
	}
    
}

13.3、传递

 map 之间的传递是引用传递 

13.4、判断存在

两个返回值

  • 是否存在

func main() {

	m := make(map[string]interface{})
	m["a"] = "AAA"

	if v, ok := m["a"]; ok {
		fmt.Println("存在", v)
	} else {
		fmt.Println("不存在")
	}

}

14. type

14.1、type

type 可以单独用于定义一种新的数据类型,即给类型取别名


type myint int

func main() {
  	var a myint = 10
		fmt.Println("a = ", a)
		fmt.Printf("type of a = %T\n", a)				//  type of a = main.myint
}

15、struct

struct 用于定义结构体,与 type 配合,即可以定义一个 新的 (由 "基本类型" 构成的)复杂结构类型,类似于 Java 的类


type Book struct {
	title string
	price string
}



func main() {
	var book Book
	book.title = "Golang"
	book.price = "111"
	fmt.Printf("%v\n", book) // {Golang 111}
}

15.1、struct传递

struct 之间的传递 是值传递,即传递一个副本,想要修改 外部的值,则需要定义为指针

image.png

一道面试题


type student struct {
	name string
	age  int
}

func main() {
	m := make(map[string]*student)
	stus := []student{
		{name: "aaa", age: 18},
		{name: "bbb", age: 23},
		{name: "ccc", age: 28},
	}
	for _, stu := range stus {
		m[stu.name] = &stu					// 注意此处,每一个v,其实记录的都是 stu的地址值,所有key的v都是同一个stu
	}
    
    // aaa => ccc   bbb => ccc   ccc => ccc
	for k, v := range m {
		fmt.Println(k, "=>", v.name)		// 此时取,v.name 都是同一个 地址的name,即最后一次更新的 stu
	}
}


// 解决

for _, stu := range stus {
  // 方法1
  temp := stu					// 每次创建一个新变量,值传递更新一个地址
  m[stu.name] = &temp
}


for i, stu := range stus {
  // 方法2
  m[stu.name] = &stus[i]		// 指定 第几个的地址
}

15.2、封装

Golang 中,类名、属性名、方法名 首字母大写 表示对外(其他包)可以访问,否则只能够在本包内访问


// 如果类名首字母大写,表示其他包也能够访问
type Hero struct {
	// 如果类的属性首字母大写, 表示该属性是对外能够访问的,否则的话只能够类的内部访问
	Name  string
	Ad    int
	level int // 只能本包访问
}

15.3、结构体方法

func( 绑定结构体 )

因为 结构体之间的传递是 值传递,如果不加 * ,则是传递当前对象的一个副本,无法实现对当前对象的 写 

所以,要加上 * 来实现对 对象的读写操作 


func (h *Hero) Show() {
	fmt.Println("Name = ", h.Name)
	fmt.Println("Ad = ", h.Ad)
	fmt.Println("Level = ", h.level)
	fmt.Println("---------")
}

func (h *Hero) GetName() string {
	return h.Name
}

// 不用指针则传递的是副本,无法赋值
func (h *Hero) SetName(newName string) {
	h.Name = newName
}

func main() {
	hero := Hero{Name: "zhang3", Ad: 100}
	hero.Show()

	hero.SetName("li4")
	hero.Show()
}

15.4、继承

  • 继承,直接在子类写入父类的结构体名即可

  • 对象

    • 在创建对象时,父类的属性单独写
    • 或者,先不初始化,后面再通过 .Attr 来赋值即可(不区分 父子属性)

// 父类
type Human struct {
	name string
	sex  string
}

func (h *Human) Eat() {
	fmt.Println("Human.Eat()...")
}

func (h *Human) Walk() {
	fmt.Println("Human.Walk()...")
}




// 子类
type SuperMan struct {
	Human 						// SuperMan类继承了Human类的方法和属性
	level int
}

// 重定义父类的方法Eat()
func (s *SuperMan) Eat() {
	fmt.Println("SuperMan.Eat()...")
}

// 子类的新方法
func (s *SuperMan) Fly() {
	fmt.Println("SuperMan.Fly()...")
}

func main() {
  // 定义一个子类对象
  // s := SuperMan{Human{"li4", "female"}, 88}
  var s SuperMan
  s.name = "li4"
  s.sex = "male"
  s.level = 88

  s.Walk() // 父类的方法
  s.Eat()  // 子类的方法
  s.Fly()  // 子类的方法 
}

16. interface

16.1、本质

接口:本质是一个指针,所以 传入的是 子类的地址 

面试题


// 编译能通过吗?

type People interface {
	Speak(string) string
}

type Student struct{}

func (stu *Student) Speak(think string) (talk string) {
	if think == "hello" {
		talk = "你好"
	} else {
		talk = "hi"
	}
	return
}

func main() {
	var peo People = Student{}
	think := "wow"
	fmt.Println(peo.Speak(think))
}


// 不能

// 不能。修改 var peo People = Student{} 为 var peo People = &Student{} 即可

16.2、实现

要实现接口,满足两个条件

接口定义方法


// 本质是一个指针
type AnimalIF interface {
	Sleep()
	GetColor() string //  获取动物的颜色
	GetType() string  // 获取动物的种类
}

子类实现父类的全部方法


// 具体的类
type Cat struct {
	color string // 猫的颜色
}

func (c *Cat) Sleep() {
	fmt.Println("Cat is Sleep")
}

func (c *Cat) GetColor() string {
	return c.color
}

func (c *Cat) GetType() string {
	return "Cat"
}

多态


// 接口的数据类型,父类指针
var animal AnimalIF
animal = &Cat{"Green"}
animal.Sleep() 					// 调用的就是Cat的Sleep()方法, 多态

16.3、空接口+断言

interface{ } 表示空接口,可以用它引用任意类型的数据类型

与 Java 中的 Object类似


// 示例


// interface{}是万能数据类型
func myFunc(arg interface{}) {
	fmt.Println(arg)
}

type Book struct {
	auth string
}

func main() {
	book := Book{"Golang"}

	myFunc(book)
	myFunc(100)
	myFunc("abc")
	myFunc(3.14)
}

断言


// if判断
func myFunc(arg interface{}) {

    // 类型断言:  值、bool
    value, ok := arg.(string)

    if !ok {
        fmt.Println("arg is not string type")
    } else {
        fmt.Println("arg is string type, value = ", value)
        fmt.Printf("value type is %T\n", value)
    }
}



// switch 选择
func justifyType(x interface{}) {
    
    switch v := x.(type) {
        case string:
        	fmt.Printf("x is a string,value is %v\n", v)
        case int:
        	fmt.Printf("x is a int is %v\n", v)
        case bool:
        	fmt.Printf("x is a bool is %v\n", v)
        default:
        	fmt.Println("unsupport type!")
    }
}

17. Error

17.1、捕获异常


func main() {
    
    // 定义并调用
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获:", err)			// 捕获: runtime error: index out of range [4] with length 3
		}
	}()

	nums := []int{1, 2, 3}
	fmt.Println(nums[4]) // 系统抛出异常
}

17.2、抛出异常


func main() {
    
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获:", err)
		}
	}()
    
	panic("出现异常!") 		// 手动抛出异常		// 捕获: 出现异常!
}

17.3、返回异常


func getCircleArea(radius float32) (area float32, err error) {
    
	if radius < 0 {
		// 构建异常对象
		err = errors.New("半径不能为负")
		return
	}
    
	area = 3.14 * radius * radius
	return
}


func main() {
	area, err := getCircleArea(-5)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(area)
	}
}

17.4、自定义异常


type PathError struct {
	path       string
	op         string
	createTime string
	message    string
}


func (p *PathError) Error() string {
	return fmt.Sprintf("path=%s \nop=%s \ncreateTime=%s \nmessage=%s",
		p.path, p.op, p.createTime, p.message)
}



func Open(filename string) error {

	file, err := os.Open(filename)
	if err != nil {
		return &PathError{
			path:       filename,
			op:         "read",
			message:    err.Error(),
			createTime: fmt.Sprintf("%v", time.Now()),
		}
	}

	defer file.Close()
	return nil
}


func main() {
	err := Open("test.txt")
	switch v := err.(type) {
	case *PathError:
		fmt.Println("get path error,", v)
	default:
	}
}

18. string

因为 Go 使用 UTF-8 编码,UTF-8 的 Unicode 中文占 3个字节

18.1、操作


package main

import (
	"fmt"
	"strings"
)


func main() {
	a := "hello"
	fmt.Println(strings.Contains(a, "ll"))							// true
	fmt.Println(strings.Count(a, "l"))								// 2
	fmt.Println(strings.HasPrefix(a, "he"))							// true
	fmt.Println(strings.HasSuffix(a, "llo"))						// true
	fmt.Println(strings.Index(a, "li"))								// -1
	fmt.Println(strings.Join([]string{"he", "llo"}, "-"))			// he-llo
	fmt.Println(strings.Repeat(a, 2))								// hellohello
	fmt.Println(strings.Replace(a, "e", "E", -1))					// hEllo
	fmt.Println(strings.Split("a-b-c", "-"))						// [a b d]
	fmt.Println(strings.ToLower(a))									// hello
	fmt.Println(strings.ToUpper(a))									// HELLO
	fmt.Println(len(a))												// 5
	
    b := "你好"							
	fmt.Println(len(b))												// 6		
}

18.2、打印

与 C 的printf类似

  • %v : 可以打印任意类型数据
  • %+v : 打印详细信息(带上属性名)
  • %#v: 打印更详细的信息(属性名+类名)

package main

import (
	"fmt"
)

type point struct {
	x, y int
}


func main() {
	s := "hello"
	n := 123
	p := point{1, 2}

	fmt.Println(s, n)										// hello 123
	fmt.Println(p)											// {1 2}
	fmt.Printf(" s=%v \n", s)								// s=hello 
	fmt.Printf(" n=%v\n", n)								// n=123
	fmt.Printf(" p=%v\n", p)								// p={1 2}
	fmt.Printf(" p=%+v\n", p)								// p={x:1 y:2}
	fmt.Printf(" p=%#v \n", p)								// p=main.point{x:1, y:2}

	f := 3.141592653
	fmt.Println(f)											// 3.141592653
	fmt.Printf("%.2f\n", f)									// 3.14
    
}

18.3、json

Go 数据 <===> Json:

如果想改变 转为 Json后的字段名,使用


Age   int `json:"age"` 			// 指定字段名   注意 ``
  • Marshal:Go ==> json

    • 返回值:byte数组 + error

image.png

  • MarshalIndent:go ==> json

    • 参数:数据 + 前缀 + 缩进

    • 返回值: byte数组 + error

image.png

  • Unmarshal:json ==> Go

    • 参数: 传化后的变量地址 + 数据

    • 返回值:error

      image-20230512170342409

image.png


package main

import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{" Golang", "TypeScript"}}
	buf, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}

	fmt.Println(buf)
	fmt.Println(string(buf))

	buf, err = json.MarshalIndent(a, "", "\t")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(buf))
	var b userInfo
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", b)

}

19. 时间


package main

import (
	"fmt"
	"time"
)

func main() {

    
    // 获取当前时间
	now := time.Now()

	fmt.Println(now)					// 2023-05-12 17:22:05.9443335 +0800 CST m=+0.002569701
	fmt.Println(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond())						// 2023 May 12 17 22 5 944333500
	
    
    
    // 返回值是string
	t := now.Format("2006-01-02 15:04:05")
	fmt.Println(t)						// 2023-05-12 17:22:05
    

    
    // 注意时区
	t3, _ := time.Parse("2006-01-02 15:04:05", "2023-05-12 17:12:00")
	fmt.Println(t3)						// 2023-05-12 17:12:00 +0000 UTC

    
    // Sub 时间相减
	newNow := time.Now()
	fmt.Println(newNow.Sub(now))		// 13.3879ms

    
    
    // 获取时间戳
	fmt.Println(newNow.Unix())			// 1683883325

}

20. 数字 - 字符串

数字和字符串之间的相互转换


package main

import (
	"fmt"
	"strconv"
)


func main() {

    
    // 数字 字符串 + 进制 + 精度 (如果不指定:  _ + 自动推断 + 64位)
	num, _ := strconv.ParseInt("100", 10, 64)
	fmt.Println(num)

	flo, _ := strconv.ParseFloat("1.35", 64)
	fmt.Println(flo)

    
    // 快速将字符串转 10 进制
	intNum, _ := strconv.Atoi("123")			// 数字 + error
	fmt.Println(intNum)
    
}

21. 进程信息

image.png

22. 输入

22.1、Scanf

与 C语言的 Scanf 差不多

image.png


func main() {

	var i int
	var s string
	var f float64
	var c byte
	fmt.Scanf("%d %s %f %c", &i, &s, &f, &c)
	fmt.Println(i, s, f, c)

}

image.png

22.2、Scanln


func main() {

	var i int
	var s string
	var f float64
	var c byte
	fmt.Scanln(&i)
	fmt.Scanln(&s)
	fmt.Scanln(&f)
	fmt.Scanln(&c)
	fmt.Println(i, s, f, c)

}

image.png

22.3、Scan


func main() {

	var i int
	var s string
	var f float64
	var c byte
	fmt.Scanln(&i, &s, &f, &c)
	fmt.Println(i, s, f, c)

}

image.png

​ ​

22.4、bufio

bufio包是对IO的封装,可以操作文件等内容

同样可以用来接收键盘的输入,此时对象不是文件,而是os.Stdin,也就是标准输入设备

bufio包含了Reader、Wrter、Scanner等对象,封装了很多对IO内容处理方法,但要从键盘输入,使用Reader对象(或Scanner对象)。

22.4.1、Readbyte

只会读取输入的第一个字节,如果 string强转:中文会因读取不全而乱码


func main() {

	reader := bufio.NewReader(os.Stdin)

	res, error := reader.ReadByte();

	if error == nil {
		fmt.Println(res)
	} else {
		fmt.Println(error)
	}

}	

image.png

22.4.2、ReadBytes

读取到指定字符才结束(包括),并且,可以接收空格


func main() {

	reader := bufio.NewReader(os.Stdin)

	res, error := reader.ReadBytes('\n')

	if error == nil {
		fmt.Println(string(res))
		fmt.Println(res)
	} else {
		fmt.Println(error)
	}

}

image.png

22.4.3、ReadRune

也是读取一个字符,返回值多了一个 size


func main() {

	reader := bufio.NewReader(os.Stdin)

	res, size, error := reader.ReadRune()

	if error == nil {
		fmt.Println(string(res))
		fmt.Println(size)
		fmt.Println(res)
	} else {
		fmt.Println(error)
	}

}

image.png

22.4.5、Readstring

读取字符串,与 ReadBytes 类似,不过返回值是一个字符串


func main() {

	reader := bufio.NewReader(os.Stdin)

	res, error := reader.ReadString('\n')

	if error == nil {
		fmt.Println(string(res))
		fmt.Println(res)
	} else {
		fmt.Println(error)
	}

}

image.png

22.5、综合:判断随机数


import (
	"fmt"
	"math/rand"
	"time"
)


func main() {

	rand.Seed(time.Now().UnixNano())		// 随机数种子,与C类似
	maxNum := 66							// 随机数最大值

	random := rand.Intn(maxNum)

	var guess int

	for {
        // 可以替换为 fmt.Scan(&guess)
		fmt.Scanf("%d\n", &guess)			// 注意 Scanf不会刷新缓存区,需要读取这个\n
		
        if guess < random {
			fmt.Println("bigger bro")
		} else if guess > random {
			fmt.Println("no no no smaller")
		} else {
			fmt.Println("Bingo!")
			break
		}

	}
}