http://blog.chinaunix.net/uid-24774106-id-3993609.html
操作文件是任何编程语言都绕不过,要掌握一门语言,知道如何操作文件是必不可少的,今天学习了下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)
O_RDONLY
O_WRONLY
O_RDWR
O_DIRECT
O_APPEND
O_TRUNC
。。。。
- func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
-
f,err := os.OpenFile("test.txt",os.O_CREATE|os.O_APPEND|os.O_RDWR,0660)
-
if(err != nil){
-
panic(err)
- }
-
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.
- )
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
- int creat (const char *name, mode_t mode)
- func Create(name string) (file *File, err error)
golang中的Open和C中的open就不能相比了(和C中的open PK那是OpenFile的事儿)接口如下:
- func Open(name string) (file *File, err error)
CLOSE
这个接口无甚好说。接口如下
- func (f *File) Close() error
我们写C的人,经常遇到了这种代码
-
fd = open(...)
-
if(fd < 0 )
-
{
-
...
-
}
-
-
if (failed_1)
-
{
-
...
-
close(fd);
-
....
-
}
-
-
if(faile_2)
-
{
-
...
-
close(fd);
-
...
-
}
- ....
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是比较重要的文件操作了,这是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)
-
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)
-
}
-
- }
-
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 golang os package
3 StackOverflow How to read/write from/to file?
上篇博文学习了go语言的对FILE的基本操作,我突然想到,文件一个很常用的场景是逐行处理,比如我们的linux下的神器awk,比如我之前写的KMean++算法处理NBA后卫的数据。对于C语言而言,fgets就解决了这个问题,看下C语言中fgets的接口:
- char *fgets(char *s, int size, FILE *stream);
我给出个C程序,完成基本的cat功能,支持-n选项,带了-n则打印出行号:
-
manu@manu-hacks:~/code/c/self/readline$
cat mycat.c
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<string.h>
-
#include<errno.h>
-
-
-
-
int num_flag = 0;
-
-
int cat(FILE* file)
-
{
-
char buf[1024] = {0};
-
int line_no = 1;
-
while(fgets(buf,1024,file) != NULL)
-
{
-
if(num_flag != 0)
-
{
-
fprintf(stdout,"%5d %s",line_no,buf);
-
}
-
else
-
{
-
fprintf(stdout,"%s",buf);
-
}
-
line_no++;
-
}
-
-
}
-
-
int main(int argc,char* argv[])
-
{
-
int i = 0 ;
-
int j = 0 ;
-
int file_exist = 0;
-
FILE* file = NULL;
-
-
for(i = 1; i < argc;i++)
-
{
-
if(strcmp(argv[i],"-n") == 0)
-
{
-
num_flag = 1;
-
break;
-
}
-
}
-
-
for(j = 1; j<argc ;j++)
-
{
-
if(j==i)
-
continue;
-
-
file_exist = 1;
-
-
file = fopen(argv[j],"rb");
-
if(file == NULL)
-
{
-
fprintf(stderr,"%s:err reading from %s:%s\n",
-
argv[0],argv[j],strerror(errno));
-
continue;
-
}
-
-
cat(file);
-
}
-
-
if(file_exist == 0)
-
{
-
cat(stdin);
-
}
- }
golang 提供了package bufio。bufio.NewReader()创建一个默认大小的readbuf,当然,也可以bufio.NewReaderSize
-
func NewReader(rd io.Reader) *Reader
-
NewReader returns a new Reader whose buffer has the default size(4096).
-
-
-
func NewReaderSize(rd io.Reader, size int) *Reader
-
NewReaderSize returns a new Reader whose buffer has at least the
-
specified size. If the argument io.Reader is already
a Reader with large
- enough size, it returns the underlying Reader.
-
func (b *Reader) ReadByte() (c byte, err
error)
-
ReadByte reads and returns a single byte. If no byte is
available,
-
returns an error.
-
-
func (b *Reader) ReadBytes(delim byte) (line []byte, err
error)
-
ReadBytes reads until the first occurrence of delim in the input,
-
returning a slice containing the data up to and including the delimiter.
-
If ReadBytes encounters an error before finding a delimiter, it returns
-
the data read before the error and the error itself (often io.EOF).
-
ReadBytes returns err != nil if and only if the
returned data does not
-
end in delim. For simple uses, a
Scanner may be more convenient.
-
-
func (b *Reader) ReadString(delim byte) (line
string, err error)
-
ReadString reads until the first occurrence of delim in the input,
-
returning a string containing the data up to and including the
-
delimiter. If ReadString encounters an error before finding a delimiter,
-
it returns the data read before the error and the error itself (often
-
io.EOF). ReadString returns err != nil if and only if the
returned data
-
does not end in delim. For simple uses, a
Scanner may be more
- convenient.
看一下go语言实现的简易mycat:
-
manu@manu-hacks:~/code/go/self$
cat mycat.go
-
package main
-
import "fmt"
-
import "os"
-
import "io"
-
import "flag"
-
import "bufio"
-
-
var num_flag = flag.Bool("n",false,"num
each line")
-
-
func usage(){
-
fmt.Printf("%s %s\n",os.Args[0],"filename")
-
}
-
-
-
-
func cat(r *bufio.Reader){
-
i := 1
-
for {
-
//buf,err := r.ReadBytes('\n')
-
buf,err := r.ReadString('\n')
-
if err == io.EOF{
-
break
-
}
-
-
if *num_flag{
-
fmt.Fprintf(os.Stdout,"%5d
%s",
-
i,buf)
-
i++
-
}else{
-
fmt.Fprintf(os.Stdout,"%s",buf)
-
}
-
-
}
-
return
-
}
-
-
-
func main(){
-
-
flag.Parse()
-
if(flag.NArg() == 0){
-
cat(bufio.NewReader(os.Stdin))
-
}
-
-
for i:=0;i<flag.NArg();i++{
-
f,err := os.OpenFile(flag.Arg(i),os.O_RDONLY,0660)
-
if err != nil{
-
fmt.Fprintf(os.Stderr,"%s
err read from %s : %s\n",
-
os.Args[0],flag.Arg(0),err)
-
continue
-
}
-
-
cat(bufio.NewReader(f))
-
f.Close()
-
}
- }
- For simple uses, a Scanner may be more convenient.
-
func NewScanner(r io.Reader) *Scanner
-
NewScanner returns a new Scanner to read from r. The split function
-
defaults to ScanLines.
-
-
func (s *Scanner) Text() string
-
Text returns the most recent token generated by a call to Scan as a
-
newly allocated string holding its bytes.
-
-
func (s *Scanner) Err() error
-
Err returns the first non-EOF error that was encountered by the Scanner.
-
-
func (s *Scanner) Scan() bool
-
Scan advances the Scanner to the next token, which will then be
-
available through the Bytes or Text method. It returns false when the
-
scan stops, either by reaching the end of the input or an error. After
-
Scan returns false, the Err method will return any error that occurred
- during scanning, except that if it was io.EOF, Err will return nil.
-
func cat(scanner *bufio.Scanner) error{
-
-
for scanner.Scan(){
-
fmt.Println(scanner.Text())
-
//fmt.Fprintf(os.Stdout,"%s\n",scanner.Text())
-
}
-
-
return scanner.Err()
- }
- fmt.Fprintf(os.Stdout,"%s",scanner.Text())
-
manu@manu-hacks:~/code/go/self$
go run mycat_v2.go test.txt
-
this is test
file created by goif not existed ,please
create this fileif existed, Please write appendhello world,hello
gothis is test file created by goif not existed ,please
create this fileif existed, Please write appendhello world,hello
gomanu@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
-
f,err := os.OpenFile(flag.Arg(i),os.O_RDONLY,0660)
-
...
-
error := cat(bufio.NewScanner(f))
-
if err != nil{
-
fmt.Fprintf(os.Stderr,"%s
err read from %s : %s\n",
-
os.Args[0],flag.Arg(i),error)
- }
参考文献:
1 godoc bufio
有疑问加站长微信联系(非本文作者)