ioutil包二

(原创随笔,转载请注明出处 http://www.cnblogs.com/majianguo/p/8016426.html)

ioutil包实现了一些I/O实用功能,导出了7个函数和1个变量:

func NopCloser(r io.Reader) io.ReadCloser

func ReadAll(r io.Reader) ([]byte, error)

func ReadDir(dirname string) ([]os.FileInfo, error)

func ReadFile(filename string) ([]byte, error)

func TempDir(dir, prefix string) (name string, err error)

func TempFile(dir, prefix string) (f *os.File, err error)

func WriteFile(filename string, data []byte, perm os.FileMode) error

var Discard io.Writer = devNull(0)

前一篇随笔Golang文件I/O一》介绍了用ReadFile和WriteFile读写文件,本文介绍ioutil包的其他功能。

ioutil.ReadAll

ReadFile和WriteFile可一次性读写文件的全部数据,非常便捷,除去错误判断,只需一行代码即可搞定。ReadAll与ReadFile类似,可一次性从io.Reader接口读取所有内容。

以下是来自godoc的内容:

func ReadAll(r io.Reader) ([]byte, error)

ReadAll reads from r until an error or EOF and returns the data it read. A successful call returns err == nil, not err == EOF. Because ReadAll is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.

ReadAll从r中读取数据直到发生错误或遇到EOF,返回读到的数据。调用成功返回err == nil,而不是err == EOF。因为ReadAll被定义为从源读取数据直到结束(EOF),不会将读取过程中遇到的EOF作为error报告。

godoc中提供了一个ReadAll的Example,从一个strings.NewReader  构造的io.Reader中读取所有数据:

r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")

b, err := ioutil.ReadAll(r)

if err != nil {

    log.Fatal(err)

}

fmt.Printf("%s", b)

  

再看另外一个比较实用的例子,发起一个http request,然后从response中读取数据:

package main

import (

    "fmt"

    "io/ioutil"

    "log"

    "net/http"

)

func main() {

    resp,err := http.Get("http://www.baidu.com")

    if err != nil {

        log.Fatal(err)

    }

    bData,err := ioutil.ReadAll(resp.Body)

    if err != nil {

        log.Fatal(err)

    }

defer resp.Body.Close()

    fmt.Println(string(bData))

}

  

通过ioutil包的源码可以看到ReadFile和ReadAll调用了同一个函数readAll

func readAll(r io.Reader, capacity int64) (b []byte, err error)

func ReadAll(r io.Reader) ([]byte, error) {

    return readAll(r, bytes.MinRead)

}

func ReadFile(filename string) ([]byte, error) {

    f, err := os.Open(filename)

    if err != nil {

        return nil, err

    }

    defer f.Close()

    // It's a good but not certain bet that FileInfo will tell us exactly how much to

    // read, so let's try it but be prepared for the answer to be wrong.

    var n int64

    if fi, err := f.Stat(); err == nil {

        // Don't preallocate a huge buffer, just in case.

        if size := fi.Size(); size < 1e9 {

            n = size

        }

    }

    // As initial capacity for readAll, use n + a little extra in case Size is zero,

    // and to avoid another allocation after Read has filled the buffer. The readAll

    // call will read into its allocated internal buffer cheaply. If the size was

    // wrong, we'll either waste some space off the end or reallocate as needed, but

    // in the overwhelmingly common case we'll get it just right.

    return readAll(f, n+bytes.MinRead)

}

  

io.TempFile\io.TempDir

TempFile和TempDir在指定位置生成临时文件和临时文件夹。

以下来自godoc:

func TempDir(dir, prefix string) (name string, err error)

TempDir creates a new temporary directory in the directory dir with a name beginning with prefix and returns the path of the new directory. If dir is the empty string, TempDir uses the default directory for temporary files (see os.TempDir). Multiple programs calling TempDir simultaneously will not choose the same directory. It is the caller's responsibility to remove the directory when no longer needed.

TempDir在指定的dir目录新创建以prefix为前缀的临时目录,返回新临时目录的路径。如果dir为空字符串,TempDir使用默认的存放临时文件的目录(os.TempDir,操作系统临时目录)。多个程序同时调用TempDir不会返回相同目录。调用者负责删除不再需要的临时目录。

Godoc中的Example,创建临时目录,退出时调用os.RemoveAll删除:

content := []byte("temporary file's content")

dir, err := ioutil.TempDir("", "example")

if err != nil {

    log.Fatal(err)

}

defer os.RemoveAll(dir) // clean up

tmpfn := filepath.Join(dir, "tmpfile")

if err := ioutil.WriteFile(tmpfn, content, 0666); err != nil {

    log.Fatal(err)

}

  

如果dir指定的目录不存在,返回err:

CreateFile d:/notexistdir: The system cannot find the file specified.

Godoc中关于TempFile的说明如下:

func TempFile(dir, prefix string) (f *os.File, err error)

TempFile creates a new temporary file in the directory dir with a name beginning with prefix, opens the file for reading and writing, and returns the resulting *os.File. If dir is the empty string, TempFile uses the default directory for temporary files (see os.TempDir). Multiple programs calling TempFile simultaneously will not choose the same file. The caller can use f.Name() to find the pathname of the file. It is the caller's responsibility to remove the file when no longer needed.

TempFile在dir目录中新创建以prefix为前缀的临时文件,打开文件用于读写,返回os.File指针。如果dir为空字符串,TempFile使用默认的存放临时文件的目录(os.TempDir,操作系统临时目录)。多个程序同时调用TempFile不会选择相同的文件。调用者负责删除不再需要的临时文件。

Godoc中关于TempFile的Example,在操作系统临时目录中创建临时文件,退出时调用os.Remove删除临时文件:

content := []byte("temporary file's content")

tmpfile, err := ioutil.TempFile("", "example")

if err != nil {

    log.Fatal(err)

}

defer os.Remove(tmpfile.Name()) // clean up

if _, err := tmpfile.Write(content); err != nil {

    log.Fatal(err)

}

if err := tmpfile.Close(); err != nil {

    log.Fatal(err)

}

  

如果dir不存在,返回err:

open d:\notexistdir\tmp946090042: The system cannot find the path specified.

在Windows中,dir为空字符串时,os.TempDir返回的目录为:

C:\Users\Dell\AppData\Local\Temp\

ioutil.ReadDir

ioutil.ReadDir可以用于遍历目录。Godoc中关于ReadDir的说明如下:

func ReadDir(dirname string) ([]os.FileInfo, error)

ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.

ReadDir返回dirname目录的目录条目列表,按照文件名排序。

Godoc中的Example:

files, err := ioutil.ReadDir(".")

if err != nil {

    log.Fatal(err)

}

for _, file := range files {

    fmt.Println(file.Name())

}

  

略作修改,即可迭代遍历目录:

package main

import (

    "log"

    "fmt"

    "io/ioutil"

)

func main() {

err := listFolder("d:/Tmp","")

if err != nil {

     log.Fatal(err)

}

}

func listFolder(dir string,indent string)(err error){

    fis ,err := ioutil.ReadDir(dir)

    if err != nil {

        return err

    }

    for _,fi := range fis {

        if fi.IsDir(){

            fmt.Printf("%sd:%s\r\n",indent,fi.Name())           

            err = listFolder(dir+fi.Name()+"/",indent+"\t")

            if err != nil {

                return err

            }

        }else{

            fmt.Printf("%sf:%s\r\n",indent,fi.Name())

        }

    }

    return 

}

  

ioutil.NopCloser

func NopCloser(r io.Reader) io.ReadCloser

NopCloser returns a ReadCloser with a no-op Close method wrapping the provided Reader r.

NopCloser函数将io.Reader接口适配为io.ReadCloser接口,为其封装了一个无操作的Close方法。

其源码非常简单;

type nopCloser struct {

    io.Reader

}

func (nopCloser) Close() error { return nil }

// NopCloser returns a ReadCloser with a no-op Close method wrapping

// the provided Reader r.

func NopCloser(r io.Reader) io.ReadCloser {

    return nopCloser{r}

}

  

这里使用了一个隐藏实现的技术,go标准库大量使用。非导出结构体nopCloser有一个匿名成员io.Reader,nopCloser实现了Closer方法,从而nopCloser实现了io.ReadCloser接口;ioutil.NopCloser函数返回nopCloser实现了从io.Reader到io.ReadCloser的适配。对调用者来说,内部的实现细节被隐藏掉了。

Io.nopCloser在net包中有大量的使用,src\net\http\httputil中的一个例子:

// drainBody reads all of b to memory and then returns two equivalent

// ReadClosers yielding the same bytes.

//

// It returns an error if the initial slurp of all bytes fails. It does not attempt

// to make the returned ReadClosers have identical error-matching behavior.

func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {

    if b == http.NoBody {

        // No copying needed. Preserve the magic sentinel meaning of NoBody.

        return http.NoBody, http.NoBody, nil

    }

    var buf bytes.Buffer

    if _, err = buf.ReadFrom(b); err != nil {

        return nil, b, err

    }

    if err = b.Close(); err != nil {

        return nil, b, err

    }

    return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil

}

  

Ioutil.NopCloser将bytes.NewReader构造的io.Reader接口封装成一个io.ReadCloser接口。

ioutil.Discard

变量定义如下:

// Discard is an io.Writer on which all Write calls succeed

// without doing anything.

var Discard io.Writer = devNull(0)

  

ioutil.Discard是一个io.Writer变量,对它的所有写调用都成功,不做任何操作。

func (devNull) Write(p []byte) (int, error) {

    return len(p), nil

}

func (devNull) WriteString(s string) (int, error) {

    return len(s), nil

}

  

ioutil.Discard在标准库net包和测试用例中有大量使用,以下代码节选自src\net\http\server.go

    // Per RFC 2616, we should consume the request body before

    // replying, if the handler hasn't already done so. But we

    // don't want to do an unbounded amount of reading here for

    // DoS reasons, so we only try up to a threshold.

    // TODO(bradfitz): where does RFC 2616 say that? See Issue 15527

    // about HTTP/1.x Handlers concurrently reading and writing, like

    // HTTP/2 handlers can do. Maybe this code should be relaxed?

    if w.req.ContentLength != 0 && !w.closeAfterReply {

        var discard, tooBig bool

        switch bdy := w.req.Body.(type) {

        case *expectContinueReader:

            if bdy.resp.wroteContinue {

                discard = true

            }

        case *body:

            bdy.mu.Lock()

            switch {

            case bdy.closed:

                if !bdy.sawEOF {

                    // Body was closed in handler with non-EOF error.

                    w.closeAfterReply = true

                }

            case bdy.unreadDataSizeLocked() >= maxPostHandlerReadBytes:

                tooBig = true

            default:

                discard = true

            }

            bdy.mu.Unlock()

        default:

            discard = true

        }

        if discard {

            _, err := io.CopyN(ioutil.Discard, w.reqBody, maxPostHandlerReadBytes+1)

            switch err {

            case nil:

                // There must be even more data left over.

                tooBig = true

            case ErrBodyReadAfterClose:

                // Body was already consumed and closed.

            case io.EOF:

                // The remaining body was just consumed, close it.

                err = w.reqBody.Close()

                if err != nil {

                    w.closeAfterReply = true

                }

            default:

                // Some other kind of error occurred, like a read timeout, or

                // corrupt chunked encoding. In any case, whatever remains

                // on the wire must not be parsed as another HTTP request.

                w.closeAfterReply = true

            }

        }

        if tooBig {

            w.requestTooLarge()

            delHeader("Connection")

            setHeader.connection = "close"

        }

    }

  

按RFC2616的规范,http server在回复之前需要消耗掉所有的request body。

(原创随笔,转载请注明出处 http://www.cnblogs.com/majianguo/p/8016426.html)

ioutil包二的更多相关文章

  1. go语言io和ioutil包的学习和使用

    io包 package main; import ( "errors" "fmt" "io" ) //io包中定义了非常多的interfac ...

  2. go语言学习笔记---读取文件io/ioutil 包

    io/ioutil 包几个函数方法 名称  作用 备注 ReadAll 读取数据,返回读到的字节 slice 1 ReadDir 读取一个目录,返回目录入口数组 []os.FileInfo, 2 Re ...

  3. GO系列-ioutil包

    ioutil包提供给外部使用的一共有1个变量,7个方法. // Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情 // 并且始终成功返回. var Disca ...

  4. Go 函数,包(二)

    #### Go 函数,包(二)***百丈峰,松如浪,地势坤,厚德载物之像*** 今天又到周五啦,你们有没有激动呢,反正我很激动,又有两天的自由了; 上一节我们学习了Go 的函数和包的一些知识 , 今天 ...

  5. Wireshark网络抓包(二)——过滤器

    一.捕获过滤器 选中捕获选项后,就会弹出下面这个框,在红色输入框中就可以编写过滤规则. 1)捕获单个IP地址 2)捕获IP地址范围 3)捕获广播或多播地址 4)捕获MAC地址 5)捕获所有端口号 6) ...

  6. Eclipse导入war包二次开发

    有实际项目在跑的war包,却没有源码,苦于想查看源码,身处运维组为研发组看不起,拿不到源码,只能自己来反编译了. 其实在解压war包后,可以看到文件夹中,已经存在了jsp文件,但是却没有逻辑代码层(a ...

  7. 动态加载jar包(二)

    上次说的加载jar包,有几个问题没有解决: 1.如果项目包含了其他的jar包如何解决? 2.如何规范上传的jar包的类和方法? 下面就解决一下上面两个问题 一.首先编写被调用的类,这次使用maven工 ...

  8. Golang学习 - io/ioutil 包

    ------------------------------------------------------------ // Discard 是一个 io.Writer 接口,调用它的 Write ...

  9. 使用Newlife网络库管道模式解决数据粘包(二)

    上一篇我们讲了 如何创建一个基本的Newlife网络服务端 这边我们来讲一下如何解决粘包的问题 在上一篇总我们注册了Newlife的管道处理器 ,我们来看看他是如何实现粘包处理的 svr.Add< ...

随机推荐

  1. web兼容性测试相关知识

    一.客户端兼容性 1.浏览器的兼容性测试 a.内核角度 Tridnt内核:代表作IE.腾讯.遨游.世界之窗等 Gecko内核:代表作Firefox webkit内核:代表作Safari.Chrome ...

  2. 洛谷最短路计数SPFA

    题目描述 给出一个N个顶点M条边的无向无权图,顶点编号为1-N.问从顶点1开始,到其他每个点的最短路有几条. 输入输出格式 输入格式: 输入第一行包含2个正整数N,M,为图的顶点数与边数. 接下来M行 ...

  3. javascript 45种缓动效果BY司徒正美

    javascript 45种缓动效果 参数 类型 说明 el element 必需,为页面元素 begin number 必需,开始的位置 change number 必需,要移动的距离 durati ...

  4. C#_表达式目录树的应用

    使用表达式目录树实现两个不同类型的属性赋值: public class People { public int Age { get; set; } public string Name { get; ...

  5. 剑指Offer_5_替换空格

    题目描述 请实现一个函数,将一个字符串中的空格替换成"%20". 例如,当字符串为We Are Happy.则经过替换之后的字符串为 We%20Are%20Happy. 在网络编程 ...

  6. Java-----关于eclipse导入项目发生的问题及解决办法

    今天通过eclipse导入了几个项目,项目名出现红叉,对于我这样的强迫症来说是无法容忍的,故现做总结,遇到同学可按照以下方法来操作. 改动的地方主要是两个方面: 1.Tomcat版本问题. 此问题是由 ...

  7. Cocos游戏引擎,让小保安成就大梦想

    秦丕胜是大连的一位保安.与非常多自学成才的人一样,2010年,在考上日照职业技术学院一年后便退了学. 因为没有高学历.加上喜欢自由,他来到了大连成为了一名保安.从高中開始,秦丕胜就酷爱代码,他曾自豪地 ...

  8. hadoop用mutipleInputs实现map读取不同格式的文件

    mapmap读取不同格式的文件这个问题一直就有,之前的读取方式是在map里获取文件的名称,依照名称不同分不同的方式读取,比如以下的方式 //取文件名 InputSplit inputSplit = c ...

  9. [UWP]本地化入门

    1. 前言 上一篇文章介绍了各种WPF本地化的入门知识,这篇文章介绍UWP本地化的入门知识. 2. 使用resw资源文件实现本地化 在以前的XAML平台,resx资源文件是一种很方便的本地化方案,但在 ...

  10. 七、Spring Boot Servlet 使用

    Web开发使用 Controller 基本上可以完成大部分需求,但是我们还可能会用到 Servlet.Filter.Listener.Interceptor 等等. 当使用spring-Boot时,嵌 ...