暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Go-原生包源码看看(flag/fmt包)

花一个无所 2019-10-19
465

题图: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 = val
      return (*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 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) 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 to
        a custom error handler. What happens after Usage is called depends
        on the ErrorHandling setting; for the command line, this defaults
        to ExitOnError, which exits the program after calling Usage.
        Usage func()


        name string
        parsed bool
        actual map[string]*Flag
        formal map[string]*Flag
        args []string / arguments after flags
        errorHandling ErrorHandling
        output 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 line
        Usage string // help message
        Value Value // value as set
        DefValue string // default value (as text); for usage message
        }

        Parse

          func (f *FlagSet) Parse(arguments []string) error {
          //解析标志
          f.parsed = true
          //参数
          f.args = arguments
          for {
          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
          }
          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 = false
              p.erroring = false
              p.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 bool
                    precPresent bool
                    // '-' => rue
                    minus bool
                    // '+' => true
                    plus bool
                    // '#' => true
                    sharp bool
                    // ' ' => true
                    space bool
                    // '0' => true
                    zero 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' => true
                    plusV bool
                    // '#v' => true
                    sharpV bool
                    }
                      func (p *pp) Flag(b int) bool {
                      switch b {
                      case '-':
                      return p.fmt.minus
                      case '+':
                      return p.fmt.plus || p.fmt.plusV
                      case '#':
                      return p.fmt.sharp || p.fmt.sharpV
                      case ' ':
                      return p.fmt.space
                      case '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 = true
                          defer 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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                          评论