我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:港彩神鹰 > 读入原语 >

go 输入输出流(io)

归档日期:08-05       文本归类:读入原语      文章编辑:爱尚语录

  io 包为 I/O 原语提供了基本的接口。它主要包装了这些原语的已有实现。

  由于这些接口和原语以不同的实现包装了低级操作,因此除非另行通知,否则客户端不应假定它们对于并行执行是安全的。

  在 io 包中最重要的是两个接口:Reader 和 Writer 接口。本章所提到的各种 IO 包,都跟这两个接口有关,也就是说,只要实现了这两个接口,它就有了 IO 的功能。

  Read 将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 = n = len(p)) 以及任何遇到的错误。即使 Read 返回的 n len(p),它也会在调用过程中使用 p 的全部作为暂存空间。若一些数据可用但不到 len(p) 个字节,Read 会照例返回可用的数据,而不是等待更多数据。

  当 Read 在成功读取 n 0 个字节后遇到一个错误或 EOF (end-of-file),它就会返回读取的字节数。它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(同时 n == 0)。 一般情况的一个例子就是 Reader 在输入流结束时会返回一个非零的字节数,同时返回的 err 不是 EOF 就是 nil。无论如何,下一个 Read 都应当返回 0, EOF。

  调用者应当总在考虑到错误 err 前处理 n 0 的字节。这样做可以在读取一些字节,以及允许的 EOF 行为后正确地处理 I/O 错误。

  也就是说,当 Read 方法返回错误时,不代表没有读取到任何数据。调用者应该处理返回的任何数据,之后才处理可能的错误。

  根据 Go 语言中关于接口和实现了接口的类型的定义(Interface_types),我们知道 Reader 接口的方法集(Method_sets)只包含一个 Read 方法,因此,所有实现了 Read 方法的类型都实现了 io.Reader 接口,也就是说,在所有需要 io.Reader 的地方,可以传递实现了 Read() 方法的类型的实例。

  ReadFrom 函数将 io.Reader 作为参数,也就是说,ReadFrom 可以从任意的地方读取数据,只要来源实现了 io.Reader 接口。比如,我们可以从标准输入、文件、字符串等读取数据,示例代码如下:

  同样的,所有实现了Write方法的类型都实现了 io.Writer 接口。

  在上个例子中,我们是自己实现一个函数接收一个 io.Reader 类型的参数。这里,我们通过标准库的例子来学习。

  在fmt标准库中,有一组函数:Fprint/Fprintf/Fprintln,它们接收一个 io.Wrtier 类型参数(第一个参数),也就是说它们将数据格式化输出到 io.Writer 中。那么,调用这组函数时,该如何传递这个参数呢?

  很显然,fmt.Println会将内容输出到标准输出中。下一节我们将详细介绍fmt包。

  初学者看到函数参数是一个接口类型,很多时候有些束手无策,不知道该怎么传递参数。还有人问:标准库中有哪些类型实现了 io.Reader 或 io.Writer 接口?

  通过本节上面的例子,我们可以知道,os.File 同时实现了这两个接口。我们还看到 os.Stdin/Stdout 这样的代码,它们似乎分别实现了 io.Reader/io.Writer 接口。没错,实际上在 os 包中有这样的代码:

  目前,Go 文档中还没法直接列出实现了某个接口的所有类型。不过,我们可以通过查看标准库文档,列出实现了 io.Reader 或 io.Writer 接口的类型(导出的类型):(注:godoc 命令支持额外参数 -analysis ,能列出都有哪些类型实现了某个接口,相关参考godoc -h或Static analysis features of godoc。另外,我做了一个官网镜像,能查看接口所有的实现类型,地址:。

  从接口名称很容易猜到,一般地, Go 中接口的命名约定:接口名以 er 结尾。注意,这里并非强行要求,你完全可以不以 er 结尾。标准库中有些接口也不是以 er 结尾的。

  当 ReadAt 返回的 n len(p) 时,它就会返回一个 非nil 的错误来解释 为什么没有返回更多的字节。在这一点上,ReadAt 比 Read 更严格。

  即使 ReadAt 返回的 n len(p),它也会在调用过程中使用 p 的全部作为暂存空间。若一些数据可用但不到 len(p) 字节,ReadAt 就会阻塞直到所有数据都可用或产生一个错误。 在这一点上 ReadAt 不同于 Read。

  若 ReadAt 按查找偏移量从输入源读取,ReadAt 应当既不影响基本查找偏移量也不被它所影响。

  若 WriteAt 按查找偏移量写入到目标中,WriteAt 应当既不影响基本查找偏移量也不被它所影响。

  若区域没有重叠,WriteAt 的客户端可对相同的目标并行执行 WriteAt 调用。

  打开文件 WriteAt.txt,内容是:Golang中文社区——Go语言中文网。

  ReadFrom 从 r 中读取数据,直到 EOF 或发生错误。其返回值 n 为读取的字节数。除 io.EOF 之外,在读取过程中遇到的任何错误也将被返回。

  如果不通过 ReadFrom 接口来做这件事,而是使用 io.Reader 接口,我们有两种思路:

  WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数。 在写入过程中遇到的任何错误也将被返回。

  通过 io.ReaderFrom 和 io.WriterTo 的学习,我们知道,如果这样的需求,可以考虑使用这两个接口:“一次性从某个地方读或写到某个地方去。”

  Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。

  也就是说,Seek 方法用于设置偏移量的,这样可以从某个特定位置开始操作数据流。听起来和 ReaderAt/WriteAt 接口有些类似,不过 Seeker 接口更灵活,可以更好的控制读写数据流的位置。

  简单的示例代码:获取倒数第二个字符(需要考虑 UTF-8 编码,这里的代码只是一个示例)

  文件 (os.File)、归档(压缩包)、数据库连接、Socket 等需要手动关闭的资源都实现了 Closer 接口。

  接下来的示例中,我们通过 bytes.Buffer 来一次读取或写入一个字节(主要代码):

  一般地,我们不会使用 bytes.Buffer 来一次读取或写入一个字节。那么,这两个接口有哪些用处呢?

  可以通过在 Go 语言源码 src/pkg 中搜索 io.ByteReader 或 io.ByteWiter,获得哪些地方用到了这两个接口。你会发现,这两个接口在二进制数据或归档压缩时用的比较多。

  这些接口是上面介绍的接口的两个或三个组合而成的新接口。例如 ReadWriter 接口:

  这些接口的作用是:有些时候同时需要某两个接口的所有功能,即必须同时实现了某两个接口的类型才能够被传入使用。可见,io 包中有大量的“小接口”,这样方便组合为“大接口”。

  也就是说,SectionReader 只是内部(内嵌)ReaderAt 表示的数据流的一部分:从 off 开始后的 n 个字节。

  这个类型的作用是:方便重复操作某一段 (section) 数据流;或者同时需要 ReadAt 和 Seek 的功能。

  关于该类型在标准库中的使用,我们在8.5 archive/zip — zip归档访问会讲到。

  从 R 读取但将返回的数据量限制为 N 字节。每调用一次 Read 都将更新 N 来反应新的剩余数量。

  关于 Read 方法的说明:从管道中读取数据。该方法会堵塞,直到管道写入端开始写入数据或写入端关闭了。如果写入端关闭时带上了 error(即调用 CloseWithError 关闭),该方法返回的 err 就是写入端传递的error;否则 err 为 EOF。

  关于 Write 方法的说明:写数据到管道中。该方法会堵塞,直到管道读取端读完所有数据或读取端关闭了。如果读取端关闭时带上了 error(即调用 CloseWithError 关闭),该方法返回的 err 就是读取端传递的error;否则 err 为 ErrClosedPipe。

  细心的读者可能发现:不是输出 3 次后结束吗?怎么“Go语言中文网”却输出了 4 次?这个问题我们稍后讨论。我们先来分析一下例子代码。

  它将 io.Reader 连接到 io.Writer。一端的读取匹配另一端的写入,直接在这两端之间复制数据;它没有内部缓存。它对于并行调用 Read 和 Write 以及其它函数或 Close 来说都是安全的。一旦等待的 I/O 结束,Close 就会完成。并行调用 Read 或并行调用 Write 也同样安全:同种类的调用将按顺序进行控制。稍后我们会分析管道相关的源码。

  在 PipeWrite 函数中,我们循环往管道中写数据,写第三次时,我们调用 CloseWithError 方法关闭管道的写入端,之后再一次调用 Write 方法,发现返回了error,于是退出了循环。

  可是,从输出结果中,我们发现,最后一次写虽然返回 error(返回的 n 并非 0),但是读取端却能读到最后一次写的数据,这让人很费解。下面我们一起来探索一下相关源码,分析问题的原因。

  从上文知道,PipeWriter 和 PipeReader 都没有导出成员。查看源码发现,两者都只有一个成员:p *pipe,这两种类型的所有方法都是调用了 pipe 类型对应的方法实现的。

  通过上面两个方法的代码注释,应该清楚例子中为啥输出4次了吧?我们再分析一下:

  Copy 将 src复制到 dst,直到在 src上到达 EOF 或发生错误。它返回复制的字节数,如果有的话,还会返回在复制时遇到的第一个错误。

  CopyN 将 n 个字节从 src复制到 dst。 它返回复制的字节数以及在复制时遇到的最早的错误。由于 Read 可以返回要求的全部数量及一个错误(包括 EOF),因此 CopyN 也能如此。

  若 dst 实现了 ReaderFrom 接口,复制操作也就会使用它来实现。

  ReadFull 精确地从 r 中将 len(buf) 个字节读取到 buf 中。它返回复制的字节数,如果读取的字节较少,还会返回一个错误。若没有读取到字节,错误就只是 EOF。如果一个 EOF 发生在读取了一些但不是所有的字节后,ReadFull 就会返回 ErrUnexpectedEOF。对于返回值,当且仅当 err == nil 时,才有 n == len(buf)。

  对于这两种类型对应的实现方法(Read 和 Write 方法)的使用,我们通过例子来演示。

  这段程序执行后在生成 tmp.txt 文件,同时在文件和屏幕中都输出:Go语言中文网。这和 Unix 中的 tee 命令类似。

  Go 实现 Unix 中 tee 命令的功能很简单吧。MultiWriter 的 Write 方法是如何实现的?有兴趣可以自己实现一个,然后对着源码比较一下。

  TeeReader 返回一个 Reader,它将从 r 中读到的数据写入 w 中。所有经由它处理的从 r 的读取都匹配于对应的对 w 的写入。它没有内部缓存,即写入必须在读取完成前完成。任何在写入时遇到的错误都将作为读取错误返回。

  也就是说,我们通过 Reader 读取内容后,会自动写入到 Writer 中去。例子代码如下:

  Go命令行参数及标准输入输出标签(空格分隔):Go1.Go命令行参数的使用Go的命令行参数存储在切片os.Args当中,可以说和python的命令行参数非常相似fmt.Println(os.Args)...博文来自:别时茫茫的专栏

  【简介】fmt包实现了格式化I/O函数,类似于C的printf和scanf。格式“占位符”衍生自C,但比C更简单。【打印】占位符:[一般]%v 相应值的默认格式。在打印结构体时,“加号”标记(...博文来自:追求技术的丸子

  常量Go语言常量和C语言差不多Go语言定义常量const不能少,数据类型可以不写Go语言定义常量不能用:=Go语言定义常量没有赋初值,那么值就和上一行的常量的值相等constnum=666constn...博文来自:weixin_34059951的博客

  1.变量Go同其他语言不同的地方在于变量的类型在变量名的后面。不是:inta,而是aint。当定义了一个变量,它默认赋值为其类型的null值。这意味着,在varaint后,a的值为0。而varsstr...博文来自:lichao_ustc的专栏

  分类1.IO流按流不同分为字符流和字节流两大类:字节流占8位1个字节,可以对所以对象操作。字符流16位两个字节,一般对文字类对象操作,Java中的字符是Unicode编码。2.按输入输出流分类:输入流...博文来自:wudh

  JavaIO流学习总结一:输入输出流转载请标明出处:本文出自【赵彦军的博客】Java流...博文来自:赵彦军

  虽然io包提供了不少类型、方法和函数,但有时候使用起来不是那么方便。比如读取一个文件中的所有内容。为此,标准库中提供了一些常用、方便的IO操作函数。说明:这些函数使用都相对简单,一般就不举例子了。1....博文来自:tianlongtc的博客

  几种输出方式的区别Print、Println、Printf、Sprintf、Fprintf都是fmt包中的公共方法,在需要打印信息时需要用到这些函数,那么这些函数有什么区别呢?Print:输出到控制台...博文来自:一只IT小小鸟

  一、File文件操作类1.File类的使用java.io.File类是一个普通的类,直接产生实例化对象即可。如果要实例化对象则需要使用到两个构造方法publicFile(Stringpathname)...博文来自:L_X_Y_HH的博客

  ava的I/O中有两种基本的流类型分别是输入流:InputStream输出流:OutputStream有的时候很容易搞混使用的顺序,只需要记住:输入流是把数据从别的地方读入本程序的内存输出流则是把数据...博文来自:phplaoniao的博客

  摘要:读完本章节,您对java的IO流有更清晰深刻的认识,对适配器模式、装饰模式也有初步的了解。      一、关于流引用百度百科上的解释:        流是一种抽象概念,它代表了数据的无结构化传递...博文来自:言必信,行必果

  IO流分类:*按流向分为:输入流、输出流。*按操作类型分为:字节流:可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的。字符流:只能操作字符数据,比较方便。父类:*字节流的抽象父类:Inp...博文来自:FenQing1213的博客

  IO流以及缓冲流可以帮我们实现文本文件的读写操作,关于它们的使用可以去参考这篇博客io流/缓冲流.接下来我们直接来看复制文本文件的五种方法,直接看代码:packagecom.qibao.iostrea...博文来自:七宝.博客

  什么是内存输出流该输出流可以向内存中写数据,把内存当作一个缓冲区,写出之后可以一次性获取所有数据。使用方式1、创建对象:newByteArrayOutStream()。2、写出数据:write(int...博文来自:玉汝于成

  实验内容1.键盘输入10个整数,从小到大进行排序。2.接收键盘输入的字符串,用FileInputStream类将字符串写入文件,用FileOutputStream类读出文件内容显示在屏幕上。3.将一个...博文来自:不知道是谁的博客

  今天来复习一下IO流的api,在java中用io流来进行文件的输出和输出操作,那么首先类讲解一下什么是输入和输出:所有往内存中送数据都是输入所有从内存出数据都是输出能用java.io包中的api操作的...博文来自:的博客

  一、什么是IO流:将数据从内存传输到外部存储设备的通道,管道。IO流的分类:1.按方向划分:以JVM为参照物输入流:将外部存储设备中的数据读入到内存中。...博文来自:MacWx的博客

  网络程序很大一部分的工作都是做简单或复杂的输入输出工作,将数据(信息)从一个系统转移到另外一个系统,所以在讲到网络编程之前我们首先要先了解到数据的输入输出工作。需要注意的是java的基本输入输出流是以...博文来自:程序猿Aaron

  流流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序列或字符序列。从流中取得数据的操作称为提取操作,亦称读操作;而向流中添加数据的操作称为插入操作,亦称写...博文来自:mulinsen77的博客

  1. 输入流和输出流的联系和区别,字符流和字节流的联系和区别输入流是得到数据,输出流是输出数据。字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。在字节流中输出数...博文来自:lyn789的博客

本文链接:http://chuyenchame.com/duruyuanyu/770.html