Go语言7

栏目: Go · 发布时间: 5年前

内容简介:操作终端相关文件句柄常量:这个是fmt包里的一个方法,打印到文件。比平时用的fmt打印多一个参数,这个参数接收的就是文件句柄,一个实现了把终端的标准输出的文件句柄传入,就是打印到标准输出,即屏幕:

终端读写

操作终端相关文件句柄常量:

os.Stdin
os.Stdout
os.Stderr

这个是fmt包里的一个方法,打印到文件。比平时用的fmt打印多一个参数,这个参数接收的就是文件句柄,一个实现了 io.Winter 的接口:

func Fprint(w io.Writer, a ...interface{}) (n int, err error)

把终端的标准输出的文件句柄传入,就是打印到标准输出,即屏幕:

package main

import (
    "os"
    "fmt"
)

func main(){
    fmt.Fprintln(os.Stdout, "TEST")
}

终端输入

先打印提示信息,然后获取用户输入的值,最后打印出来:

package main

import "fmt"

var firstName, lastName string

func main(){
    fmt.Print("Please enter your full name:")
    fmt.Scanln(&firstName, &lastName)
    // fmt.Scanf("%s %s", &firstName, &lastName)  // 和上面那句效果一样
    fmt.Printf("Hi %s %s.\n", firstName, lastName)
}

把字符串作为格式的化输入

使用 fmt 包里的 Sscanf()方法:

func Sscanf(str string, format string, a ...interface{}) (n int, err error)

Scanf 扫描实参 string,并将连续由空格分隔的值存储为连续的实参, 其格式由 format 决定。它返回成功解析的条目数。

不是很好理解的话,参考下下面的例子:

package main

import "fmt"

func main(){
    var (
        input = "12.34 567 Golang"  // 要扫描的字符串
        format = "%f %d %s"  // 每段字符串的格式
        i float32  // 对应格式的变量,把字符串里的每一段赋值到这些变量里
        j int
        k string
    )
    fmt.Sscanf(input, format, &i, &j, &k)
    fmt.Println(i)
    fmt.Println(j)
    fmt.Println(k)
}

带缓冲区的读写

不直接操作 io,在缓冲区里进行读写,io的操作交由操作系统处理,主要是解决性能的问题。

这里要使用 bufio 包,下面是缓冲区进行读操作的示例:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    var inputReader *bufio.Reader  // bufio包里的一个结构体类型
    // 给上面的结构体赋值,包里提供了构造函数
    inputReader = bufio.NewReader(os.Stdin)  // 生成实例,之后要调用里面的方法
    fmt.Print("请随意输入内容: ")
    // 调用实例的方法进行读操作,就是带缓冲区的操作了
    input, err := inputReader.ReadString('\n')  // 这里是字符类型
    if err == nil {
        fmt.Println(input)
    }
}

上面是从终端读取,文件读写下面会讲,先来个从文件读取的示例:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "io"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("ERROR:", err)
        return
    }
    defer file.Close()  // 函数返回时关闭文件
    bufReader := bufio.NewReader(file)
    for {
        line, err := bufReader.ReadString('\n')
        // 最后一行会同时返回 line 和 err,所以先打印
        fmt.Println(strings.TrimSpace(line))
        if err != nil {
            if err == io.EOF {
                fmt.Println("读取完毕")
                break
            } else {
                fmt.Println("读取文件错误:", err)
                return
            }
        }
    }
}

文件读写

os.File 是个结构体,封装了所有文件相关的操作。之前讲的 os.Stdin、os.Stdout、os.Stderr 都是文件句柄,都是 *os.File

读取整个文件

"io/ioutil" 可以直接把整个文件读取出来,适合文件不是很大的情况:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    buf, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Println("ERROR", err)
        return
    }
    fmt.Println(string(buf))  // buf是[]byte类型,要转字符串
    // 写操作
    err = ioutil.WriteFile("wtest.txt", buf, 0x64)
    if err != nil {
        panic(err.Error())
    }
}

上面还有整个文件写入的操作。

读取压缩文件

下面的代码是解压读取一个 .gz 文件,注意不是 .tar.gz 。打了tar包应该是不行的:

package main

import (
    "compress/gzip"
    "os"
    "fmt"
    "bufio"
    "io"
    "strings"
)

func main() {
    fileName := "test.gz"
    var reader *bufio.Reader
    file, err := os.Open(fileName)
    if err != nil {
        fmt.Println("Open ERROE:", err)
        os.Exit(1)
    }
    defer file.Close()  // 记得关文件
    gzFile, err := gzip.NewReader(file)
    if err != nil {
        fmt.Println("gz ERROR:", err)
        return
    }
    reader = bufio.NewReader(gzFile)
    for {
        line, err := reader.ReadString('\n')
        fmt.Println(strings.TrimSpace(line))
        if err != nil {
            if err == io.EOF {
                fmt.Println("读取完毕")
                break
            } else {
                fmt.Println("Read ERROR:", err)
                os.Exit(0)
            }
        }
    }
}

文件写入

写入文件的命令:

os.OpenFile("output.dat", os.O_WRONLY|os.O_CREATE, 0666)

第二个参数是文件打开模式:

  • os.O_WRONLY : 只写
  • os.O_CREATE : 创建文件
  • os.O_RDONLY : 只读
  • os.O_RDWR : 读写
  • os.O_TRUNC : 清空

第三个参数是权限控制,同 Linux 的ugo权限。

文件写入的示例:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func main() {
    outputFile, err := os.OpenFile("test.txt", os.O_WRONLY|os.O_CREATE, 0666)
    if err != nil {
        fmt.Println("ERROR", err)
        return
    }
    defer outputFile.Close()
    outputWriter := bufio.NewWriter(outputFile)
    outputString := "Hello World! "
    for i := 0; i < 10; i++ {
        outputWriter.WriteString(outputString + strconv.Itoa(i) + "\n")
    }
    outputWriter.Flush()  // 强制刷新,保存到磁盘
}

拷贝文件

首先分别打开2个文件,然后拷贝文件只要一次调用传入2个文件句柄就完成了:

package main

import (
    "io"
    "fmt"
    "os"
)

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        fmt.Println("Open ERROR", err)
        return
    }
    defer src.Close()
    dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
    if err != nil {
        fmt.Println("OpenFile ERROR", err)
        return
    }
    defer dst.Close()
    // 先依次把2个文件都打开,然后拷贝只要下面这一句
    return io.Copy(dst, src)
}

func main() {
    CopyFile("test_copy.txt", "test.txt")
    fmt.Println("文件拷贝完成")
}

命令行参数

os.Args 是一个 string 的切片,用来存储所有的命令行参数。

package main

import (
    "os"
    "fmt"
)

func main() {
    fmt.Println(len(os.Args))
    for i, v := range os.Args {
        fmt.Println(i, v)
    }
}

/* 执行结果
PS H:\Go\src\go_dev\day7\args\beginning> go run main.go arg1 arg2 arg3
4
0 [省略敏感信息]\main.exe
1 arg1
2 arg2
3 arg3
PS H:\Go\src\go_dev\day7\args\beginning>
*/

os.Args 至少有一个元素,如果一个参数也不打,第一个元素就是命令本身。之后的命令行参数从下标1开始存储。

解析命令行参数

flag 包实现命令行标签解析。

func BoolVar(p *bool, name string, value bool, usage string)
func StringVar(p *string, name string, value string, usage string)
func IntVar(p *int, name string, value int, usage string)

第一个参数是个指针,指向要接收的参数的值

第二个参数是指定的名字

第三个参数是默认值

第四个参数是用法说明

用法示例:

package main

import (
    "fmt"
    "flag"
)

func main() {
    var (
        enable bool
        conf string
        num int
    )
    flag.BoolVar(&enable, "b", false, "是否启用")
    flag.StringVar(&conf, "s", "test.conf", "配置文件")
    flag.IntVar(&num, "i", 0, "数量")
    flag.Parse()  // 读取命令行参数进行解析
    fmt.Println(enable, conf, num)
}

/* 执行结果
PS H:\Go\src\go_dev\day7\args\flag_var> go run main.go
false test.conf 0
PS H:\Go\src\go_dev\day7\args\flag_var> go run main.go -b -s default.conf -i 10
true default.conf 10
PS H:\Go\src\go_dev\day7\args\flag_var>
*/

Json数据协议

导入包

import "encoding/json"

序列化

json.Marshal(data interface{})

示例:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    UserName string  `json:"username"`
    NickName string  `json:"nickname"`
    Age int  `json:"age"`
    Vip bool  `json:"vip"`
}

func main() {
    u1 := &User{
        UserName: "Sara",
        NickName: "White Canary",
        Age: 29,
        Vip: true,
    }
    if data, err := json.Marshal(u1); err == nil{
        fmt.Println(string(data))
    }
}

反序列化

json.Unmarshal(data []byte, v interface{})

示例:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    UserName string  `json:"username"`
    NickName string  `json:"nickname"`
    Age int  `json:"age"`
    Vip bool  `json:"vip"`
}

func main() {
    var jsonStr = `{
        "username": "Kara",
        "nickname": "Supergirl",
        "age": 20,
        "vip": false
        }`
    var jsonByte = []byte(jsonStr)
    var u2 User
    if err := json.Unmarshal(jsonByte, &u2); err == nil {
        fmt.Println(u2)
    } else {
        fmt.Println("ERROR:", err)
    }
}

错误处理

error 类型是在 builtin 包里定义的。error 是个接口,里面实现了一个 Error() 的方法,返回一个字符串:

type error interface {
    Error() string
}

所以其实 error 也就是个字符串信息。

定义错误

error 包实现了用于错误处理的函数。

New 返回一个按给定文本格式化的错误:

package main

import (
    "errors"
    "fmt"
)

var errNotFound error = errors.New("Not found error")

func main() {
    fmt.Println("ERROR:", errNotFound)
}

平时简单这样用用就可以了,也很方便。不过学习嘛,下面稍微再深入点。

自定义错误

主要是学习,上面的 New() 函数用起来更加方便。

使用自定义错误返回:

package main

import (
    "fmt"
    "os"
)

type PathError struct {
    Op string
    Path string
    err string  // 把这个信息隐藏起来,所以是小写
}

// 实现error的接口
func (e *PathError) Error() string {
    return e.Op + " " + e.Path + " 路径不存在\n原始错误信息: " + e.err
}

func Open(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return &PathError{
            Op: "read",
            Path: filename,
            err: err.Error(),
        }
    }
    defer file.Close()
    return nil
}

func main() {
    err := Open("test.txt")
    if err != nil {
        fmt.Println(err)
    }
}

/* 执行结果
PS H:\Go\src\go_dev\day7\error\diy_error> go run main.go
read test.txt 路径不存在
原始错误信息: open test.txt: The system cannot find the file specified.
PS H:\Go\src\go_dev\day7\error\diy_error>
*/

判断自定义错误

这里用 switch 来判断:

switch err := err.(type) {
case ParseError:
    PrintParseError(err)
case.PathError:
    PrintPathError(err)
default:
    fmt.Println(err)
}

异常和捕捉

首先调用 panic 来抛出异常:

package main

func badCall() {
    panic("bad end")
}

func main() {
    badCall()
}

/* 执行结果
PS H:\Go\src\go_dev\day7\error\panic> go run main.go
panic: bad end

goroutine 1 [running]:
main.badCall()
        H:/Go/src/go_dev/day7/error/panic/main.go:4 +0x40
main.main()
        H:/Go/src/go_dev/day7/error/panic/main.go:8 +0x27
exit status 2
PS H:\Go\src\go_dev\day7\error\panic>
*/

执行后就抛出异常了,但是这样程序也崩溃了。

下面来捕获异常,go里没有try之类来捕获异常,所以panic了就是真的异常了,但是还不会马上就崩溃。panic的函数并不会立刻返回,而是先defer,再返回。如果有办法将panic捕获到,并阻止panic传递,就正常处理,如果没有没有捕获,程序直接异常终止。 这里并不是像别的语言里那样捕获异常,因为即使捕获到了,也只是执行defer,之后还是要异常终止的,而不是继续在错误的点往下执行。

注意:就像上面说的,在 go 里panic了就是真的异常了。recover之后,逻辑并不会恢复到panic那个点去,函数还是会在defer之后返回。

下面是使用 defer 处理异常的示例:

package main

import "fmt"

func badCall() {
    panic("bad end")
}

func test() {
    // 用defer在最后捕获异常
    defer func() {
        if e := recover(); e != nil {
            fmt.Println("Panic", e)
        }
    }()
    badCall()
}

func main() {
    test()
}

所以像 python 里的 try except 那样捕获异常,在go里,大概就是返回个 err(error类型) ,然后判断一下 err 是不是 nil。

课后作业

实现一个图书管理系统v3,增加一下功能:

  • 增加持久化存储的功能
  • 增加日志记录的功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

软件测试的艺术

软件测试的艺术

梅尔斯 / 机械工业出版社 / 2006年01月 / 22.0

《软件测试的艺术》(原书第2版)成功、有效地进行软件测试的实用策略和技术:    基本的测试原理和策略      验收测试    程序检查和走查         安装测试    代码检查            模块(单元)测试    错误列表            测试规划与控制    同行评分            独立测试机构    黑盒、白盒测试    ......一起来看看 《软件测试的艺术》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具