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

}

 


版权声明:本文为qq_22323251原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。