golang源碼閱讀-flag.Int

flag.int使用簡單示例

func main() {
	var age = flag.Int("age", 18, "age age")
	var height = flag.Int("height", 20, "height height")
	var b = flag.Bool("b", false, "height height")

	flag.Parse()
	//flag.Usage()
	fmt.Printf("age=%d  height=%d bool=%t", *age, *height, *b)
}
//go run main.go -age=30 -height=60 -b
//res: age=30  height=60 bool=true

整體大概邏輯: 

a. 在調用flag.Int時,生成Flag實例,而Flag 又放在了 FlagSet的formal map中,將對應變量指針返回

b. 在調用flag.Parse()後,解析出各變量的值,複製給對應的指針,最後在打印時就能拿到對應的值

 

1. flag.Int 代碼:

//CommandLine 是實例化的FlagSet
func Int(name string, value int, usage string) *int {
    //int方法調用了FlagSet的Int方法
	return CommandLine.Int(name, value, usage)
}

2.CommandLine.Int 代碼

func (f *FlagSet) Int(name string, value int, usage string) *int {
	 //自己生成int
     p := new(int)
     //p 這裏傳的是指針
	f.IntVar(p, name, value, usage)
    //將指針返回
	return p
}

3.f.IntVar 代碼

func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
     //newIntValue 就是將默認值value複製給p,同時將p轉化成intValue,intValue就是int,只是
     //實現了Set() string()等方法
	f.Var(newIntValue(value, p), name, usage)
}

4.f.Var 代碼

func (f *FlagSet) Var(value Value, name string, usage string) {
	//Flag結構體 存儲命令
    flag := &Flag{
		name, usage, value, value.String(),
	}

	//判斷是否有重複的
	_, alreadythere := f.formal[name]
   //如果是重複的name 使用panic
	if alreadythere {
		var msg string
		if f.name == "" {
			msg = fmt.Sprintf("flag redefined: %s", name)
		} else {
			msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
		}
		fmt.Fprintln(f.Output(), msg)
		panic(msg)
	}

     //將name 和 flag存到FlagSet的map中
	if f.formal == nil {
		f.formal = make(map[string]*Flag)
	}

	f.formal[name] = flag
}

5. flag.Parse()

func Parse() {
    //os.Args 值 [C:\Users\xuxinxin\AppData\Local\Temp\go-build934697874\b001\exe\main.exe -age=30 -height=60 -b]
    //將參數傳入
	CommandLine.Parse(os.Args[1:])
}

6. CommandLine.Parse 代碼

func (f *FlagSet) Parse(arguments []string) error {
    //標記調用過Parse
	f.parsed = true
    //存了參數
	f.args = arguments

	for {
        //不停的解析f.args 參數
		seen, err := f.parseOne()

		if seen {
            //說明還有參數要解析
			continue
		}
        //沒有參數需要解析了,並且沒有錯誤則退出循環
		if err == nil {
			break
		}

		switch f.errorHandling {
		case ContinueOnError:
			return err
		case ExitOnError:
			os.Exit(2)
		case PanicOnError:
			panic(err)
		}
	}
	
	return nil
}

7.

func (f *FlagSet) parseOne() (bool, error) {
	//沒有參數結束
	if len(f.args) == 0 {
		return false, nil
	}
    //獲取第一個參數如: -height=60
	s := f.args[0]
    //如果長度小於2  或者 第一個字符不是 - 則退出
    //如果height=60 -age=10, age也不會被解析了
	if len(s) < 2 || s[0] != '-' {
		return false, nil
	}

    //提取去掉-的 name
	numMinuses := 1
	if s[1] == '-' {
		numMinuses++
        //第二字符還是- 並且長度只有2,停止解析
		if len(s) == 2 {
			f.args = f.args[1:]
			return false, nil
		}
	}
    
    //獲取name 如 -height=80 -> height=80
	name := s[numMinuses:]
    //異常情況判斷
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		return false, f.failf("bad flag syntax: %s", s)
	}

    //修改參數的目的是 外部再調用 parseOne時,解析的參數就是下一個了
	f.args = f.args[1:]
    //開始解析第一個值 name
	hasValue := false
	value := ""
  
    //遍歷name 找到 = 
	for i := 1; i < len(name); i++ {
   
		if name[i] == '=' {
			value = name[i+1:]
			hasValue = true
			name = name[0:i]
           //如果有= 則 name=height value = 80
			break
		}
	}

    //獲取 flag.Int 類似方法,用戶設置了哪些
	m := f.formal
    
	flag, alreadythree := m[name]
    //如果解析出來的不在 已經設置的formal map中
	if !alreadythree {
		if name == "help" || name == "h" { // special case for nice help message.
			f.usage()
			return false, ErrHelp
		}

		return false, f.failf("flag provided but not defined: -%s", name)
	}

    //判斷是不是bool
	if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() {
		if hasValue {
            //如果解析出值了 則直接改變 *p的值 類似於 *p = true
			if err := fv.Set(value); err != nil {
				return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
			}
		} else {
             //沒有則設爲true
			if err := fv.Set("true"); err != nil {
				return false, f.failf("invalid boolean flag %s: %v", name, err)
			}
		}
	} else {
		if !hasValue && len(f.args) > 0 {
            //沒有 = 則 name對應的值就是下一個
            //就是者這種情況 -height 80  => name=height value=80
			hasValue = true
			value, f.args = f.args[0], f.args[1:]
		}
		if !hasValue {
			return false, f.failf("flag needs an argument: -%s", name)
		}
        //修改值
		if err := flag.Value.Set(value); err != nil {
			return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
		}
	}

//解析好的存起來
	if f.actual == nil {
		f.actual = make(map[string]*Flag)
	}
	f.actual[name] = flag

    //正常解析了 調用 parseOne的for循環 可以再次調用parseOne了
	return true, nil

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章