位运算
go中位运算符的使用 ,有什么常用场景?
首先了解一下位运算
(1) & 按位与,两数各对应的⼆进位相与 同⼀位同时为1则为1 252 & 63 = 60
(2)|按位或,两数各对应的⼆进位相或 同⼀位其中一个为1则为1 178|94 = 254
(3) ^ 按位异或, 同⼀位不相同则为1 20 ^ 5 = 17
(4) << 左移运算符 3 << 4 = 48 将数字3左移4位 左移n位就相当于乘以2的n次⽅
(5) >> 右移运算符 右移n位相当于除以2的n次⽅
利用位运算设置参数
每个权限数字都是 2 的 n 次幂,用二进制来表示只有一个 1。每位 1 代表一个权限,当进行或运算的时候,把其他权限的 1加了进来,就代表有多个权限了。
这点和linux中的权限相似,比如 rwx(读写执行)
r = 4(0100) w = 2(0010) x = 1(0001)
r|w = 6 (0110) r|w|x = 7 (0111)
判断具有某个权限:把结果再与某个权限做与运算,当具有该权限时候,其他位都为 0,该权限位为 1,正好等于该权限的数值。所以如果与运算不为零或者结果和某个权限数值相等就代表具有该权限
6&r = 0100 == r != 0
6&w = 0010 == w != 0
6&x = 0000 == 0
6&(r|w) = 0110 == r|w
实现
gf框架日志组件中:
// 日志级别
logger.SetLevel(glog.LEVEL_ALL)
func (l *Logger) SetLevel(level int) {
l.config.Level = level | LEVEL_CRIT | LEVEL_PANI | LEVEL_FATA
}
// 级别常量
const (
LEVEL_ALL = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
LEVEL_DEV = LEVEL_ALL
LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
LEVEL_DEBU = 1 << iota // 8
LEVEL_INFO // 16
LEVEL_NOTI // 32
LEVEL_WARN // 64
LEVEL_ERRO // 128
LEVEL_CRIT // 256
LEVEL_PANI // 512
LEVEL_FATA // 1024
)
// 日志打印中的参数判断
// 判断Flags是否为其中某个参数
if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 {
callerFnName, path, line := gdebug.CallerWithFilter([]string{pathFilterKey}, l.config.StSkip)
if l.config.Flags&F_CALLER_FN > 0 {
if len(callerFnName) > 2 {
input.CallerFunc = fmt.Sprintf(`[%s]`, callerFnName)
}
}
if line >= 0 && len(path) > 1 {
if l.config.Flags&F_FILE_LONG > 0 {
input.CallerPath = fmt.Sprintf(`%s:%d:`, path, line)
}
if l.config.Flags&F_FILE_SHORT > 0 {
input.CallerPath = fmt.Sprintf(`%s:%d:`, gfile.Basename(path), line)
}
}
}
itoa
上面示例中的常量定义涉及到go特有的itoa
- iota每遇到一个const都会清零
- 默认从0开始 (itoa不一定在第一个数出现,比如在第4个出现,itoa值为3)
- const 集合中从上到下,iota是逐步递增的
- iota遇到主动赋值的条目时,并不会终止累加,而是会继续隐式增加iota的值