Golang学习 Day_12

本文介绍了Go语言中如何进行文件的一次性读取与写入,包括读取文件内容、写入文件、读写结合以及文件的打开模式。接着讨论了如何判断文件是否存在、拷贝文件以及统计文件中字符类型数量的方法。此外,还讲解了命令行参数处理和JSON的序列化与反序列化。最后,文章提到了Go语言的goroutine和channel,以及如何进行单元测试,强调了并发编程和测试的重要性。
摘要由CSDN通过智能技术生成

文件

一次性读取文件

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)

  1. name string:文件名字

  1. flag int:文件打开模式(可以组合)

  1. 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()
}
  1. 覆盖写 :file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666)

  1. 追加: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()函数返回的错误值进行判断:

  1. 如果返回的错误为nil,说明文件或文件夹存在

  1. 如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在

  1. 如果返回的错误为其他类型,则不确定是否存在

拷贝文件

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)
    }
}

缺点:

  1. 不方柏霓,我们需要在main函数中去调用,这样就需要去修改main函数,如果现在项目于正在运行,就可能去停止项目

  1. 不利于管理,因为当我们测试多个函数或者多个模块时,都需要写在main函数,不利于我们管理和清晰我们思路

  1. 引出单元测试。->testing 测试框架,可以很好解决问题

基本介绍

Go语言自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。

  1. 确保每个函数是可运行的,并且运行结果是正确的

  1. 确保写出来的代码性能是好的

  1. 单元测试能及时发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决。而性能测试的重点在与发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定

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) 执行正确")
}

总结

  1. 测试用例文件名必须以_test..go结尾

  1. 测试用例函数必须以Test开头,一般来说,就是Test+被测试的函数名

  1. TestXXX(t *testing.T)的形参类型必须是*testing.T

  1. 一个测试用例文件中,可以有多个测试用力函数

  1. 运行测试用例指令

  1. cmd>go test[如果运行正确,无日志,错误时,会输出日志]

  1. cmd>go test -v[运行正确或是错误,都输出日志]

  1. 当出现错误时们可以使用t.Fatalf来格式化输出错误信息,并退出程序

  1. t.Logf方法可以输出相应的日志

  1. 测试用例函数,并没有放在main函数中,也执行了

  1. PASS表示测试用例运行成功,FAIL表示测试用例运行失败

  1. 测试单个文件,一定要带上被测试的原文件 go test -v cal_test.go cal.go

  1. 测试单个方法 go test -v -test.run 函数名

goroutine(协程)和channel(管道)

goroutine

要求统计1-20000的数字中,那些是素数?

  1. 传统的方法,就是使用一个循环,循环的判断各个数是不是素数

  1. 使用并发或者并行的方式,将统计素数的任务分配给多个goroutine去完成,这时就会使用到goroutine

基本介绍

进程和线程的说明
  1. 进程就是程序在操作系统中的依次执行过程,是系统进行资源分配和调度的基本单位

  1. 线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位

  1. 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行

  1. 一个程序至少有一个进程,一个进程至少有以个线程

并发和并行
  1. 多线程程序在单核上运行,就是并发

  1. 多线程程序在多核上运行,就是并行

Go协程和Go主线程

  1. Go主线程:一个Go线程上,可以起多个协程,协程是轻量级的线程。

  1. Go协程的特点

  1. 有独立的栈空间

  1. 共享程序对空间

  1. 调度由用户控制

  1. 写成是轻量级的线程

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)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值