操作文件是任何编程语言都绕不过,要掌握一门语言,知道如何操作文件是必不可少的,今天学习了下golang对文件操作的支持。
golang对文件的支持是在os package里。我无意将本文写成官方文档的模样,我只是想讨论如何利用这些接口操作文件。
OPEN
熟悉文件系统的人都知道,open是整个文件系统中最复杂的接口之一。熟悉C语言的都知道,C语言中有open和creat,接口如下:
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
-
int open(const char *pathname, int flags);
-
int open(const char *pathname, int flags, mode_t mode);
-
-
int creat(const char *pathname, mode_t mode)
对C的open而言,如果flag里面有了O_CREAT,那么必须带上mode参数,制定创建文件时的perm,如果文件已经存在了,这个O_CREAT标志就无效了(除非O_EXCL标志被指定。 除了O_CREAT,还有很多的标志
O_RDONLY
O_WRONLY
O_RDWR
O_DIRECT
O_APPEND
O_TRUNC
。。。。
这些标志位基本是顾名思义,对于open这种很复杂很综合的文件操作,golang中对应的是OpenFile
-
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
我们看到了也有flag,也有FileMode.比如说我要读写打开一个文件,如果不存在就创建,如果存在,就追加写,如何写go 代码?
-
f,err := os.OpenFile("test.txt",os.O_CREATE|os.O_APPEND|os.O_RDWR,0660)
-
if(err != nil){
-
panic(err)
-
}
我们看到了,golang中也有这些标志(注意O_CREATE,在C语言中,是O_CREAT),我在上面代码片段中用了几个标志
-
const (
-
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
-
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
-
O_RDWR int = syscall.O_RDWR // open the file read-write.
-
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
-
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
-
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist
-
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
-
O_TRUNC int = syscall.O_TRUNC // if possible, truncate file when opened.
-
)
C语言中有creat,没有则创建,有则截断写,本质等于O_WRONLY | O_CREAT | O_TRUNC
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
int creat (const char *name, mode_t mode)
Ken Thompson大神曾经戏言,漏掉creat系统调用中的e字母是他设计Unix最后悔的事情,呵呵看起来老爷子接收了教训,没有犯同样的拼写错误,golang中对应的接口是Create(大神这一次没有拼写错)
-
func Create(name string) (file *File, err error)
和C的creat系统调用相比,少了mode入参,默认是0x666(before umask),同时标志不再是O_WRONLY,而是O_RDWR,仍然带创建标志位,仍然带截断标志。
golang中的Open和C中的open就不能相比了(和C中的open PK那是OpenFile的事儿)接口如下:
-
func Open(name string) (file *File, err error)
直白说,就是带O_RDONLY的open,太菜了。
CLOSE
这个接口无甚好说。接口如下
-
func (f *File) Close() error
但说接口没啥说的,但是golang提供了defer,这是一个我认为很赞的特点,就是将不得不做的cleanup放到defer去做。
我们写C的人,经常遇到了这种代码
-
fd = open(...)
-
if(fd < 0 )
-
{
-
...
-
}
-
-
if (failed_1)
-
{
-
...
-
close(fd);
-
....
-
}
-
-
if(faile_2)
-
{
-
...
-
close(fd);
-
...
-
}
-
....
只要打开了文件,每次异常处理都要想着close,否则句柄泄漏,太烦。所以C语言是一门你要小心伺候的语言。
go提供了defer解决这种困境,后面不用时刻惦记close,函数退出前,会执行close。
-
f,err := os.OpenFile("test.txt",os.O_CREATE|os.O_APPEND|os.O_RDWR,0660)
-
if(err != nil){
-
panic("open file failed")
-
}
-
defer f.Close()
-
...
READ和WRITE
read和write是比较重要的文件操作了,这是C的接口。
-
#include <unistd.h>
-
-
ssize_t write(int fd, const void *buf, size_t count);
-
ssize_t read(int fd, void *buf, size_t count)
对于golang,接口如下:
-
func (f *File) Read(b []byte) (n int, err error)
-
func (f *File) ReadAt(b []byte, off int64) (n int, err error)
-
-
func (f *File) Write(b []byte) (n int, err error)
-
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
-
func (f *File) WriteString(s string) (ret int, err error)
看到代码片段,学习使用读写接口:
-
read_buf := make([]byte,32)
-
var pos int64 = 0
-
for{
-
-
n,err := f.ReadAt(read_buf,pos)
-
if err != nil && err != io.EOF{
-
panic(err)
-
}
-
if n == 0{
-
fmt.Printf("\nfinish read\n")
-
break
-
}
-
fmt.Printf("%s",string(read_buf[:n]))
-
pos = pos +(int64)(n)
-
}
在看一个代码片段:
-
var buff = make([]byte,1024)
-
for{
-
n,err := fi.Read(buff)
-
if err != nil && err != io.EOF{
-
panic(err)
-
}
-
-
if n == 0{
-
break
-
}
-
-
if _,err := fo.Write(buff[:n]); err != nil{
-
panic(err)
-
}
-
-
}
最后,我写了一个完整的代码,完成简单cp功能,就叫mycp
-
manu@manu-hacks:~/code/go/self$ cat mycp.go
-
package main
-
import "fmt"
-
import "os"
-
import "io"
-
-
func usage(){
-
fmt.Printf("%s %s %s\n",os.Args[0],"filename" , "newfile")
-
}
-
-
-
func main(){
-
-
if len(os.Args) != 3{
-
usage()
-
return
-
}
-
-
filename_in := os.Args[1]
-
fi,err := os.Open(filename_in)
-
if err != nil{
-
panic(err)
-
}
-
defer fi.Close()
-
-
filename_out := os.Args[2]
-
fo,err := os.Create(filename_out)
-
if err != nil{
-
panic(err)
-
}
-
defer fo.Close()
-
-
-
var buff = make([]byte,1024)
-
for{
-
n,err := fi.Read(buff)
-
if err != nil && err != io.EOF{
-
panic(err)
-
}
-
-
if n == 0{
-
break
-
}
-
-
if _,err := fo.Write(buff[:n]); err != nil{
-
panic(err)
-
}
-
-
}
-
}
执行结果:
-
manu@manu-hacks:~/code/go/self$ ./mycp test.txt test.bak
-
manu@manu-hacks:~/code/go/self$ diff test.txt test.bak
-
manu@manu-hacks:~/code/go/self$ cat test.txt
-
this is test file created by go
-
if not existed ,please create this file
-
if existed, Please write append
-
hello world,hello go
-
this is test file created by go
-
if not existed ,please create this file
-
if existed, Please write append
-
hello world,hello go
参考文献
1 Linux system program
2
3 StackOverflow
阅读(23864) | 评论(1) | 转发(2) |