文件
一次性读取文件
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 使用ioutil.ReadFile一次性将文件读取到位
file := "F:/goproject/src/go_file/test.txt"
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Printf("read file err=%v", err)
}
// 把读取到的内容显示在终端
fmt.Printf("%v", string(content)) // []byte
// 因为,我们没有显式的Open文件,因此也不需要显式的Close文件
// 因为,文件的Open和Close被封装到 ReadFiles 函数内部
}
写文件的操作
基本介绍
func OpenFile(name string,flag int,perm FileMode)(file *File,err error)
name string:文件名字
flag int:文件打开模式(可以组合)
perm FileMode:权限控制
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// 创建新文件,写入内容
filePath := "F:/goproject/src/go_file/test01.txt"
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err = %v\n", err)
return
}
defer file.Close()
str := "hello Golang\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString(str)
}
// 因为writer是带缓存的,因此在调用WriterString方法是,内容是先写入缓存的
writer.Flush()
}
覆盖写 :file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666)
追加:file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666)
3.读写
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 创建新文件,写入内容
filePath := "F:/goproject/src/go_file/test01.txt"
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("open file err = %v\n", err)
return
}
defer file.Close()
// 先读取原来文件中的内容,并显示到终端
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
}
fmt.Print(str)
}
str := "abcabc\r\n"
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
writer.WriteString(str)
}
// 因为writer是带缓存的,因此在调用WriterString方法是,内容是先写入缓存的
writer.Flush()
}
判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
如果返回的错误为nil,说明文件或文件夹存在
如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
如果返回的错误为其他类型,则不确定是否存在
拷贝文件
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) {
srcfile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
}
defer srcfile.Close()
reader := bufio.NewReader(srcfile)
dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
writer := bufio.NewWriter(dstFile)
defer dstFile.Close()
return io.Copy(writer, reader)
}
func main() {
srcFile := "C:/Users/28160/Desktop/1.jpg"
dstFile := "F:/goproject/src/go_file/abc.jpg"
_, err := CopyFile(dstFile, srcFile)
if err == nil {
fmt.Println("拷贝完成")
} else {
fmt.Printf("拷贝错误, err=%v", err)
return
}
}
文件编程应用
统计英文、数字、空格和其他字符数量
package main
import (
"bufio"
"fmt"
"io"
"os"
)
type CharCount struct {
chCount int
NumCount int
SpaceCount int
OtherCount int
}
func main() {
fileName := "F:/goproject/src/go_file/test03.txt"
file, err := os.Open(fileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer file.Close()
var count CharCount
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
}
for _, v := range str {
switch {
case v >= 'a' && v <= 'z':
fallthrough
case v > 'A' && v <= 'z':
count.chCount++
case v == ' ' || v == '\t':
count.SpaceCount++
case v >= '0' && v <= '9':
count.NumCount++
default:
count.OtherCount++
}
}
}
fmt.Printf("字符的个数为=%v \n数字的个数为=%v \n空格的个数为=%v \n其他字符的个数为=%v", count.chCount, count.NumCount, count.SpaceCount, count.OtherCount)
}
命令行参数
基本介绍
os.Args是一个string的切片,用来存储所有的命令行参数
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("命令行的参数有", len(os.Args))
for i, v := range os.Args {
fmt.Printf("args[%v]=%v\n", i, v)
}
}
flag包解析
package main
import (
"flag"
"fmt"
)
func main() {
// 定义几个变量,用于接收命令行的参数
var user string
var pwd string
var host string
var port int
flag.StringVar(&user, "u", "", "用户名,默认为空")
flag.StringVar(&pwd, "pwd", "", "密码,默认为空")
flag.StringVar(&host, "h", "localhost", "主机名,默认为localhost")
flag.IntVar(&port, "port", 3306, "端口号,默认为3306")
flag.Parse()
fmt.Printf("user=%v \npwd=%v \nhost=%v \n port=%v", user, pwd, host, port)
}
json基本介绍
json(javaScript Object Notation)是一种轻量级的数据交换格式。已于人阅读和编写。同时也是易于机器解析和生成。
JSON易于机器解析和生成,并有效地提升网络传输效率,通常程序在网络传输时会先将数据(结构体、map等)序列化成json字符串,到接收方得到json字符串时,在反序列化恢复成原来的数据类型。
json数据格式说明
json键值对是用来保存数据的一种方式
键/值对组合中键名写在前面并用双引号""包裹,使用冒号 : 分隔,然后紧接着值
在线验证json格式网站:https://www.json.cn/
json序列化
json序列化是指,将有key-value结构的数据理性序列化成json的操作
package main
import (
"encoding/json"
"fmt"
)
// 定义一个结构体
type Monster struct {
Name string
Age int
Birthday string
Sal float64
Skill string
}
func testStruct() {
monster := Monster{
Name: "牛魔王",
Age: 500,
Birthday: "2011-11-11",
Sal: 80000.0,
Skill: "牛魔拳",
}
// 将monster序列化
data, err := json.Marshal(&monster)
if err != nil {
fmt.Printf("序列号错误 err=%v\n", err)
}
// 输出序列化后的结果
fmt.Printf("monster序列化后=%v", string(data))
}
// 将 map序列化
func testMAp() {
// var a map[string]interface{}
a := make(map[string]interface{})
a["name"] = "红孩儿"
a["age"] = 30
a["address"] = "洪崖洞"
data, err := json.Marshal(a)
if err != nil {
fmt.Printf("序列号错误 err=%v\n", err)
}
// 输出序列化后的结果
fmt.Printf("a序列化后=%v", string(data))
}
func testSlice() {
var slice []map[string]interface{}
m1 := make(map[string]interface{})
m1["name"] = "jack"
m1["age"] = 17
m1["address"] = "北京"
slice = append(slice, m1)
m2 := make(map[string]interface{})
m2["name"] = "tom"
m2["age"] = 18
m2["address"] = "上海"
slice = append(slice, m2)
data, err := json.Marshal(slice)
if err != nil {
fmt.Printf("序列号错误 err=%v\n", err)
}
// 输出序列化后的结果
fmt.Printf("a序列化后=%v", string(data))
}
func main() {
testStruct()
testMAp()
testSlice()
}
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_name"`
Birthday string
Sal float64
Skill string
}
json反序列化
json反序列化是指,将json字符串反序列化成对应的数据类型
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_name"`
Birthday string
Sal float64
Skill string
}
func unmarshalStruct() {
str := `{"name":"牛魔王","monster_name":500,"Birthday":"2011-11-11","Sal":80000,"Skill":"牛魔拳"}`
// 定义monster实例
var monster Monster
err := json.Unmarshal([]byte(str), &monster)
if err != nil {
fmt.Printf("unmarshal err= %v\n", err)
}
fmt.Printf("反序列后 monster=%v", monster)
}
func unmarshalMap() {
str := `{"address":"洪崖洞","age":30,"name":"红孩儿"}`
var a map[string]interface{}
err := json.Unmarshal([]byte(str), &a)
if err != nil {
fmt.Printf("unmarshal err= %v\n", err)
}
fmt.Printf("反序列后 a=%v", a)
}
func main() {
unmarshalStruct()
unmarshalMap()
}
单元测试
传统方法测试
package main
import "fmt"
func addUpper(n int) int {
res := 0
for i := 1; i <= n; i++ {
res += i
}
return res
}
func main() {
res := addUpper(30)
if res != 55 {
fmt.Printf("addUpper错误 返回值=%v 期望值=%v\n", res, 55)
} else {
fmt.Printf("addUpper正确 返回值=%v 期望值=%v\n", res, 55)
}
}
缺点:
不方柏霓,我们需要在main函数中去调用,这样就需要去修改main函数,如果现在项目于正在运行,就可能去停止项目
不利于管理,因为当我们测试多个函数或者多个模块时,都需要写在main函数,不利于我们管理和清晰我们思路
引出单元测试。->testing 测试框架,可以很好解决问题
基本介绍
Go语言自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。
确保每个函数是可运行的,并且运行结果是正确的
确保写出来的代码性能是好的
单元测试能及时发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决。而性能测试的重点在与发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定
package main
func AddUpper(n int) int {
res := 0
for i := 1; i <= n; i++ {
res += i
}
return res
}
func GetSub(n1 int, n2 int) int {
return n1 - n2
}
package main
import (
"testing"
)
func TestGetSub(t *testing.T) {
// 调用
res := GetSub(10, 3)
if res != 7 {
// fmt.Printf("addUpper错误 返回值=%v 期望值=%v\n", res, 55)
t.Fatalf("AddUpper错误 返回值=%v 期望值=%v\n", res, 55)
}
// 如果正确,输出日志
t.Logf("AddUpper(10) 执行正确")
}
package main
import (
"testing"
)
func TestAddUpper(t *testing.T) {
// 调用
res := AddUpper(10)
if res != 55 {
// fmt.Printf("addUpper错误 返回值=%v 期望值=%v\n", res, 55)
t.Fatalf("AddUpper错误 返回值=%v 期望值=%v\n", res, 55)
}
// 如果正确,输出日志
t.Logf("AddUpper(10) 执行正确")
}
总结
测试用例文件名必须以_test..go结尾
测试用例函数必须以Test开头,一般来说,就是Test+被测试的函数名
TestXXX(t *testing.T)的形参类型必须是*testing.T
一个测试用例文件中,可以有多个测试用力函数
运行测试用例指令
cmd>go test[如果运行正确,无日志,错误时,会输出日志]
cmd>go test -v[运行正确或是错误,都输出日志]
当出现错误时们可以使用t.Fatalf来格式化输出错误信息,并退出程序
t.Logf方法可以输出相应的日志
测试用例函数,并没有放在main函数中,也执行了
PASS表示测试用例运行成功,FAIL表示测试用例运行失败
测试单个文件,一定要带上被测试的原文件 go test -v cal_test.go cal.go
测试单个方法 go test -v -test.run 函数名
goroutine(协程)和channel(管道)
goroutine
要求统计1-20000的数字中,那些是素数?
传统的方法,就是使用一个循环,循环的判断各个数是不是素数
使用并发或者并行的方式,将统计素数的任务分配给多个goroutine去完成,这时就会使用到goroutine
基本介绍
进程和线程的说明
进程就是程序在操作系统中的依次执行过程,是系统进行资源分配和调度的基本单位
线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位
一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行
一个程序至少有一个进程,一个进程至少有以个线程
并发和并行
多线程程序在单核上运行,就是并发
多线程程序在多核上运行,就是并行
Go协程和Go主线程
Go主线程:一个Go线程上,可以起多个协程,协程是轻量级的线程。
Go协程的特点
有独立的栈空间
共享程序对空间
调度由用户控制
写成是轻量级的线程
package main
import (
"fmt"
"strconv"
"time"
)
func test() {
for i := 1; i <= 10; i++ {
fmt.Println("hello world" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main() {
go test()
for i := 1; i <= 10; i++ {
fmt.Println("main() hello golang" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
设置Golang运行的cpu数
package main
import (
"fmt"
"runtime"
)
func main() {
cpuNum := runtime.NumCPU()
fmt.Println(cpuNum)
runtime.GOMAXPROCS(cpuNum - 1)
fmt.Println("ok~n")
}
channel(管道)
channel是在Goroutine之间进行同步的主要方法。在无缓存的通道上的每一次发送操作都有与其对应的接收操作相匹配,发送和接受操作通常发生在不同的Goroutine上。
(在同一个goroutine上执行两个操作很容易导致死锁)
package main
var done = make(chan bool)
var msg string
func aGoroutine() {
msg = "hello world"
done <- true
}
func main() {
go aGoroutine()
<-done
println(msg)
}