3-观察

    处理输入的简单程序都有一个差不多的设计结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出。

示例代码(以stdin作为输入):

// Dup prints the text of each line that appears more than
// once in the standard input, preceded by its count.
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	//分行读取标准输入s,将s放入map统计出现次数
	input := bufio.NewScanner(os.Stdin)
	statistic := make(map[string]int)
	for input.Scan() {
		line := input.Text()
		statistic[line]++
	}
	//打印出现重复的输入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
复制代码

input := bufio.NewScanner(os.Stdin) os.Stdin是标准输入描述结构,类似C语言中的FILE。处理输入的时候将该结构使用bufio包中的Scanner类型进行包装。每次调用input.Scan(),即读入下一行,并移除行末的换行符,读取的内容可以使用input.Text()获取。

statistic := make(map[string]int) 这条语句定义了一个map变量。map是存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。键可以是任意类型,只要其值能用==运算符比较,最常见的例子是字符串。值可以是任意类型。这个例子中的键是字符串,值是整数。内置函数make创建空map,此外还有别的作用。make有些神奇,它的参数是什么形式的?以后观察。

示例代码(以文件作为输入):

// Dup2 prints the count and text of lines that appear more than once
// in the input.  It reads from stdin file
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	//读取文件名
	var file string
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, "Paramater number error!")
		return
	} else {
		file = os.Args[1]
	}
	//打开文件
	f, err := os.Open(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "open file error: %v\n", err)
		return
	}

	//分行读取文件,将内容放入map统计出现次数
	input := bufio.NewScanner(f)
	statistic := make(map[string]int)
	for input.Scan() {
		line := input.Text()
		statistic[line]++
	}
	f.Close()

	//打印出现重复的输入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
复制代码

os.Open 函数返回两个值。第一个值是被打开的文件(*os.File),其后被Scanner读取。

os.Open 返回的第二个值是内置error类型的值。如果err等于内置值nil,那么文件被成功打开。读取文件,直到文件结束,然后调用Close关闭该文件,并释放占用的所有资源。相反的话,如果err的值不是nil,说明打开文件时出错了。这种情况下,错误值描述了所遇到的问题。我们的错误处理非常简单,只是使用Fprintf与表示任意类型默认格式值的动词%v,向标准错误流打印一条信息。

Go中错误处理在所有的高级语言中简直是一股清流啊,这其实贴合Go的开发思想,简单、直观。事实上,我们在设计代码逻辑的时候,本来就是在考虑两种情况,成功了往下走,失败了怎么办?我们在开发产品的时候,本质上在不断取舍,在可能出现错误的地方用分支语句处理就可以了,既符合逻辑又直白简单。

以上两种读取输入的方式都是使用的“流”模式,并根据需要拆分成多个行。理论上,这些程序可以处理任意数量的输入数据。还有另一个方法,就是一口气把全部输入数据读到内存中,一次分割为多行,然后处理它们。

示例代码(以文件作为输入):

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

func main() {
	//读取文件名
	var file string
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, "Paramater number error!")
		return
	} else {
		file = os.Args[1]
	}

	//读取文件所有内容
	data, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "open file error: %v\n", err)
		return
	}

	//分行统计数据
	content := string(data)
	statistic := make(map[string]int)
	for _, line := range strings.Split(content, "\r\n") {
		statistic[line]++
	}

	//打印出现重复的输入
	for line, num := range statistic {
		if num > 1 {
			fmt.Printf("%s\t%d\n", line, num)
		}
	}
}
复制代码

ReadFile 函数返回一个字节切片(byte slice),必须把它转换为string,才能用strings.Split分割。实现上,bufio.Scannerioutil.ReadFile ioutil.WriteFIle 都使用 *os.FileReadWrite 方法,但是,大多数程序员很少需要直接调用那些低级函数。高级函数,像 bufioio/ioutil 包中所提供的那些,用起来要容易点。


猜你喜欢

转载自juejin.im/post/5bcd8326f265da0ad701e16e
今日推荐