广州智能模板建站/最好的网站设计公司
GoLang读写数据---中
- 文件拷贝
- 从命令行读取参数
- flag 包
- 实例演示
- 用 buffer 读取文件
- 用切片读写文件
- 使用接口的实际例子:fmt.Fprintf
文件拷贝
如何拷贝一个文件到另一个文件?最简单的方式就是使用 io 包:
// filecopy.go
package mainimport ("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 mainimport ("fmt""os""strings"
)func main() {var who stringif 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 // keyUsage 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 stringflag.StringVar(&s,"s","default val","字符串")
实例:
package mainimport ("flag""fmt"
)//用来存放命令行参数
var (name stringage intaddr 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 = 512var buf [NBUF]bytefor {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: // EOFreturncase 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 = 512var buf [NBUF]bytefor {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: // EOFreturncase 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 flagsif 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 mainimport ("bufio""fmt""os"
)func main() {// unbufferedfmt.Fprintf(os.Stdout, "%s\n", "hello world! - unbuffered")// buffered: os.Stdout implements io.Writerbuf := 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(),否则最后的输出不会被写入。