Go語言中的IO操作、Flag包以及urfave/cli命令行框架


一、格式化輸入和輸出

1.從終端獲取用戶的輸入

fmt.Scanf  空格作為分隔符,占位符和格式化輸出的一致

fmt.Scan  從終端獲取用戶的輸入,存儲在Scanln中的參數里,空格和換行符作為分隔符

fmt.Scanln 從終端獲取用戶的輸入,存儲在Scanln中的參數里,空格作為分隔符,換行符作為結束

Scanf 例子:

package main

import (
   "fmt"
)

func TestScanf(){
   var a int
   var b string
   var c float32
   fmt.Scanf("%d%s%f",&a,&b,&c)
   fmt.Printf("a=%d b=%s c=%f",a,b,c)
}

func main(){
   TestScanf()
}

結果:

> 111 abvc 111
a=111 b=abvc c=111.000000

這里我們是通過一行輸入的,並且是以空格作為分割的,但是如果想通過多行輸入,那么在Windows下Scanf可能會存在一個問題,如下:

func TestScanf(){
   var a int
   var b string
   var c float32
   fmt.Scanf("%d",&a)
   fmt.Scanf("%s",&b)
   fmt.Scanf("%f",&c) 
   fmt.Printf("a=%d b=%s c=%f",a,b,c)
}

然后我們在windows下面去運行一下

 

由上面的我們可以看到,只接受到了第一個值,第二值輸入之后回車,發現b和c都沒有值。主要是由於Windows下面,回車會 自動加上 \r\n。

如果想要正確的獲取這個值,可以修改如下:在每一個scanf上面都加上“\n”

func TestScanf(){
   var a int
   var b string
   var c float32
   fmt.Scanf("%d\n",&a)
   fmt.Scanf("%s\n",&b)
   fmt.Scanf("%f\n",&c)
   fmt.Printf("a=%d b=%s c=%f \n",a,b,c)
}

Scan例子:

func TestScan(){
   var a int
   var b string
   var c float32
   fmt.Scan(&a,&b,&c)
   fmt.Printf("a=%d b=%s c=%f \n",a,b,c)
}

Scanln例子:

func TestScanln(){
   var a int
   var b string
   var c float32
   fmt.Scanln(&a)
   fmt.Scanln(&b)
   fmt.Scanln(&c)
   fmt.Printf("a=%d b=%s c=%f \n",a,b,c)
}

2.從字符串中獲取

上面是從終端輸入的字符串中提取數據,然后還可以從一個已經存在的字符串中提取。

fmt.Sscanf  空格作為分隔符,占位符和格式化輸出的一致

fmt.Sscan  從字符串獲取用戶的輸入,存儲在Scanln中的參數里,空格和換行符作為分隔符

fmt.Sscanln 從字符串獲取用戶的輸入,存儲在Scanln中的參數里,空格作為分隔符,換行符作為結束

下面的例子就是從str中提取整數、字符串和浮點數

func TestSscanf(){
   var a int
   var b string
   var c float32
   str := "123 hello 111.1"
   fmt.Sscanf(str,"%d%s%f",&a,&b,&c)
   fmt.Printf("a=%d b=%s c=%f \n",a,b,c)
}

備注:一定要傳地址進去,否則修改是變量的副本,也就是說讀取不到用戶的輸入了。

 

3.從文件中獲取

fmt.Fscanf  空格作為分隔符,占位符和格式化輸出的一致

fmt.Fscan  從文件獲取用戶的輸入,存儲在Scanln中的參數里,空格和換行符作為分隔符

fmt.Fscanln 從文件獲取用戶的輸入,存儲在Scanln中的參數里,空格作為分隔符,換行符作為結束

 

4.終端輸入輸出的原理

終端相當於是一個文件, 所以可以用 os.stdin 和os.stdout 作為輸入和輸出。查看源碼可以看到他們其實就是*File

 

二、bufio包的使用

為了完善文件本身讀取性能差的問題,增加了緩沖區操作。

package main

import (
   "bufio"
   "os"
)

func main() {
   reader := bufio.NewReader(os.Stdin)
   buf, _ := reader.ReadBytes('\n')
   println((string)(buf))
}

備注:這里可以看到NewReader的參數是一個io.Reader接口 , os.Stdin則是返回一個*File ,由於File實現了io.Reader的Read方法,所以可以接受File作為參數(鴨子類型)。

 

三、命令行參數處理以及urfave/cli使用

1.通過Os.Args獲取cli的參數

package main

import (
   "fmt"
   "os"
)

func main() {

   if(len(os.Args) > 1) {
      for _,value := range os.Args{
         fmt.Printf( "%v\n",value)
      }
      return
   }
   fmt.Printf( "%s","沒輸入參數")
}

2. 增加命令行選項

我們經常能看到 使用一些命令行會有很多選項。例如  ls -l 等

可以使用flag包來獲取選項,例如下面的代碼:

package main

import (
    "flag"
    "fmt"
)

var recusive bool
var test string
var level int

func init() {
    flag.BoolVar(&recusive, "r", false, "Recusive xxxx")
    flag.StringVar(&test, "t", "Default String", "string option")
    flag.IntVar(&level, "l", 1, "level of xxxx")
    flag.Parse()
}

func main() {
    fmt.Println("recusive:", recusive)
    fmt.Println("test:", test)
    fmt.Println("level:", level)
}

init函數內部使用了flag包中的BoolVar、StringVar以及IntVar等方法,標記了命令的選項。

// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func StringVar(p *string, name string, value string, usage string) {
    CommandLine.Var(newStringValue(value, p), name, usage)
}

像源碼中描述那樣,第一個參數用來接收輸入的參數值,第二個用來定義參數名稱(-l -r 等),第三個是默認參數、第四個是使用方法。

於是像上面的代碼我們就可以這樣使用:這里BoolVar的默認值是false, -r后面不增加其他參數,不用 -r true 這樣。

3.urfave/cli的簡單使用

urfave/cli是一個命令行的框架。舉例說明:

package main

import (
    "fmt"
    "os"

    "github.com/urfave/cli" //必須使用這個包
)

func main() {

    //定義兩個變量用於接收控制台輸入的值
    var stringValue string
    var boolValue bool

    //new一個app出來,就是我們的命令行程序
    app := cli.NewApp()
    app.Name = "TestCliApp" //起個名稱
    app.Usage = "Test"      //描述一下用途
    app.Version = "2.0.0"   //設置一下版本號
    //重點可以設置一些選項操作
    //第一個是一個字符串的選項,第二個是一個布爾的選項
    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:        "StringOption,s",
            Value:       "DefaultValue",
            Usage:       "Display a string value",
            Destination: &stringValue,
        },
        cli.BoolFlag{
            Name:        "BoolOption,b",
            Usage:       "Display a bool value",
            Destination: &boolValue,
        },
    }
    
    //定義我們命令行程序主要的工作
    app.Action = func(c *cli.Context) error {

        if c.NArg() > 0 {
            cmds := c.Args()
            for index, v := range cmds {
                fmt.Printf("args[%d]=%v\t", index, v)
            }
        } else {
            fmt.Println("No Args")
        }

        fmt.Println("stringOption", stringValue)
        fmt.Println("boolOption", boolValue)
        return nil
    }

    //執行程序
    app.Run(os.Args)

}

有幾個點:

1.定義Flag的時候,Name可以用【,】分割,前面長的 可以用 --XXXX 來增加參數,后面則是短名 -x 來表示

2.默認會有help和version兩個選項

3.我在使用的時候發現,當輸入正常的 -b -s Hello 這樣的選項的時候,C.NArg()返回的值是0,而輸出錯誤的參數列表的時候,則是有值存在的。

 我們可以用 -h (--help)查看一下生成的文檔

 

可以參考具體的文檔來構建自己的命令


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM