Go语言的基础语法可以跟着教程: learnku.com/docs/the-wa…
自己挨个敲一遍,熟悉一下Go的语法风格,如果你有其他编程语言基础,应该一两天就能练完。我个人是利用国庆节,用了三天,每天上午9点起床后,练到1点左右,平均一天3~4个小时。
一、基本语法
package main
import (
"fmt"
"math/rand"
"os"
"runtime"
"sort"
"time"
)
// 1. 基础变量定义
func variable() {
//写法1:
var a int
var s string
a = 10
s = "你好"
fmt.Print(a, s)
//写法2,自动推断类型
var b = 20
var c = "Hello"
fmt.Print(b, c)
//写法3
var e int = 11
var f bool = false
fmt.Print(e, f)
//写法4,推荐简写
g := 100
h := "简写"
fmt.Print(g, h)
}
// 2. 使用内置方法获取系统信息
func sysFn() {
goos := runtime.GOOS //获取操作系统类型,如Windows
path := os.Getenv("PATH") //获取环境变量中的值
fmt.Printf("The operating system is: %s\n", goos)
fmt.Printf("Path is %s\n", path)
}
// 3. 布尔类型及运算(与或非)
func boolFn() {
a := 1
fmt.Print(a > 10) //false
b := 0
//fmt.Print(a && b) //会报错,无法隐式类型转换
c := a > 0 && b > 0
fmt.Print(c) //false
d := a > 0 || b > 0
fmt.Print(d) //true
fmt.Print(!d) //false
}
// 4. 算数运算符(+ - * / %)
func mathFn() {
a := 9
b := 4
//fmt.Print(a + b) //13
//fmt.Print(a - b) //5
//fmt.Print(a * b) //36
//fmt.Print(a / b) //2
fmt.Print(a % b) //1
}
// 5. 随机数
func randFn() {
//fmt.Print(rand.Int()) //1225083820117581013
//fmt.Print(rand.Intn(10)) //0~10之间的随机数
//fmt.Print(rand.Float32()) //0.20632032930679400
fmt.Print(rand.Float64()) //0.05067825471427861173943200
times := time.Now().Nanosecond() //随机时间戳 now~9999999999
fmt.Print(times)
}
// 6. 字符串
func strFn() {
//a := "Hello"
//fmt.Print(len(a)) //获取字符串长度
//fmt.Printf("序号是%d,值是%c ", a[3], a[2]) //打印字符串使用Printf
//1. 前缀和后缀
//b := strings.HasPrefix(a, "H") //a字符串是不是H字母打头的
//fmt.Print(b) //true
//c := strings.HasSuffix(a, "l") //a字符串是不是l结尾的
//fmt.Print(c) //false
//2. 字符串包含关系
//d := strings.Contains(a, "l") //a字符串中是否包含l字母
//fmt.Print(d) //true
//3.判断子字符串或字符在父字符串中出现的位置(索引)
//e := strings.Index(a, "l") //2 首次出现的位置下标
//f := strings.Index(a, "h") //-1 不存在
//fmt.Print(e, f)
//g := strings.LastIndex(a, "l") // 最后一次出现的位置下标
//fmt.Print(g) //3
//4. 字符串替换
//h := strings.Replace("Hello", "l", "L", 1) // HeLlo, 只替换一个l
//hh := strings.Replace("Hello", "l", "L", -1) // HeLLo, 替换所有l
//fmt.Print(h, hh)
//5.统计字符串出现次数
//i := strings.Count("Hello", "l") //2
//fmt.Print(i)
//6.重复字符串
//j := strings.Repeat("Hello", 3) //HelloHelloHello
//fmt.Print(j)
//7. 修改字符串大小写
//aa := "Hello"
//fmt.Print(strings.ToLower(aa)) //hello
//fmt.Print(strings.ToUpper(aa)) //HELLO
//8.修剪字符串
//k := strings.TrimSpace(" Hello World ") //剔除首尾空格
//fmt.Print(k) //Hello World
//fmt.Print(strings.Trim("do something in this world", "d")) //剔除收尾的d
//fmt.Print(strings.TrimLeft("Hello", "H")) //ello , 剔除首位的H
//fmt.Print(strings.TrimRight("Hello", "o")) //Hell , 剔除末尾的o
//9. 字符串分割与拼接
//l := strings.Fields("Hello World") //按照空白符分割字符串,得到slice
//fmt.Print(l) //[Hello World]
//fmt.Print(strings.Split("Hello", "")) //[H e l l o] ,按指定字符分割,得到slice
//fmt.Print(strings.Join(l, "-")) //Hello-World
}
// 7. 时间
func timeFn() {
t := time.Now()
fmt.Print(t.Year(), "-")
fmt.Print(t.Month(), "-")
fmt.Print(t.Day(), "-")
fmt.Print(t.Hour(), "-")
fmt.Print(t.Minute(), "-")
fmt.Print(t.Second(), "-")
}
// 8. 条件语句
func ieFn() {
//1.基本语法
if runtime.GOOS == "windows" {
fmt.Print("这是Windows系统")
} else {
fmt.Print("这不是windows系统")
}
//2.if 可以包含一个初始化语句(如:给一个变量赋值)。这种写法具有固定的格式(在初始化语句后方必须加上分号)
if val := 10; val > 9 {
fmt.Print("10大于9")
}
//3.switch
i := 1
switch i {
case 1:
fmt.Print("传入了1")
case 2:
fmt.Print("传入了2")
default:
fmt.Print("传入了其他")
}
}
// 9.for循环
func forFn() {
//1.基本语法;永远不要在循环体内修改计数器,这在任何语言中都是非常差的实践!
//for i := 0; i < 10; i++ {
// fmt.Print(i, "\n")
//}
//2. 拓展练习,打印下列需求
//G
//GG
//GGG
//GGGG
//GGGGG
//GGGGGG
//for i := 0; i < 6; i++ {
// fmt.Print(strings.Repeat("G", i+1), "\n")
//}
//3. 基于条件判断的迭代
//i := 5
//for i > 0 {
// i = i - 1
// fmt.Print(i, "\n")
//}
//4.for-range 结构
str := "Hello"
for i, chart := range str {
fmt.Printf("序号是%d,值是%c \n", i, chart)
}
//5. break
for i := 0; i < 3; i++ {
for j := 0; j < 10; j++ {
if j > 5 {
break //只会每次跳出j循环
}
print(j)
}
print(" ")
}
//6. continue
for i := 0; i < 10; i++ {
if i == 5 {
continue //跳过5的打印
}
print(i)
}
}
// 10. 函数
// 10-1.函数及其返回值
func sum(a int, b int) int {
return a * b
}
// 10-2 函数返回多个值
func more(arg int) (a int, b int) {
x := 2 * arg
y := 3 * arg
return x, y
}
// 10-3 传递变长参数
func restFn(a int, b int, arg ...int) int {
c := a + b
for _, i2 := range arg {
c += i2
}
return c
}
// 10-4 关键字 defer
// 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。
// (译者注:return 是非原子性的,需要两步,执行前首先要得到返回值 (为返回值赋值),return 将返回值返回调用处。
// defer 和 return 的执行顺序是先为返回值赋值,然后执行 defer,然后 return 到函数调用处。)
func fn1() {
fmt.Print("1111", "\n")
defer fn2() //1111 2222 3333 对比使用defer与不使用defer的区别
fmt.Print("2222", "\n")
}
func fn2() {
fmt.Print("3333", "\n")
}
// 10-5 内置函数
//Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作,
//例如:len、cap 和 append,或必须用于系统级的操作,
//例如:panic。
//因此,它们需要直接获得编译器的支持。
// 10-6 递归函数
//计算斐波那契数列,即前两个数为 1,从第三个数开始每个数均为前两个数之和。
//1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, …
func fibonacci(n int) (res int) {
if n < 1 {
res = 1 //可以直接为返回值赋值
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
return
}
func testFibonacci() {
for i := 0; i < 10; i++ {
m := fibonacci(i)
fmt.Printf("fibonacci(%d),is: %d \n", i, m)
}
}
// 递归练习
// 重写本节中生成斐波那契数列的程序并返回两个命名返回值,
// 即数列中的位置和对应的值,例如 5 与 4,89 与 10。
func fibonacci6(n int) (i int, res int) {
if n < 1 {
res = 1 //可以直接为返回值赋值
i = n
} else {
_, m := fibonacci6(n - 1)
_, w := fibonacci6(n - 2)
res = m + w
i = n + 1
}
return
}
func testFibonacci6() {
for i := 0; i < 10; i++ {
j, m := fibonacci6(i)
fmt.Printf("fibonacci(%d),is: %d \n", j, m)
}
}
// 10-7 将函数作为参数 【回调函数】
func add(a int, b int) {
fmt.Printf("累加结果为 %d", a+b)
}
func callbackFn(a int, b int, f func(int, int)) {
f(a, b)
}
// 10-8 计算函数执行时间 Now()、Sub()
func runTimeFn() {
start := time.Now() //开始时间
for i := 0; i < 100; i++ {
print(i)
}
end := time.Now() //结束时间
delta := end.Sub(start) //差值计算
fmt.Printf("执行时间为 %f 秒", delta.Seconds())
}
func runFn() {
//print(sum(3, 7))
//
//var a, b int
//a, b = more(3)
//fmt.Printf("函数返回多个值,分别为 %d 和 %d ", a, b)
//
//d := restFn(1, 2, 3, 4, 5, 6, 7, 8, 9)
//print(d)
//fn1()
//testFibonacci() //测试递归
//testFibonacci6() //测试递归练习
//callbackFn(2, 3, add) //测试回调函数
runTimeFn()
}
// 11. 数组
func arrFn() {
//11-1 数组定义方式
//var array1 [5]int //定义空数组
//array2 := [3]int{1, 2, 3} //:写法必须要设定初始值
//array3 := [...]int{1, 2, 3, 4, 5, 6, 7} // 让go自动识别数组长度
//var array4 [4][5]int //定义4行5列的二维数组
//fmt.Println(array1, array2, array3)
//fmt.Println(array4)
//
//11-2 使用for循环遍历数组
//var arr1 [5]int
//for i := 0; i < len(arr1); i++ {
// arr1[i] = i * 2
//}
//for i := 0; i < len(arr1); i++ {
// fmt.Printf("数组的序号是 %d ,对应的值是 %d \n", i, arr1[i])
//}
//11-3 使用for-range遍历数组【获取一个数组中的最大值及其对应下标】
arr2 := [...]int{2, 5, 7, 33, 9, 10}
maxi := -1
maxValue := -1
for i, v := range arr2 {
if v > maxValue {
maxi, maxValue = i, v
}
}
fmt.Print(maxi, maxValue)
//11-4-1 数组是值类型 【跟js不同,go数组默认没有引用关系】
a1 := [3]int{2, 3, 4}
//printArr1(a1)
//fmt.Print("函数外部 \n")
//fmt.Println(a1)
// 11-4-2 通过指针传递引用操作数组
printArr2(&a1)
fmt.Print("函数外部 \n")
fmt.Println(a1)
}
func printArr1(arr [3]int) {
arr[0] = 111 //此处的修改不会影响传入的a1
fmt.Print("函数1内部 \n")
fmt.Println(arr)
}
func printArr2(arr *[3]int) {
arr[0] = 222 //此处的修改会影响传入的a1
fmt.Print("函数2内部 \n")
fmt.Println(arr)
}
// 12. 切片 【引用类型】
// 切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),
// 所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。
// 这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。
// 需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
func sliceFn() {
//12-1 基本操作
arr := [...]int{0, 1, 2, 3, 4, 5, 6}
s := arr[:] //slice可以是数组的一段view
s1 := arr[2:6] //slice可以向后扩展。虽然s1的值是[2,3,4,5],但是s1的容量是5---[2,3,4,5,6]
s2 := s1[3:5] //[5,6],slice可以获取内容之外,容量之内的值6
fmt.Println(s, s1, s2)
//fmt.Printf("s1的值:%v s1的长度:%d s1的容量:%d", s1, len(s1), cap(s1))
//12-2 向slice中添加新值
s3 := append(s1, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
//fmt.Println("s3=", s3) //[2 3 4 5 10] ,s1容量的最后一位,也是arr的最后一位
//fmt.Println("arr=", arr) // [0 1 2 3 4 5 10] 对于s1容量范围内的操作,会影响arr
//fmt.Println("s4=", s4) //[2 3 4 5 10 11] 此处append的11,因为已经超出了arr的范围,go会自动在内存中为s4分配一个更大的空间,重新存放,后续操作将无法再影响arr
fmt.Println("s5=", s5) // [2 3 4 5 10 11 12]
//12-3 其他创建slice的方法
var ss []int //初始定义的空slice默认值为nil【类似js中的null】
fmt.Println(ss, len(ss))
ss1 := append(ss, 1, 2, 3, 4)
fmt.Println(ss1, len(ss1))
ss2 := make([]int, 10) //创建一个长度为10的slice
fmt.Println(ss2, len(ss2), cap(ss2))
ss3 := make([]int, 6, 10) //创建一个长度为6,容量为10的slice
fmt.Println(ss3, len(ss3), cap(ss3))
// 12-4 拷贝
copy(ss2, ss1) //将ss1拷贝到ss2中【目标在前,源头在后】
fmt.Println("拷贝后的ss2", ss2) // [1 2 3 4 0 0 0 0 0 0]
// 12-5 删除
// 删除中间位,例如:删除ss2中的3
ss2 = append(ss2[:2], ss2[3:]...) //没有直接删除,而是跳过截取,...类似js的拓展运算符
fmt.Println("删除3后的ss2", ss2) //[1 2 4 0 0 0 0 0 0]
front := ss2[0]
ss2 = ss2[1:]
fmt.Println("删除首位后的ss2", ss2, "被删除的值为", front)
tail := ss2[len(ss2)-1]
ss2 = ss2[:len(ss2)-1]
fmt.Println("删除末尾后的ss2", ss2, "被删除的值为", tail)
//总结
// 可以发现,对于slice的删除操作,其本质是不同姿势的截取操作
}
// 13. Map 【引用类型】
// 声明语法
// var map1 map[keytype]valuetype
// var map1 map[string]int
func mapFn() {
//13-1 基本定义及引用特征
var map1 map[string]int
var map2 map[string]int
map1 = map[string]int{"one": 1, "two": 2}
map2 = map1 //发生引用关系
map2["three"] = 3 //map2的操作会影响map1
fmt.Println(map1) //map[one:1 three:3 two:2]
fmt.Println(map2) //map[one:1 three:3 two:2]
// 13-2 使用make创建map 【永远使用make来构造map,不要使用new】
var map3 = make(map[string]float32)
map3["key1"] = 3.14
map3["key2"] = 4.28
fmt.Println(map3)
// 13-3 map的值可以是任意类型
// 如:函数
map4 := map[int]func() int{
1: func() int {
return 10
},
2: func() int {
return 20
},
}
fmt.Println(map4) //map[1:0xc3f220 2:0xc3f240] 值为函数的地址
fmt.Println(map4[1]()) //10
//如:切片
map5 := map[string][]int{
"first": []int{1, 2, 3, 4},
"second": []int{4, 5, 6, 7},
}
fmt.Println(map5)
fmt.Println(map5["second"][1:3]) //[5,6]
//13-4 判断map的key是否存在
v, ok := map5["first"] //此处的v如果不使用,也可以使用_占位
if ok {
fmt.Println("map5数据包中存在名为first的key,值为", v)
}
//13-5 使用for-range操作map
map6 := map[int]float32{1: 1.2, 2: 2.34, 3: 3.14}
for key, v := range map6 {
fmt.Println("键名为", key, "键值为", v)
}
//13-6 map类型的切片 【可以两次make】
items := make([]map[int]int, 10)
//方法1:
//for i := 0; i < len(items); i++ {
// items[i] = map[int]int{i: i * 2}
//}
//fmt.Println(items)
//方法2:
for i := range items {
items[i] = make(map[int]int, 1) //每个map都是key为1,value变化
items[i][1] = i * 2
}
fmt.Println(items)
//13-7 map的排序
//按需提取key,形成slice后,对slice进行排序,然后再按排序后的key提取value
var (
barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,
"delta": 87, "echo": 56, "foxtrot": 12,
"golf": 34, "hotel": 16, "indio": 87,
"juliet": 65, "kili": 43, "lima": 98}
)
for k, v := range barVal {
fmt.Println("key是:", k, "value是:", v) //无序打印
}
keys := make([]string, len(barVal))
i := 0
for k1 := range barVal {
keys[i] = k1
i++
}
fmt.Println("整合map的所有key为slice数据", keys)
sort.Strings(keys) //按字母排序
fmt.Println("排序后的slice", keys)
for _, key := range keys {
fmt.Println("key是:", key, "value是:", barVal[key]) //有序打印
}
}
// 14. 结构体 struct
// 特点:结构体内部只有属性,没有方法
// 在 C 家族的编程语言中它也存在,并且名字也是 struct,在面向对象的编程语言中,
// 跟一个无方法的轻量级类一样。不过因为 Go 语言中没有类的概念,
// 因此在 Go 中结构体有着更为重要的地位。
// 1. 定义一个游戏玩家结构体
type Player struct {
Name string
level int
exp int
}
// 5. 为结构体挂载方法
func (p *Player) levelUpdate() {
p.level++
p.exp = 0
fmt.Printf("玩家升级成功,当前等级%d,当前经验%d \n", p.level, p.exp)
}
// 7. 为结构体,封装工厂函数
func NewPlayer(name string, lv int, exp int) *Player {
return &Player{
Name: name,
level: lv,
exp: exp,
}
}
func structFn() {
//2.创建玩家实例
player := Player{Name: "三丰", level: 10, exp: 100}
//3.访问结构体字段
fmt.Println(player.Name)
//4.修改结构体数据
player.level = 11
fmt.Println(player.level)
//6. 调用为结构体挂载的方法
player.levelUpdate()
//8. 调用工厂函数,创建结构体实例
player1 := NewPlayer("无忌", 1, 10)
fmt.Println(player1.Name)
}
func main() {
fmt.Println("Hello World")
//variable()
//sysFn()
//boolFn()
//mathFn()
//randFn()
//strFn()
//timeFn()
//ieFn()
//forFn()
//runFn()
//arrFn()
//sliceFn()
//mapFn()
structFn()
}
二、闭包【函数式编程】
package main
import "fmt"
// 1. 封装闭包函数
func adder() func(int) int {
sum := 0
// 当我们调用下面这个闭包函数的时候,可以拿到整个adder内部的自由变量
return func(v int) int {
sum += v
return sum
}
}
func main() {
a := adder()
fmt.Printf("入参:%d,闭包返回:%d \n", 1, a(1))
fmt.Printf("入参:%d,闭包返回:%d \n", 2, a(2))
fmt.Printf("入参:%d,闭包返回:%d \n", 3, a(3))
}
三、接口interface
package main
import "fmt"
// 1. 创建一个攻击接口,凡是具备攻击能力的游戏角色,统一由这个接口约束
type Attacker interface {
Attack(skill string)
}
// 2. 实现一个【战士】结构体
type Worrior struct {
Name string
}
// 3. 为【战士】结构体挂载方法
func (w Worrior) Attack(skill string) {
fmt.Println("战士:", w.Name, "施展了", skill, "技能进行攻击")
}
// 4. 封装【战士】工厂函数
func NewWorrior(name string) Worrior {
return Worrior{Name: name}
}
func main() {
//使用接口约定player,以确保所有player后续可以调用Attack,
//注意Go中的interface使用的是duck typing【鸭子辨型】,
//意思就是说未来我们为player赋值的时候,只要长得像player就行,
//并不需要跟接口的格式一模一样,可以让接口的运用更灵活
var player Attacker
player = NewWorrior("孙策")
player.Attack("飞船")
//player.Say("Hello") //此处会报错,因为Attacker接口上并没有Say方法
player = NewWorrior("刘备")
player.Attack("大喷子")
}
四、pacakge包
1. 目录结构
2. 包功能测试代码逻辑说明
假设我们正在开发一个图书管理系统,需要处理图书的借阅、归还等操作。我们可以将代码组织为多个包,以便更好地管理和复用代码。
3. 图书结构体
我们可以创建一个名为 book 的包来定义图书的结构体和相关函数,如下所示:
package book
import (
"GoGo/pkg/lib"
"fmt"
"log"
)
// 图书结构体
type Book struct {
Title string
Author string
Borrowed bool
}
// 借阅图书
func Borrow(b *Book) {
if b.Borrowed {
log.Printf("图书《%s》已经被借出", b.Title)
return
}
b.Borrowed = true
log.Printf("图书《%s》借阅成功", b.Title)
}
// 归还图书
func Return(b *Book) {
if !b.Borrowed {
log.Printf("图书《%s》尚未被借出", b.Title)
return
}
b.Borrowed = false
log.Printf("图书《%s》归还成功", b.Title)
}
// 打印图书信息
func PrintBookInfo(b *Book) {
fmt.Println("图书信息:")
fmt.Println("标题:", b.Title)
fmt.Println("作者:", b.Author)
if b.Borrowed {
fmt.Println("状态: 已借出")
} else {
fmt.Println("状态: 未借出")
}
}
// 记录借阅日志
func LogBorrowedBook(b *Book) {
lib.Log(fmt.Sprintf("借阅图书《%s》", b.Title))
}
// 记录归还日志
func LogReturnedBook(b *Book) {
lib.Log(fmt.Sprintf("归还图书《%s》", b.Title))
}
4. book引用的lib模块定义
在这个修改后的 book 包中,我们引入了一个名为 library 的包,用于记录图书的借阅和归还日志。
我们在 Book 结构体中添加了一个 Borrowed 字段,用于表示图书的借阅状态。然后,我们修改了 Borrow 和 Return 函数,以便在借阅和归还图书时更新借阅状态,并通过日志记录相应的操作。
此外,我们还添加了 PrintBookInfo 函数,用于打印图书的详细信息,包括借阅状态。
在 LogBorrowedBook 和 LogReturnedBook 函数中,我们调用了 library.Log 函数来记录借阅和归还的操作日志。
最后,我们需要创建一个名为 library 的包来定义日志记录的函数,如下所示:
package lib
import "fmt"
// 记录日志
func Log(message string) {
fmt.Println("图书馆日志:", message)
}
5. main包及逻辑
现在,我们可以在 main 包中使用 book 包来进行图书管理操作,并同时记录借阅和归还的日志,如下所示:
package main
import "GoGo/pkg/book"
func main() {
// 创建图书实例
bk := &book.Book{
Title: "The Great Gatsby",
Author: "F. Scott Fitzgerald",
}
// 借阅图书并记录日志
book.Borrow(bk)
book.LogBorrowedBook(bk)
// 再次尝试借阅相同的图书
book.Borrow(bk)
book.LogBorrowedBook(bk)
// 归还图书并记录日志
book.Return(bk)
book.LogReturnedBook(bk)
// 打印图书信息
book.PrintBookInfo(bk)
}
- 运行main包,查看结果
五、IO读写
1. 终端获取键盘输入
package main
import (
"bufio"
"fmt"
"os"
)
// 1. 基本键盘输入案例
func welcome() {
inputReader := bufio.NewReader(os.Stdin) //1. 创建标准键盘输入
fmt.Println("请输入您的姓名:")
input, err := inputReader.ReadString('\n') //2. 读取输入内容
if err != nil {
fmt.Println("输入错误请重试")
return
}
fmt.Printf("您的名字是%s", input) //3. 使用输入内容
switch input { //4. 判断输入内容
case "sanfeng\r\n":
fmt.Println("三丰,你要去闭关")
case "wuji\r\n":
fmt.Println("无忌,你要去练功")
default:
fmt.Println("一边玩儿去,拜拜")
}
//welcome()
}
// 练习:实现逆波兰式计数器
// https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/exercises/chapter_12/stack/stack_struct.go
func main() {
welcome()
}
2. 文件读写操作
package main
import (
"bufio"
"fmt"
"io"
"os"
)
// 1. 创建文件并写入内容
func writeFn() {
//1. 打开或创建文件
outputFile, outputError := os.OpenFile("test.txt", os.O_WRONLY|os.O_CREATE, 06666)
if outputError != nil {
fmt.Println("写入或创建文件时发生错误")
return
}
defer outputFile.Close() //2. 函数执行完毕后,关闭文件
outputWriter := bufio.NewWriter(outputFile) //3. 创建写入程序
for i := 0; i < 10; i++ {
text := fmt.Sprintf("第%d行,值为%d \n", i, i*2)
outputWriter.WriteString(text) //4. 写入内容【缓冲区】
}
outputWriter.Flush() //5.刷新写入内容
}
// 2. 读取指定文件内容
func readFn() {
inputFile, inputErr := os.Open("test.txt") //1.打开文件
if inputErr != nil {
fmt.Println("文件读取打开失败,文件路径错误或没有权限!")
return
}
defer inputFile.Close() //2.函数执行完毕后,关闭文件
inputReader := bufio.NewReader(inputFile) //3. 创建读程序
for {
readString, readErr := inputReader.ReadString('\n') //4. 逐行读取文件中的字符串
fmt.Println(readString)
if readErr == io.EOF { //5.读取结束的判断
fmt.Println("读取完毕")
return
}
}
}
// 3. 文件拷贝
func copyFn(dst string, src string) {
srcFile, srcErr := os.Open(src) //1. 打开被拷贝文件
if srcErr != nil {
fmt.Println("打开目标文件失败")
return
}
defer srcFile.Close() //2. 函数执行完毕后关闭文件
dstFile, desErr := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, 0666) //3.创建新文件
if desErr != nil {
fmt.Println("新文件创建失败")
return
}
defer dstFile.Close() //4. 使用完毕后,关闭新文件。切记加defer,否则会在拷贝前关闭,导致内容拷贝失败
bytesCopied, err := io.Copy(dstFile, srcFile) //5. 执行拷贝
if err != nil {
fmt.Println("文件内容复制失败", err)
return
}
fmt.Printf("已成功复制 %d 字节的文件内容。\n", bytesCopied)
}
func main() {
//writeFn()
//readFn()
copyFn("test1.txt", "test.txt")
}