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