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

在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编码。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容