GoLang读写数据---中

x33g5p2x  于2022-08-17 转载在 其他  
字(5.7k)|赞(0)|评价(0)|浏览(400)

文件拷贝

如何拷贝一个文件到另一个文件?最简单的方式就是使用 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)
}

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

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

flag 包

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

./demo -i 10 -b=true

关于如何将命令行的参数取出来,我们可以通过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 //默认值
}
  • 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","字符串")

实例:

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
}

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

用切片读写文件

切片提供了 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())
            }
        }
    }
}

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

如果结合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()
    }
}

使用接口的实际例子: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()
}

输出:

hello world! - unbuffered
hello world! - buffered

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

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

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

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

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)

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

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

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

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

相关文章