加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

golang编程之文件操作

发布时间:2020-12-16 18:35:44 所属栏目:大数据 来源:网络整理
导读:http://blog.chinaunix.net/uid-24774106-id-3993609.html 操作文件是任何编程语言都绕不过,要掌握一门语言,知道如何操作文件是必不可少的,今天学习了下golang对文件操作的支持。 golang对文件的支持是在os package里。我无意将本文写成官方文档的模样,

http://blog.chinaunix.net/uid-24774106-id-3993609.html


操作文件是任何编程语言都绕不过,要掌握一门语言,知道如何操作文件是必不可少的,今天学习了下golang对文件操作的支持。
golang对文件的支持是在os package里。我无意将本文写成官方文档的模样,我只是想讨论如何利用这些接口操作文件。
OPEN
熟悉文件系统的人都知道,open是整个文件系统中最复杂的接口之一。熟悉C语言的都知道,C语言中有open和creat,接口如下:

  1. #include<sys/types.h>
  2. #<sys/stat>
  3. #<fcntl>

  4. intopen(const char*pathname,intflags);
  5. ;

  6. intcreat)
对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
funcOpenFile(namestringint(file*File)
我们看到了也有flag,也有FileMode.比如说我要读写打开一个文件,如果不存在就创建,如果存在,就追加写,如何写go 代码?
f:=os."test.txt".O_CREATE|os.O_APPEND.O_RDWR)
  • if(err!=nil{
  • panicerr)
  • }
  • 我们看到了,golang中也有这些标志(注意O_CREATE,在C语言中,是O_CREAT),我在上面代码片段中用了几个标志
    const(
  • O_RDONLY=syscall.O_RDONLY // open the file read-only.
  • O_WRONLY.O_WRONLY // open the file write.
  • O_RDWR.O_RDWR // open the file read-write.
  • O_APPEND.O_APPEND // append data to the file when writing.
  • O_CREATE.O_CREAT // create a new fileifnone exists.
  • O_EXCL.O_EXCL // used with O_CREATEnotexist
  • O_SYNC.O_SYNC // openforsynchronous I/O.
  • O_TRUNC.O_TRUNC //ifpossible)
  • C语言中有creat,没有则创建,有则截断写,本质等于O_WRONLY | O_CREAT | O_TRUNC
    >
  • *name)
  • Ken Thompson大神曾经戏言,漏掉creat系统调用中的e字母是他设计Unix最后悔的事情,呵呵看起来老爷子接收了教训,没有犯同样的拼写错误,golang中对应的接口是Create(大神这一次没有拼写错)
    func Create)
    和C的creat系统调用相比,少了mode入参,默认是0x666(before umask),同时标志不再是O_WRONLY,而是O_RDWR,仍然带创建标志位,仍然带截断标志。
    golang中的Open和C中的open就不能相比了(和C中的open PK那是OpenFile的事儿)接口如下:
    func Open)
    直白说,就是带O_RDONLY的open,太菜了。

    CLOSE
    这个接口无甚好说。接口如下
    func(f)Close()error
    但说接口没啥说的,但是golang提供了defer,这是一个我认为很赞的特点,就是将不得不做的cleanup放到defer去做。
    我们写C的人,经常遇到了这种代码
    fd=open.(fd<0)
  • {
  • }

  • (failed_1.
  • close(faile_2}
  • .
  • 只要打开了文件,每次异常处理都要想着close,否则句柄泄漏,太烦。所以C语言是一门你要小心伺候的语言。
    go提供了defer解决这种困境,后面不用时刻惦记close,函数退出前,会执行close。
    "open file failed")
  • }
  • defer f.Close)
  • ...
  • READ和WRITE
    read和write是比较重要的文件操作了,这是C的接口。
    <unistd>

  • ssize_t writeintfd*buf;
  • ssize_t read)
  • 对于golang,接口如下:
    )Read(b[]byte(ninterrerror)
  • func)ReadAt)

  • func)Write)
  • func)WriteAt)WriteString(sstring(ret)
  • 看到代码片段,学习使用读写接口:
    read_buf=make]byte)
  • var pos int64=0
  • for{

  • n=fReadAt(read_bufiferr=nil && err=io.EOF{
  • panic)
  • }
  • ifn==0{
  • fmt.Printf"nfinish readn")
  • break
  • }
  • fmt"%s":n])
  • pos=pos+(int64(n)
  • }
  • 在看一个代码片段:
    var buff{
  • n=fi.Read(buff)
  • }

  • =0{
  • break
  • }

  • if_=fo.Write;err=nil{
  • panic)
  • }

  • }
  • 最后,我写了一个完整的代码,完成简单cp功能,就叫mycp
    manu@manu-hacks~/code/go/self$ cat mycp.go
  • package main
  • import"fmt"
  • import"os"
  • import"io"

  • func usage{
  • fmt"%s %s %sn".Args[0"filename""newfile"}


  • func main{

  • iflen(os=3{
  • usage)
  • return
  • }

  • filename_in[1]
  • fi.Open(filename_in}
  • defer fi)

  • filename_out[2]
  • fo.Create(filename_out}
  • defer fo)


  • var buff{
  • n&&.EOF=0{
  • break
  • ;}
  • 执行结果:
    /code/go/self$/mycptest.txt.bak
  • manu@manu/code/go/self$ diff.bak
  • manu@manu/code/go/self$ cat.txt
  • thisistestfile created by go
  • notexistedthisfile
  • ifexisted
  • hello world
  • 参考文献
    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*sintsize*stream;
    当然了首先要fopen,获得文件描述符,然后可以fgets按行获取。
    我给出个C程序,完成基本的cat功能,支持-n选项,带了-n则打印出行号:
    /c/self/readline$ cat mycat.c
  • #include<stdio>
  • #include<stdlib<<errno>



  • intnum_flag;

  • intcat(FILE*file{
  • char buf[1024{0};
  • intline_no=1while(fgetsbuf,0)">1024file)!=NULL)
  • (num_flag{
  • fprintf(stdout"%5d %s"bufelse
  • }
  • line_no}

  • intmainintargc*argv{
  • intiintjintfile_exist;
  • FILENULL(i;i<argc(strcmp(argv[i"-n"{
  • num_flag;
  • break(j;j=i)
  • continue;

  • file_existfopenargv[j]"rb";
  • (stderr"%s:err reading from %s:%sn"
  • argv[j(errno;
  • continue}

  • cat(file_exist{
  • cat(stdin}
  • golang怎么办?
    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*Reader
  • NewReaderSize returns a new Reader whose buffer has at least the
  • specified sizeIfthe argument ioisalready a Reader with large
  • enough size.
  • bufio提供

    *Reader)ReadByte(c)
  • ReadByte readsandreturnsa singleIfnobyteis available
  • returns an error.

  • func)ReadBytes(delim(line)
  • ReadBytes reads until the first occurrenceofdeliminthe input
  • returning a slice containing the data up toandincluding the delimiterIfReadBytes encounters an error before finding a delimiter
  • the data read before the errorandthe error itself(often io.
  • ReadBytes returns errandonlyifthe returned data doesnot
  • endindelimForsimpleusesa Scanner may be more convenient.

  • func)ReadString(line string)
  • ReadString reads until the first occurrence
  • returning a string containing the data up toandincluding the
  • delimiterIfReadString encounters an error before finding a delimiter
  • it returns the data read before the error(often
  • io.ReadString returns errifthe returned data
  • doesnotendForsimpleuses
  • convenient ReadByte这个接口,和C语言中fgetc很接近,每次读取一个字节。ReadBytes和ReadString都可以实现逐行读取,只要delim设置为'n'.
    看一下go语言实现的简易mycat:
    /self$ cat mycat"io"
  • import"flag"
  • import"bufio"

  • var num_flag=flag.Bool"n"false"num each line")

  • func usage"%s %sn"}



  • func cat(r*bufio{
  • i=1
  • /:r.ReadBytes'nReadString*num_flag.Fprintf.Stdout
  • i)
  • i+
  • else}
  • return
  • {

  • flag.Parse(flag.NArg(bufio.NewReader.Stdinfori<flagfosflagArgiO_RDONLY0660.Stderr"%s err read from %s : %sn"
  • os.Arg(0)
  • continue
  • catbufioNewReader)
  • f 单纯考虑逐行读取,line by line, bufio的文档也说
    .
  • 先看文档:
    func NewScanner(r io*Scanner
  • NewScanner returns a new Scanner to read from r.The split function
  • defaults to ScanLines(s*Scanner)Text)string
  • Text returns the most recent token generated by acallto Scan as a
  • newly allocated string holding its bytes)Err)error
  • Err returns the first non-EOF error that was encountered by the Scanner)Scan)bool
  • Scan advances the Scanner to the next token
  • available through the BytesorText method.It returns false when the
  • scan stopsofthe inputoran error.After
  • Scan returns false
  • during scanningifit was io怎么用Scanne呢?
    func cat(scanner.Scanner{

  • forscanner.Scan.Println.Text)
  • //fmt.Fprintf(os.Stdout,"%sn",scanner.Text())
  • }

  • return scannerErr 注意,为啥执行Scan,Text()函数就能返回下一行呢?因为默认的分割函数就是ScanLines.如你有特殊的需求来分割,func (s *Scanner) Split(split SplitFunc)
    这个函数可以制定SplitFunc。你可以定制自己的分割函数。
    需要注意的是,Scan会将分割符号n去除,如果Fprintf输出的话,不添加n打印,会出现没有换行的现象,如下所示
    fmt)
  • /self$ go run mycat_v2.go test.txt
  • thisistest file created by goifnotexistedplease create this fileif existedPlease write appendhello worldhello gothishello gomanu@manu/self$ cat test.txt
  • thisistest file created by go
  • this调用部分的代码如下:
    .OpenFile.O_RDONLY.
  • =cat.NewScanner 推荐使用Scanner,使用比较简单。
    参考文献:
    1 godoc bufio

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    • 推荐文章
        热点阅读