本文提供常见的两种输入方式(文本输入和终端输入)的处理样例代码。

一、文件读取

1.1 普通文件读取

  • 普通文件的读取可使用os.Open()或os.OpenFile(),组合bufio.NewReader()或bufio.NewScanner()逐行读取数据。

os.Open()和os.OpenFile()的区别:

//仅读取文件,O_RDONLY
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

//flag可设置不同的文件模式及perm,文件可用于正常IO读写
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	return openFileNolog(name, flag, perm)
}

bufio.NewReader()或bufio.NewScanner()区别

均可用于文件读写。以下样例中ReadLine及Scan默认以换行符"\n"分行读取
file, _ := os.Open(filePath)
reader := bufio.NewReader(file)
for {
	result, _, _ := reader.ReadLine()
	fmt.Println(result)//输出到标准输出
}
/********或**********/
input := bufio.NewScanner(file)//初始化一个扫表对象
for {
	if input.Scan() {//扫描输入内容
		line := input.Text()//把输入内容转换为字符串
		fmt.Println(line)//输出到标准输出
	}
}

其中scanner提供灵活的文本分隔方法,用于使用更方便,具体可参照2.1中scanner.Split方法的介绍。

1.2 配置文件读取

常用的配置文件格式有toml、json和conf三种。

  • toml文件读取,依赖"github.com/BurntSushi/toml"包解析数据
/**************conf.toml内容为******************/
[hostInfo]
Warehouse = "GZ-HL"
Host = "10.96.66.19"
/**************配置解析函数为******************/
type HostInfo struct {
	Host      string `toml:"Host"`
	Warehouse string `toml:"Warehouse"`
}
type Config struct {
	HostConf  HostInfo `toml:"hostInfo"`
}
var Conf Config
func InitConf() error {
	file, err := os.OpenFile("./conf/conf.toml", os.O_RDWR, 0666)
	if err != nil {
		log.Panic(err.Error())
		return err
	}

	content, err := ioutil.ReadAll(file)
	if err != nil {
		log.Panic(err.Error())
		return err
	}

	_, err = toml.Decode(string(content), &Conf)
	if err != nil {
		log.Panic(err.Error())
		return err
	}
	return nil
}
  • json文件读取,依赖"encoding/json"包解析数据
/**************conf.toml内容为******************/
{
	"hostInfo":{
	"warehuose":"GZ-HL",
	"host":"10.96.66.19"
	}
}
/**************配置解析函数为******************/
type HostInfo struct {
	Host      string `json:"host"`
	Warehouse string `json:"warehouse"`
}
type Config struct {
	HostConf  HostInfo `json:"hostInfo"`
}
var Conf Config
func InitConf() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	file, err := os.OpenFile("./conf/conf.json", os.O_RDWR, 0666)

	if err != nil {
		log.Panic(err.Error())
	}

	content, err := ioutil.ReadAll(file)
	if err != nil {
		log.Panic(err.Error())
	}

	err = json.Unmarshal(content, &Conf)
	if err != nil {
		log.Panic(err.Error())
	}
}
  • conf文件读取,依赖"github.com/astaxie/beego/config"包解析数
/**************conf.conf内容为******************/
[hostInfo]
warehouse = "GZ-HL"
host = "10.96.66.19"
/**************配置解析函数为******************/
func main(){
	ConfPath := flag.String("c", "./conf/app.conf", "config file.")
	flag.Parse()
	conf, err = config.NewConfig("ini", *ConfPath)
	warehouse := strings.ToLower(conf.String("hostInfo::warehouse"))
	host := strings.ToLower(conf.String("hostInfo::host"))	
}

json、toml和conf文件格式的配置文件均可满足用户需求,json层次清晰,配置文件中不可添加注释。toml轻量,可添加注释。conf文件解析无需定义struct格式。

二、命令行输入

2.1 bufio包参数解析

  • bufio用于标准输入读取。其中bufio.Reader及bufio.Scanner均可实现输入读取。bufio.Scanner用于文本扫描&缓存&自定义匹配函数,bufio.Reader对于文本处理,需考虑处理行太长问题。二者底层均依赖io.Reader(bytes,strings)
package main
import (
	"bufio"
	"fmt"
	"os"
)

func main(){
	reader := bufio.NewReader(os.Stdin)
	for {
		result,_,_ := reader.ReadLine()
		fmt.Println("this line is : ", result)
	}
}
/********或**********/
func main(){	
input := bufio.NewScanner(os.Stdin)//初始化一个扫表对象,os.stdin读取linux下文件/dev/stdin
	for {
		if input.Scan() {//扫描输入内容
			line := input.Text()//把输入内容转换为字符串
			fmt.Println("this line is : ", line)//输出到标准输出
		}
	}
}
输入输出:
hello
this line is : hello

Scanner.Scan方法默认以换行符\n作为分隔符号。Go语言还提供四种方法,ScanBytes返回单个字节作为一个token,ScanWords通过空格分隔单词,ScanRunes返回单个UTF-8编码的rune作为一个token,ScanLines返回一行文本,换行以\n或windows下\r\n为依据。

/********ScanWords样例**********/
scanner := bufio.NewScanner(strings.NewReader("hello world !"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
     fmt.Println(scanner.Text())
}
输出:
hello
world
!
/********ScanLines样例**********/
scanner := bufio.NewScanner(strings.NewReader("hello world \n!"))
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
     fmt.Println(scanner.Text())
}
输出:
hello world 
!

同时可自定义分隔方法splitFunc,下面给出一个自定义以字符"Golang"为切割的自定义splitFunc。

package main

import (
	"bufio"
	"bytes"
	"strings"
	"fmt"
)

func main() {
	const inputstr = "This is The Golang Standard Library.\nWelcome you!"
	input := bufio.NewScanner(strings.NewReader(inputstr))
	split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		if j := bytes.IndexAny(data, "Golang"); j >0{
			return j, data[:j], nil
		}

		if atEOF {
			return 0, data, bufio.ErrFinalToken
		} else {
			return 0, nil, nil
		}
	}
	input.Split(split)
	for {
		if input.Scan() {//扫描输入内容
			line := input.Text()//把输入内容转换为字符串
			fmt.Println(line)//输出到标准输出
		}
	}
}
输出
This is The 
Golang Standard Library.
Welcome you!

对于简单的读取一行数据,并灵活分隔数据,我们采用Scanner而不是Reader

2.2 os包参数解析

  • Go的命令行参数存储在切片 os.Args,其中第一个参数为可执行文件的名字,其他的参数都是以字符串的形式,存储在slice os.Args当中,可以通过for range 语句来遍历所有的参数。下面是一个例子。
//编译后可执行文件名为paramRead
package main
import (
	"fmt"
	"os"
)
func main(){
	if len(os.Args)!=0{
		//遍历打印slice os.Args中参数
		for i, args := range os.Args{
			fmt.Printf("arg[%d]:%s\n",i,args)
		}
	}
}
$ ./paramRead 5 3 1  //输入参数5 3 1
arg[0]:./paramRead   //第一个参数为可执行文件名称
arg[1]:5 //第二个参数
arg[2]:3 //第三个参数
arg[3]:1 //第四个参数

2.3 flag包参数解析

  • os包参数解析只是把命令行中参数存储在os.Args切片当中,对于需输入多维度参数的可执行文件的使用,可使用golang内置的flag包对参数进行说明,并可设置默认值。
  • flag包的使用方法有两种
flag.Type(name string,defaultvalue Type, helpMsg string) *Type
或
flag.TypeVar(目标参数*Type, name string, value Type, usage string)
  • flag包依据Type的类型、参数标志name,对标志name设置默认值和帮助信息,最终返回一个指向该类型的指针,可通过指针是否为空来判断命令行里是否使用该标志参数,下面是一个例子
//编译后可执行文件名为paramRead
package main
import (
	"flag"
	"fmt"
)
var addr string
var hostname = flag.String("name", "hello","localhost name")//参数名为hostname,默认配置为hello,注释为localhost name

func main(){
	flag.StringVar(&addr, "addr", "127.0.0.1","localhost ip addr")//参数名为addr,默认配置为127.0.0.1,注释为localhost ip addr
	flag.Parse()
	fmt.Println("addr: ", addr)
	fmt.Println("hostname: ", *hostname)
}
  • 命令行执行./paramRead -help,可看到设置的参数帮助信息
$ ./paramRead -help
Usage of ./paramRead:
  -addr string
        localhost ip addr (default "127.0.0.1")
  -name string
        localhost name (default "hello")
  • 命令行参数输入./paramRead -addr 172.25.5.0,对localaddr进行配置,发现addr显示为172.25.5.0,name走默认配置为hello
$ ./paramRead -addr 172.25.5.0
addr:  172.25.5.0
name:  hello

2.4 fmt包参数解析

fmt包中提供fmt.Scan(以空格作为参数分隔符),调用Scan函数时,需指定接收输入的变量名和变量数,直到接收完所有指定的变量数,Scan函数才会返回,回车符也无法提前让它返回。以下给出一个样例。

package main
import "fmt"
func main() {
	fmt.Println("Please enter the firstParam and secondParam: ")
	var firstParam, secondParam string
	fmt.Scan(&firstParam, &secondParam)
	fmt.Printf("firstParam is %s, secondParam is %s\n", firstParam, secondParam)
}
输入输出:
Please enter the firstParam and secondParam: 
hello
world
firstParam is hello, secondParam is world

总结:

  • 文件的读取:普通文件采用os.Open或os.OpenFile及bufio.Reader或bufio.Scanner组合,项目中配置文件介绍了.json、.toml及.conf格式,采用os.Open与ioutil.ReadAll组合方式。
  • 终端输入读取:os.Args、bufio、flag及fmt均可满足需求

参考链接:[golang学习链接]https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.4.html