[golang] 读写数据

101 阅读4分钟

读取用户的输入

从键盘和标准输入 os.Stdin 读取输入,最简单的办法是使用 fmt 包提供的 ScanSscan 开头的函数。

import "fmt"

var (
    firstName, lastName, s string
    i                      int
    f                      float32
    input                  = "56.12 / 5212 / Go"
    format                 = "%f / %d / %s"
)

func main() {
    fmt.Println("Please enter your full name: ")
    fmt.Scanln(&firstName, &lastName)
    fmt.Printf("Hi firstName: %s, lastName: %s!\n", firstName, lastName)
    fmt.Sscanf(input, format, &f, &i, &s)
    fmt.Println("From the string we read: ", f, i, s)
}
Please enter your full name: 
eric wang
Hi firstName: eric, lastName: wang!
From the string we read:  56.12 5212 Go

Scanln 扫描来自标准输入的文本,将空格分隔的值依次存放到后续的参数内,直到碰到换行。Sscan 和以 Sscan 开头的函数则是从字符串读取文本。

bufio

也可以使用 bufio 包提供的缓冲读取(buffered reader)来读取数据。

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

var (
    inputReader *bufio.Reader
    input       string
    err         error
)
inputReader = bufio.NewReader(os.Stdin)
fmt.Println("Please enter some input: ")
input, err = inputReader.ReadString('\n')
if err == nil {
    fmt.Printf("The input was: %s\n", input)
}
Please enter some input: 
this is input!
The input was: this is input!

读取器对象提供 ReadString(delim byte) 从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。ReadString 返回读取到的字符串,如果碰到错误则返回 nil。如果它一直读到文件结束,则返回读取到的字符串和 io.EOF。如果读取过程中没有碰到 delim 字符,将返回错误 err != nil

文件读写

读取文件

利用 ioutil.ReadFile 直接从文件读取到 []byte 中。

import (
    "fmt"
    "io/ioutil"
)

func main() {
    f, err := ioutil.ReadFile("file/test")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(f))
}

先从文件读取到 file 中,再从 file 读取到 bufbuf 再追加到最终的 []byte

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

func main() {
    // 获得一个 file
    f, err := os.Open("file/test")
    if err != nil {
        panic(err)
    }

    // 把 file 读取到缓冲区中
    defer f.Close()
    var chunk []byte
    buf := make([]byte, 1024)

    for {
        // 从 file 读取到 buf 中
        n, err := f.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        // 说明读取结束
        if n == 0 {
            break
        }
        // 读取到最终的缓冲区中
        chunk = append(chunk, buf[:n]...)
    }

    fmt.Println(string(chunk))
}

先从文件读取到 file 中,再从 file 读取到 Reader 中,从 Reader 读取到 bufbuf 最终追加到 []byte

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

func main() {
    fi, err := os.Open("file/test")
    if err != nil {
        panic(err)
    }
    defer fi.Close()

    r := bufio.NewReader(fi)
    var chunks []byte

    buf := make([]byte, 1024)

    for {
        n, err := r.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }
        chunks = append(chunks, buf...)
    }

    fmt.Println(string(chunks))
    
    for {
        line, err := r.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                fmt.Println("File read ok!")
                break
            } else {
                panic(err)
            }
        }
        line = strings.TrimSpace(line)
        fmt.Println(line)
    }
}

读取到 file 中,再利用 ioutilfile 直接读取到 []byte 中。

import (
    "fmt"
    "io/ioutil"
    "os"
)

func main() {
    f, err := os.Open("file/test")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    fd, err := ioutil.ReadAll(f)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(fd))
}

使用 fmt 包提供的以 FScan 开头的一系列函数来读取文件。

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    var col1, col2, col3 []string
    for {
        var v1, v2, v3 string
        _, err := fmt.Fscanln(file, &v1, &v2, &v3)
        // scans until newline
        if err != nil {
            break
        }
        col1 = append(col1, v1)
        col2 = append(col2, v2)
        col3 = append(col3, v3)
    }

    fmt.Println(col1)
    fmt.Println(col2)
    fmt.Println(col3)
}
[ABC FUNC GO]
[40 56 45]
[150 280 356]

写入文件

使用 io.WriteString 写入文件。

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

func main() {
    fileName := "file/test"
    strTest := "测试测试"

    var f *os.File
    var err error

    if CheckFileExist(fileName) { // 文件存在
        f, err = os.OpenFile(fileName, os.O_APPEND, 0666) // 打开文件
        if err != nil {
            panic(err)
        }
    } else { // 文件不存在
        f, err = os.Create(fileName) // 创建文件
        if err != nil {
            panic(err)
        }
    }
    // 将文件写进去
    n, err := io.WriteString(f, strTest)
    if err != nil {
        panic(err)
    }
    fmt.Println("写入的字节数是:", n)
}

func CheckFileExist(fileName string) bool {
    _, err := os.Stat(fileName)
    return os.IsNotExist(err)
}

使用 ioutil.WriteFile 写入文件。

import (
    "fmt"
    "io/ioutil"
)

func main() {
    fileName := "file/test"
    strTest := "测试测试"
    var d = []byte(strTest)
    err := ioutil.WriteFile(fileName, d, 0666)
    if err != nil {
        panic(err)
    }
    fmt.Println("write success")
}

使用 File(Write, WriteString) 写入文件。

import (
    "fmt"
    "os"
)

func main() {
    fileName := "file/test"
    strTest := "测试测试"
    var d1 = []byte(strTest)

    f, err := os.Create(fileName) // 创建文件
    if err != nil {
        panic(err)
    }
    defer f.Close()

    n1, err := f.Write(d1) // 写入文件(字节数组)
    if err != nil {
        panic(err)
    }
    fmt.Printf("写入 %d 个字节", n1)

    n2, err := f.WriteString("writesn") // 写入文件(字符串)
    if err != nil {
        panic(err)
    }
    fmt.Printf("写入 %d 个字节", n2)

    f.Sync()
}

使用 bufio.NewWriter 写入文件。

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

func main() {
    fileName := "file/test"
    f, err := os.Create(fileName) // 创建文件
    if err != nil {
        panic(err)
    }
    defer f.Close()

    w := bufio.NewWriter(f) // 创建新的 Writer 对象
    n, err := w.WriteString("bufferedn")
    if err != nil {
        panic(err)
    }
    fmt.Printf("写入 %d 个字节", n)
    w.Flush()
}

复制文件

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

func main() {
    CopyFile("target.txt", "source.txt")
    fmt.Println("Copy done!")
}

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

从命令行读取参数

os

os 包中有一个 string 类型的切片变量 os.Args,用来处理一些基本的命令行参数,它在程序启动后读取命令行输入的参数。

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    who := "Alice "
    if len(os.Args) > 1 {
        who += strings.Join(os.Args[1:], " ")
    }
    fmt.Println("Good Morning", who)
}

命令行参数会放置在切片 os.Args[] 中(以空格分隔),从索引 1 开始(os.Args[0] 放的是程序本身的名字)。

flag

import (
    "flag"
    "os"
)

var NewLine = flag.Bool("n", false, "print newline") // echo -n flag, of type *bool

const (
    Space   = " "
    Newline = "\n"
)

func main() {
    flag.PrintDefaults()
    flag.Parse() // Scans the arg list and sets up flags
    var s string = ""
    for i := 0; i < flag.NArg(); i++ {
        if i > 0 {
            s += Space
            if *NewLine { // -n is parsed, flag becomes true
                s += Newline
            }
        }
        s += flag.Arg(i)
    }
    os.Stdout.WriteString(s)
}
go run main.go -n=true eric wang       
  -n    print newline
eric
wang

flag.Parse() 扫描参数列表(或者常量列表)并设置 flagflag.Arg(i) 表示第 i 个参数。Parse() 之后 flag.Arg(i) 全部可用,flag.Arg(0) 就是第一个真实的 flag,而不是像 os.Args(0) 放置程序的名字。
flag.Bool() 定义了一个默认值是 false 的 flag,当在命令行出现第一个符合的参数,flag 被设置成 true。flag 被解引用到 *NewLine,所以当值是 true 时将添加一个 Newline
flag.PrintDefaults() 打印 flag 的使用帮助信息。