• GoLang读写数据---中



    文件拷贝

    如何拷贝一个文件到另一个文件?最简单的方式就是使用 io 包:

    // filecopy.go
    package main
    
    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注意 defer 的使用:当打开dst文件时发生了错误,那么 defer 仍然能够确保 src.Close() 执行。如果不这么做,src文件会一直保持打开状态并占用资源。


    命令行读取参数

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

    package main
    
    import (
    	"fmt"
    	"os"
    	"strings"
    )
    
    func main() {
    	var who string
    	if len(os.Args) > 1 {
    		who += strings.Join(os.Args[1:], ",")
    	}
    	fmt.Println("你好 ", who)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个命令行参数会放置在切片 os.Args[] 中(以空格分隔),从索引1开始(os.Args[0] 放的是程序本身的名字,在本例中是 os_args)。函数 strings.Join 以空格为间隔连接这些参数。

    在这里插入图片描述


    flag 包

    不管是在linux还是windows下,都支持在程序运行的情况下传递命令行参数。如:

    ./demo -i 10 -b=true
    
    • 1

    关于如何将命令行的参数取出来,我们可以通过os包来实现。

    通过range os.Args,我们可以取出所有的命令行参数,但是这种方法存在一定的局限性。这种方法没有将每一个参数的标志和其值映射起来(对于./demo -i 10 -b=true来说,-i是参数的标志,10是该标志的值)。

    flag包相比os提供的取命令行参数方法相比,flag包在取命令行参数时可以将每个标志和其值做映射,将特定标志的参数值放入我们期望的变量中。


    实例演示

    在 flag 包中有一个 Flag 被定义成一个含有如下字段的结构体:

    type Flag struct {
        Name     string // key
        Usage    string // 帮助信息
        Value    Value  // 用户输入的值
        DefValue string //默认值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • flag.Parse函数

    这个函数中做的内容比较多。取输入的命令行参数,在其中一一检测之前已绑定的检测标志。如果找到标志,将其值存入对应的变量。如果一切正常,往后执行。如果检测到不在待检测集合中的标志,则打印Usage信息,退出程序。

    flag.Arg(0) 就是第一个真实的 flag,而不是像 os.Args(0) 放置程序的名字。

    i.为检测标志指定存放变量系列函数
    
    ①StringVar(&val, "val","default val","usage note")StringInt(&val, "val",default val,"usage note")StringBool(&val, "val",default val,"usage note")StringVar(&val, "val","default val","usage note")为例,
    
    参数1&val,存放标志值的变量
    
    参数2"val",待检测的标志
    
    参数3:"default val",标志的默认值
    
    参数4"usage note",用法信息,当检测异常时打印该信息提示命令行参数的使用方法
    
    其他雷同
    
    ii.定义存放指定检测标志值的变量
    
    ①String("val","default val","usage note")Int("val",default val,"usage note")Bool("val",default val,"usage note")
    
    和i系列的区别点:
    
    s := flag.String("s","defalut val","字符串")
    
    等价于:
    
    var s string
    
    flag.StringVar(&s,"s","default val","字符串")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    实例:

    package main
    
    import (
    	"flag"
    	"fmt"
    )
    
    //用来存放命令行参数
    var (
    	name string
    	age  int
    	addr string
    )
    
    //flag参数初始化,将flag绑定各个存放命令行参数的变量
    func FlagInit() {
    	//我们需要通过flag检测命令行中的-name这个标志,那就需要告诉flag,1.需要取哪些标志;2.取出
    	//的标志放在哪里。通过flag包的StringVar、IntVar等函数就可以实现这种绑定。下同
    	flag.StringVar(&name, "name", "匿名", "你的姓名")
    	flag.IntVar(&age, "age", -1, "你的年龄")
    	flag.StringVar(&addr, "addr", "杭州", "你的地址")
    }
    func main() {
    	//在flag.Parse()之前必须先要做好绑定
    	FlagInit()
    	//Parse执行时,检测命令行中的各个标志。我们在FlagInit中已经绑定了name、age、addr这3个标
    	//志,Parse时就会从命令行参数中找这三个标志,并将对应的值保存在相应的变量中
    	flag.Parse()
    	fmt.Printf("%s你好,你的年龄是%d,你的地址是:%s\n", name, age, addr)
    	return
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述


    用 buffer 读取文件

    在下面的例子中,我们结合使用了缓冲读取文件和命令行 flag 解析这两项技术。如果不加参数,那么你输入什么屏幕就打印什么。

    参数被认为是文件名,如果文件存在的话就打印文件内容到屏幕。命令行执行 cat test 测试输出。

    package main
    import (
        "bufio"
        "flag"
        "fmt"
        "io"
        "os"
    )
    func cat(r *bufio.Reader) {
        for {
            buf, err := r.ReadBytes('\n')
            fmt.Fprintf(os.Stdout, "%s", buf)
            if err == io.EOF {
                break
            }
        }
        return
    }
    func main() {
        flag.Parse()
        if flag.NArg() == 0 {
            cat(bufio.NewReader(os.Stdin))
        }
        for i := 0; i < flag.NArg(); i++ {
            f, err := os.Open(flag.Arg(i))
            if err != nil {
                fmt.Fprintf(os.Stderr, "%s:error reading from %s: %s\n", os.Args[0], flag.Arg(i), err.Error())
                continue
            }
            cat(bufio.NewReader(f))
            f.Close()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    用切片读写文件

    切片提供了 Go 中处理 I/O 缓冲的标准方式,下面 cat 函数的第二版中,在一个切片缓冲内使用无限 for 循环(直到文件尾部 EOF)读取文件,并写入到标准输出(os.Stdout)。

    func cat(f *os.File) {
        const NBUF = 512
        var buf [NBUF]byte
        for {
            switch nr, err := f.Read(buf[:]);  {
            case nr < 0:
                fmt.Fprintf(os.Stderr, "cat: error reading: %s\n", err.Error())
                os.Exit(1)
            case nr == 0: // EOF
                return
            case nr > 0:
                //将读取到的数据写到控制台上,如果读取的字节数和写入到控制台上的字节数不一致,说明出现了错误
                if nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {
                    fmt.Fprintf(os.Stderr, "cat: error writing: %s\n", ew.Error())
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    对于函数的返回值含义,大家可以参考源码注释

    如果结合flag包使用,效果如下:

    package main
    import (
        "flag"
        "fmt"
        "os"
    )
    func cat(f *os.File) {
        const NBUF = 512
        var buf [NBUF]byte
        for {
            switch nr, err := f.Read(buf[:]); true {
            case nr < 0:
                fmt.Fprintf(os.Stderr, "cat: error reading: %s\n", err.Error())
                os.Exit(1)
            case nr == 0: // EOF
                return
            case nr > 0:
                if nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {
                    fmt.Fprintf(os.Stderr, "cat: error writing: %s\n", ew.Error())
                }
            }
        }
    }
    func main() {
        flag.Parse() // Scans the arg list and sets up flags
        if flag.NArg() == 0 {
            cat(os.Stdin)
        }
        for i := 0; i < flag.NArg(); i++ {
            f, err := os.Open(flag.Arg(i))
            if f == nil {
                fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err)
                os.Exit(1)
            }
            cat(f)
            f.Close()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    使用接口的实际例子:fmt.Fprintf

    // interfaces being used in the GO-package fmt
    package main
    
    import (
        "bufio"
        "fmt"
        "os"
    )
    
    func main() {
        // unbuffered
        fmt.Fprintf(os.Stdout, "%s\n", "hello world! - unbuffered")
        // buffered: os.Stdout implements io.Writer
        buf := bufio.NewWriter(os.Stdout)
        // and now so does buf.
        fmt.Fprintf(buf, "%s\n", "hello world! - buffered")
        buf.Flush()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出:

    hello world! - unbuffered
    hello world! - buffered
    
    • 1
    • 2

    下面是 fmt.Fprintf() 函数的实际签名

    func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
    
    • 1

    其不是写入一个文件,而是写入一个 io.Writer 接口类型的变量,下面是 Writer 接口在 io 包中的定义:

    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    
    • 1
    • 2
    • 3

    fmt.Fprintf() 依据指定的格式向第一个参数内写入字符串,第一个参数必须实现了 io.Writer 接口。Fprintf() 能够写入任何类型,只要其实现了 Write 方法,包括 os.Stdout,文件(例如 os.File),管道,网络连接,通道等等,同样的也可以使用 bufio 包中缓冲写入。bufio 包中定义了 type Writer struct{…}。

    bufio.Writer 实现了 Write 方法:

    func (b *Writer) Write(p []byte) (nn int, err error)
    
    • 1

    它还有一个工厂函数:传给它一个 io.Writer 类型的参数,它会返回一个带缓冲的 bufio.Writer 类型的 io.Writer:

    func NewWriter(wr io.Writer) (b *Writer)
    
    • 1

    其适合任何形式的缓冲写入。

    在缓冲写入的最后千万不要忘了使用 Flush(),否则最后的输出不会被写入。


  • 相关阅读:
    【HTML源码--一】:登录+蛋糕+照片+烟花;生日快乐、新年快乐、表白等
    Elasticsearch实战(二十一)---ES相关度分数评分优化及FunctionScore 自定义相关度分数算法
    Java设计模式 | 七大原则之合成复用原则
    一个完整的数据分析案例 | 用Python搞定用户预测分析
    我有点想用JDK17了
    渗透测试CTF-图片隐写的详细教程(干货)
    Java 阿里云存储OSS,上传文件,删除文件
    KingBase服务器参数配置(Kylin)
    Gin+getway+Fabric2.4.4演示
    Paas-容器安全
  • 原文地址:https://blog.csdn.net/m0_53157173/article/details/126360958