Go语言实践Go

Go语言I/O编程(一)----输入与输出

2019-04-28  本文已影响2人  帅气的昵称都有人用了

在Go语言当中,I/O的操作主要被封装在以下几个包中:

io:为I/O提供基本的接口,在这个包中最为重要的是两个接口---ReaderWriter接口。
io/ioutil:封装了一些比较实用的I/O函数。
fmt:实现了格式化的I/O。
bufio:实现带缓冲的I/O。

我们先来介绍io这个包。

io包

我们刚才说到,io包中最为重要的是两个接口,ReaderWriter接口,只要实现了这两个接口,它就有了I/O的功能。

Reader接口

Reader接口的定义如下:

type Reader interface {
    Read(p []byte) (n int, err error)
}

io.Reader的规则是比较复杂的,我们来大致说明一下:

(1)Read方法最多读取len(p)字节的数据,并保存到p;
(2)Read方法返回读取的字节数以及任何发生的错误信息;
(3)读取的字节数n要满足0<=n<=len(p)
(4)当读取的字节数不足以填满p时,方法会立即返回,而不会等待更多的数据;
(5)读取的过程中遇到错误,会返回读取的字节数n以及相应的错误err
(6)在底层数据流结束时,方法会返回n>0字节,但是遇到错误可能会中断(EOF),也可以返回nil
(7)在第6中情况下,再次调用Read方法的时候,肯定会返回(0,EOF);
(8)调用Read方法时,如果n>0时,优先处理读入的数据,然后再返回错误err,EOF也要这样处理;
(9)Read方法不鼓励返回n=0或者err=nil的情况。

我们用一个例子来谈谈这个接口的用法:

func ReadFrom(reader io.Reader, num int) ([]byte, error) {
    p := make([]byte, num)
    n, err := reader.Read(p)
    if n > 0 {
        return p[:n], nil
    }
    return p, err
}

该函数将io.Reader作为参数,这样一来,ReadFrom函数可以从任意地方读取数据,只要来源实现了io.Reader接口:

// 从标准输入读取
data, err = ReadFrom(os.Stdin, 11)
// 从普通文件读取,其中file是os.File的实例
data, err = ReadFrom(file, 9)
// 从字符串中读取
data, err = ReadFrom(strings.NewReader("from string"), 12)

有一点需要注意,那就是io.EOF变量的定义,var EOF = errors.New("EOF")error类型。


Writer接口

定义如下:

type Writer interface {
    Write(p []byte) (n int, err error)
}

规则:

(1)Write方法向底层数据流写入len(p)字节的数据,这些数据来自于切片p;
(2)返回被写入的字节数n,其中0<=n<=len(p)
(3)如果n<len(p),则必须返回一些非nilerr
(4)如果中途出现问题,也要返回非nilerr
(5)Write方法绝对不能修改切片p以及其中的数据。

fmt标准库中,有一组函数:Fprint/Fprintf/Fprintln,他们都接收一个io.Writer类型参数。比如我们最常用的fmt.Println函数:

func Println(a ...interface{}) (n int, err error) {
    return Fprintln(os.Stdout, a...)
}

很显然,fmt.Println会将内容输出到标准输出中。


fmt:格式化的I/O

fmt包实现了格式化的I/O函数,类似于C语言中的printfscanf

Print函数序列

在Go语言的fmt中,其实有很多的输出函数类型,但大致的包括三类:Sprintf/Fprintf/Printf函数通过指定的格式输出或格式化内容;Sprint/Fprint/Print函数只是使用默认的格式输出或格式化内容;Sprintln/Fpirntln/Println函数使用默认的格式输出或格式化内容,同时会在最后加上"换行符"。
我们看到最后一组函数中存在着一个ln,它们之间会将两者内容直接通过一个空格连接起来:

result1 := fmt.Sprintln("example.com", 2019)
result2 := fmt.Sprint("example.com", 2019)

result1的结果是example.com 2019,而result2的结果是example.com2019.从而起到了连接字符串的作用。


Stringer接口

定义如下:

type Stringer interface {
    String() string
}

我们通过一个例子来具体讲解一下:
我们做如下定义:

type Person struct {
    Name string
    Age int
    Sex int
}

Person实现String方法:

p := &Person{"Alice", 28, 0}
fmt.Println(p)

接下来,我们再为Person增加String方法:

func (this *Person) String() string {
    buffer := bytes.NewBufferString("This is ")
    buffer.WriteString(this.Name + ", ")
    if this.Sex == 0 {
        buffer.WriteString("he")
    } else {
        buffer.WriteString("she")
    }
    buffer.WriteString("is")
    buffer.WriteString(strconv.Itoa(this.Age))
    buffer.WriteString("years old.")
    return buffer.String()
}

此时,我们再运行刚才那两行代码,得到的结果会是:

This is Alice, he is 28 years old

Formatter接口

定义:

type Formatter interface {
    Format(f State, c rune)
}

Formatter接口由带有定制的格式化符的值所实现,Format的实现可调用SprintfFprintf(f)等函数来生成其输出。
我们继续前面的那个例子:

func (this *Person) Format(f fmt.State, c rune) {
    if c == 'L' {
        f.Write([]byte(this.String())
        f.Write([]byte("Person has three paraments.")
    } else {
        f.Write([]byte(fmt.Sprintln(this.String())))
    }
}

这样就实现了Formatter的接口。
在这里有几点需要说明:
1)Formatter接口可以实现自定义占位符,同时fmt包中和类型相对应的预定义占位符会无效。因此在例子中Format的实现加上了else子句。
2)实现了Foramtter接口,相应的Stringer接口就不起作用了,但实现了Formatter接口的类型应该实现Stringer接口,这样方便在Format方法中调用String()方法。就像上面这样。
3)Format方法的第二个参数是占位符中%后的字母(有精度和宽度会被忽略,只保留字母)


GoStringer方法

定义:

type GoStringer interface {
    GoString() string
}

该接口定义了类型的Go语言语法格式,用于打印格式化占位符%#v的值。
但该接口一般是不需要的。


Scan序列函数

Print序列函数一样,我们也将Scan序列函数分为三类:
1)Scan/FScan/Sscan

var (name string age int) n, _ := fmt.Sscan("Alice 28", &name, &age)
fmt.Println(n, name, age)

这一组函数将连续由空格分隔的值存储为连续的实参。
2)Scanf/FScanf/Sscanf

var (name string age int) n, _ := fmt.Sscanf("Alice 28", "%s %d", &name, &age)
fmt.Println(n, name, age)

这组函数将连续由空格分隔的值存储为连续实参,其格式为format决定,换行符处停止扫描。
3)Scanln/FScanln/Sscanln

var (name string age int) n, _ := fmt.Sscanln("Alice 28", &name, &age)
fmt.Println(n, name, age)

Scanln/FScanln/Sscanln的表现和上一组是一样的,遇到\n停止。
在一般情况下,我们使用最后这一句函数。


文本处理

Go原因标准库中有几个包专门用于处理文本。
strings是一个字符串操作包,通常一般的字符串操作需求都可以在这个包中找到。
vbytesbyte slice便利操作,在Go语言中string是内置类型。同时它与普通的slice类型有着相似的性质。
strconv:在Go语言中是没有隐式转换的,需要用这个包的方法来实现转换。
regexp:正则表达式,这个包提供了正则表达式的功能。
unicodeUnicode编码,UTF-8/16编码。

上一篇 下一篇

猜你喜欢

热点阅读