
题图:pixabay
flag包
很早就想研究下这个包,一直用。但不明白为啥用,有啥用,可否作别用。
这个包的主要作用是解析命令行参数。
从我们线上代码一步步解析
//参数释义:命令行参数,默认值,字段描述configPath := flag.String("c", common.ConfPath, "config file's path")p := flag.Int("p", common.DefaultPort, "监听端口")flag.Parse()
上面是我线上的代码,下面看看源码调用链:
func String(name string, value string, usage string) *string {return CommandLine.String(name, value, usage)}// String defines a string flag with specified name, default value, and usage string.// The return value is the address of a string variable that stores the value of the flag.func (f *FlagSet) String(name string, value string, usage string) *string {p := new(string)f.StringVar(p, name, value, usage)return p}// 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 (f *FlagSet) StringVar(p *string, name string, value string, usage string) {f.Var(newStringValue(value, p), name, usage)}//把默认值附上//stringValue 实现了Value类型func newStringValue(val string, p *string) *stringValue {*p = valreturn (*stringValue)(p)}//塞到FlagSet结构体里func (f *FlagSet) Var(value Value, name string, usage string) {Remember the default value as a string; it won't change.flag := &Flag{name, usage, value, value.String()}_, alreadythere := f.formal[name]if alreadythere {var msg stringif 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) Happens only if flags are declared with identical names}if f.formal == nil {f.formal = make(map[string]*Flag)}f.formal[name] = flag}
关键的结构体
type FlagSet struct {Usage is the function called when an error occurs while parsing flags.The field is a function (not a method) that may be changed to point toa custom error handler. What happens after Usage is called dependson the ErrorHandling setting; for the command line, this defaultsto ExitOnError, which exits the program after calling Usage.Usage func()name stringparsed boolactual map[string]*Flagformal map[string]*Flagargs []string / arguments after flagserrorHandling ErrorHandlingoutput io.Writer // nil means stderr; use out() accessor}// A Flag represents the state of a flag.type Flag struct {Name string // name as it appears on command lineUsage string // help messageValue Value // value as setDefValue string // default value (as text); for usage message}
Parse
func (f *FlagSet) Parse(arguments []string) error {//解析标志f.parsed = true//参数f.args = argumentsfor {seen, err := f.parseOne()if seen {continue}if err == nil {break}switch f.errorHandling {case ContinueOnError:return errcase ExitOnError:os.Exit(2)case PanicOnError:panic(err)}}return nil}func (f *FlagSet) parseOne() (bool, error) {}
那么有一个问题?初始化默认值塞结构体跟Parse 是两个步骤。为嘛可以直接用 p := flag.Int
?
因为 flag.Type()
返回的是一个指针。在 Parse 函数里有一行flag.Value.Set(value)
把地址替换了。所以还是可以用指针的取值方式去用。
其他的用法自己了解。
fmt包
这个包还是从实际应用场景开始说起。
logger.Warn(middleware.BAMAI_DFLAG_UNDEF, fmt.Sprintf("call cc rpc fail, err: %v, params: %v", err, params))
func Sprintf(format string, a ...interface{}) string {p := newPrinter()p.doPrintf(format, a)s := string(p.buf)p.free()return s}//初始化一个pp。这里就不得不讲到pp是个啥玩意儿?func newPrinter() *pp {//从临时对象池中取一个pp。没有就new一个p := ppFree.Get().(*pp)p.panicking = falsep.erroring = falsep.fmt.init(&p.buf)return p}
//pp是一个格式化的容器,用来保存打印时的状态信息type pp struct {//数据缓冲区buf buffer// arg holds the current item, as an interface{}.//当前需要格式化的参数arg interface{}// value is used instead of arg for reflect values.//参数反射值value reflect.Value// fmt is used to format basic items such as integers or strings.//格式化基础类型//这个fmt又是啥玩意儿?在下面fmt fmt// reordered records whether the format string used argument reordering.//是否使用了自定义的参数序号reordered bool// goodArgNum records whether the most recent reordering directive was valid.//参数序号是否合法goodArgNum bool// panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.//避免持续恐慌panicking bool// erroring is set when printing an error string to guard against calling handleMethods.erroring bool}
type fmt struct {//缓冲区buf *buffer//这又是个啥玩意儿?下面fmtFlags//参数宽度wid int // width//参数精度prec int // precision// intbuf is large enough to store %b of an int64 with a sign and// avoids padding at the end of the struct on 32 bit architectures.intbuf [68]byte}
//格式串中的占位符类型处理。比如#、+、-、零值、空格等等//配合下面的方法使用type fmtFlags struct {widPresent boolprecPresent bool// '-' => rueminus bool// '+' => trueplus bool// '#' => truesharp bool// ' ' => truespace bool// '0' => truezero bool// For the formats %+v %#v, we set the plusV/sharpV flags// and clear the plus/sharp flags since %+v and %#v are in effect// different, flagless formats set at the top level.// '+v' => trueplusV bool// '#v' => truesharpV bool}
func (p *pp) Flag(b int) bool {switch b {case '-':return p.fmt.minuscase '+':return p.fmt.plus || p.fmt.plusVcase '#':return p.fmt.sharp || p.fmt.sharpVcase ' ':return p.fmt.spacecase '0':return p.fmt.zero}return false}
pp还有一些方法
type State interface {// Write is the function to call to emit formatted output to be printed.//写入格式化的文本Write(b []byte) (n int, err error)// Width returns the value of the width option and whether it has been set.//返回宽度值,及其是否被设置Width() (wid int, ok bool)// Precision returns the value of the precision option and whether it has been set.//返回精度值,及其是否被设置Precision() (prec int, ok bool)// Flag reports whether the flag c, a character, has been set.//返回特殊标志符('#'、'0'、'+'、'-'、' ')是否被设置Flag(c int) bool}//前三个方法 + 上面那个方法实现了上面的接口func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent }//func (p *pp) Write(b []byte) (ret int, err error) {}//判断 p.arg 是否有自定义的格式化方法,如果有就调用,如果没有,就返回 false//针对panic做了处理func (p *pp) handleMethods(verb rune) (handled bool) {}//处理基本类型的arg//如果存在自定义的格式化方法,利用反射处理//arg:参数值//verb:占位符类型//方法中通过switch参数的type,分别处理不同的参数类型。func (p *pp) printArg(arg interface{}, verb rune) {}//处理复杂类型,比如struct、interface、map、slice、array、ptr、func (p *pp) printValue(value reflect.Value, verb rune, depth int) {}//解析格式字符串中的"占位符"//从指定 arg 中读取整数值func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) {}//解析 arg 索引func parseArgNumber(format string) (index int, wid int, ok bool) {}//解析 format 中的一个 arg 索引,i 标识索引字符串的起始位置func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {}//解析占位符并格式化相应 arg,以替换占位符//这个方法很关键func (p *pp) doPrintf(format string, a []interface{}) {}
golang格式化语法
•% 开始位置•v 占位符•"+" flag•宽度、精度
占位符
•%v 默认格式打印•%t 布尔类型•%d 以10进制格式打印数字•%c 将数据转换成 Unicode 里面的字符打印•%e 科学计数法表示•%f 以10进制表示浮点数•%s 字符串•%p 指针,以0x开头的16进制地址•%#v 自定义类型。
•%+v 打印完整数据,包括field + value
下面是格式化过程中的一些小技巧
//实现自定义格式化方法,可以通过在自定义结构体中实现 Format 方法来实现这个目的type Formatter interface {Format(f State, c rune)}//handleMethods方法会调用//如果没有实现 Format ,当 采用 %#v 作为占位符时,则会尽可能的用 GoStringer 接口的 GoString 来格式化处理type GoStringer interface {GoString() string}if p.fmt.sharpV {if stringer, ok := p.arg.(GoStringer); ok {handled = truedefer p.catchPanic(p.arg, verb, "GoString")// Print the result of GoString unadorned.p.fmt.fmtS(stringer.GoString())return}} else {//(%v %s %q %x %X)作为占位符,或者被不使用格式字符串-如Print函数打印操作数时,会调用String方法来生成输出的文本type Stringer interface {String() string}
文章转载自花一个无所,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




