diff --git a/TODO b/TODO index 661fba172..7f7f9d41c 100644 --- a/TODO +++ b/TODO @@ -26,6 +26,7 @@ gvalid校验支持当第一个规则失败后便不再校验后续的规则, gvalid增加支持对[]rune的长度校验(一个中文占3个字节); ghttp.Request增加对输入参数的自动HtmlEncode机制; 常量命名风格根据golint进行修改; +开放rwmutex包,并将gjson的互斥锁使用自定义的mutex替换; 文档完善: gconv struct tag、 控制器及执行对象注册的Init&Shut方法、 diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index 7267f1fa3..b504d462c 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -9,7 +9,6 @@ package gjson import ( - "sync" "strings" "strconv" "io/ioutil" @@ -21,6 +20,7 @@ import ( "gitee.com/johng/gf/g/encoding/gtoml" "gitee.com/johng/gf/g/util/gstr" "time" + "gitee.com/johng/gf/g/encoding/gjson/internal/rwmutex" ) const ( @@ -29,37 +29,48 @@ const ( // json解析结果存放数组 type Json struct { - mu sync.RWMutex + mu *rwmutex.RWMutex p *interface{} // 注意这是一个指针 c byte // 层级分隔符,默认为"." vc bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率) } // 将变量转换为Json对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义 -func New(value interface{}) *Json { - switch value.(type) { - case map[string]interface{}: - return &Json{ - p : &value, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false , - } - case []interface{}: - return &Json{ - p : &value, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false , - } - default: - // 这里效率会比较低 - b, _ := Encode(value) - v, _ := Decode(b) - return &Json{ - p : &v, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false, - } +func New(value interface{}, safe...bool) *Json { + j := (*Json)(nil) + if value != nil { + switch value.(type) { + case map[string]interface{}: + j = &Json{ + p : &value, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + case []interface{}: + j = &Json{ + p : &value, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + default: + // 这里效率会比较低 + b, _ := Encode(value) + v, _ := Decode(b) + j = &Json{ + p : &v, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false, + } + } + } else { + j = &Json{ + p : nil, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false, + } } + j.mu = rwmutex.New(safe...) + return j } // 编码go变量为json字符串,并返回json字符串指针 @@ -100,7 +111,7 @@ func Load (path string) (*Json, error) { return LoadContent(data, gfile.Ext(path)) } -// 支持的配置文件格式:xml, json, yaml/yml, toml +// 支持的配置文件格式:xml, json, yaml/yml, toml,默认为json func LoadContent (data []byte, dataType...string) (*Json, error) { var err error var result interface{} diff --git a/g/encoding/gjson/internal/rwmutex/mutex.go b/g/encoding/gjson/internal/rwmutex/mutex.go new file mode 100644 index 000000000..cc1055313 --- /dev/null +++ b/g/encoding/gjson/internal/rwmutex/mutex.go @@ -0,0 +1,44 @@ +package rwmutex + +import "sync" + +// RWMutex的封装,支持对并发安全开启/关闭的控制。 +// 但是只能初始化时确定并发安全性,不能在运行时动态修改并发安全特性设置。 +type RWMutex struct { + sync.RWMutex + safe bool +} + +func New(safe...bool) *RWMutex { + mu := new(RWMutex) + if len(safe) > 0 { + mu.safe = safe[0] + } else { + mu.safe = true + } + return mu +} + +func (mu *RWMutex) Lock(force...bool) { + if mu.safe || (len(force) > 0 && force[0]) { + mu.RWMutex.Lock() + } +} + +func (mu *RWMutex) Unlock(force...bool) { + if mu.safe || (len(force) > 0 && force[0]) { + mu.RWMutex.Unlock() + } +} + +func (mu *RWMutex) RLock(force...bool) { + if mu.safe || (len(force) > 0 && force[0]) { + mu.RWMutex.RLock() + } +} + +func (mu *RWMutex) RUnlock(force...bool) { + if mu.safe || (len(force) > 0 && force[0]) { + mu.RWMutex.RUnlock() + } +} \ No newline at end of file diff --git a/g/encoding/gparser/gparser.go b/g/encoding/gparser/gparser.go index 2966448de..75d4b490c 100644 --- a/g/encoding/gparser/gparser.go +++ b/g/encoding/gparser/gparser.go @@ -17,12 +17,9 @@ type Parser struct { } // 将变量转换为Parser对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义 -// 该参数为非必需参数,默认为创建一个空的Parser对象 -func New (values...interface{}) *Parser { - if len(values) > 0 { - return &Parser{gjson.New(values[0])} - } - return &Parser{gjson.New(nil)} +// value可以传递nil, 表示创建一个空的Parser对象 +func New (value interface{}, safe...bool) *Parser { + return &Parser{gjson.New(value, safe...)} } func Load (path string) (*Parser, error) { diff --git a/g/g.go b/g/g.go index 174a5702d..b3aaa8c5c 100644 --- a/g/g.go +++ b/g/g.go @@ -27,10 +27,14 @@ const ( ) // 常用map数据结构(使用别名) -type Map = map[string]interface{} +type Map = map[string]interface{} // 常用list数据结构(使用别名) -type List = []Map +type List = []Map + +// 常用slice数据结构(使用别名) +type Slice = []interface{} +type Array = Slice // 阻塞等待HTTPServer执行完成(同一进程多HTTPServer情况下) func Wait() { diff --git a/g/os/gcmd/gcmd.go b/g/os/gcmd/gcmd.go index 9dac2ba7a..5d37eee2f 100644 --- a/g/os/gcmd/gcmd.go +++ b/g/os/gcmd/gcmd.go @@ -10,10 +10,8 @@ package gcmd import ( "os" - "regexp" "errors" - "strconv" - "strings" + "regexp" ) // 命令行参数列表 @@ -34,8 +32,8 @@ var cmdFuncMap = make(map[string]func()) // 终端命令及函数地址对应表 // 检查并初始化console参数,在包加载的时候触发 // 初始化时执行,不影响运行时性能 func init() { + reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`) Option.options = make(map[string]string) - reg := regexp.MustCompile(`\-\-{0,1}(.+?)=(.+)`) for i := 0; i < len(os.Args); i++ { result := reg.FindStringSubmatch(os.Args[i]) if len(result) > 1 { @@ -57,59 +55,25 @@ func (c *gCmdOption) GetAll() map[string]string { } // 获得一条指定索引位置的value参数 -func (c *gCmdValue) Get(index uint8) string { +func (c *gCmdValue) Get(index uint8, def...string) string { if index < uint8(len(c.values)) { return c.values[index] + } else if len(def) > 0 { + return def[0] } return "" } -// 类型转换 -func (c *gCmdValue) GetInt(key uint8) int { - if v := c.Get(key); v != "" { - i, _ := strconv.Atoi(v) - return i - } - return 0 -} - -// 类型转换bool -func (c *gCmdValue) GetBool(key uint8) bool { - v := c.Get(key) - v = strings.ToLower(v) - if v != "" && v != "0" && v != "false" { - return true - } - return false -} - // 获得一条指定索引位置的option参数; -func (c *gCmdOption) Get(key string) string { +func (c *gCmdOption) Get(key string, def...string) string { if option, ok := c.options[key]; ok { return option + } else if len(def) > 0 { + return def[0] } return "" } -// 类型转换int -func (c *gCmdOption) GetInt(key string) int { - if v := c.Get(key); v != "" { - i, _ := strconv.Atoi(v) - return i - } - return 0 -} - -// 类型转换bool -func (c *gCmdOption) GetBool(key string) bool { - v := c.Get(key) - v = strings.ToLower(v) - if v != "" && v != "0" && v != "false" { - return true - } - return false -} - // 绑定命令行参数及对应的命令函数,注意命令函数参数是函数的内存地址 // 如果操作失败返回错误信息 func BindHandle (cmd string, f func()) error { diff --git a/g/os/genv/genv.go b/g/os/genv/genv.go index 7b413b7bf..38e7c6d5b 100644 --- a/g/os/genv/genv.go +++ b/g/os/genv/genv.go @@ -13,8 +13,13 @@ func All() []string { return os.Environ() } -func Get(k string) string { - return os.Getenv(k) +// 获取环境变量,并可以指定当环境变量不存在时的默认值 +func Get(k string, def...string) string { + v, ok := os.LookupEnv(k) + if !ok && len(def) > 0 { + return def[0] + } + return v } func Set(k, v string) error { diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 9f7bd271f..14b094a7b 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -11,7 +11,6 @@ import ( "os" "io" "io/ioutil" - "sort" "fmt" "time" "strings" @@ -24,6 +23,7 @@ import ( "gitee.com/johng/gf/g/util/gregex" "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/os/gfilepool" + "sort" ) // 封装了常用的文件操作方法,如需更详细的文件控制,请查看官方os包 @@ -241,9 +241,20 @@ func Copy(src string, dst string) error { return nil } -// 文件名正则匹配查找 -func Glob(pattern string) ([]string, error) { - return filepath.Glob(pattern) +// 文件名正则匹配查找,第二个可选参数指定返回的列表是否仅为文件名(非绝对路径),默认返回绝对路径 +func Glob(pattern string, onlyNames...bool) ([]string, error) { + if list, err := filepath.Glob(pattern); err == nil { + if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 { + array := make([]string, len(list)) + for k, v := range list { + array[k] = Basename(v) + } + return array, nil + } + return list, nil + } else { + return nil, err + } } // 文件/目录删除 @@ -290,32 +301,49 @@ func Chmod(path string, mode os.FileMode) error { return os.Chmod(path, mode) } -// 打开目录,并返回其下一级子目录名称列表,按照文件名称大小写进行排序,支持目录递归遍历。 -// 当递归遍历时,结果集返回的是子级文件/目录的绝对路径,而不仅仅是一个名字 -func ScanDir(path string, recursive ... bool) []string { - f, err := os.Open(path) +// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。 +func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) { + list, err := doScanDir(path, pattern, recursive...) if err != nil { - return nil + return nil, err } + if len(list) > 0 { + sort.Strings(list) + } + return list, nil +} - list, err := f.Readdirnames(-1) - f.Close() +// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果 +func doScanDir(path string, pattern string, recursive ... bool) ([]string, error) { + var list []string + // 打开目录 + dfile, err := os.Open(path) if err != nil { - return nil + return nil, err + } + defer dfile.Close() + // 读取目录下的文件列表 + names, err := dfile.Readdirnames(-1) + if err != nil { + return nil, err } // 是否递归遍历 - if len(recursive) > 0 && recursive[0] && len(list) > 0 { - for k, v := range list { - p := fmt.Sprintf("%s%s%s", path, Separator, v) - list[k] = p - if IsDir(p) { - list = append(list, ScanDir(p, true)...) + if len(recursive) > 0 && recursive[0] && len(names) > 0 { + for _, name := range names { + path := fmt.Sprintf("%s%s%s", path, Separator, name) + if IsDir(path) { + array, _ := doScanDir(path, pattern, true) + if len(array) > 0 { + list = append(list, array...) + } + } + // 满足pattern才加入结果列表 + if match, err := filepath.Match(pattern, name); err == nil && match { + list = append(list, path) } } } - // 默认按照字符串大小排序 - sort.Slice(list, func(i, j int) bool { return list[i] < list[j] }) - return list + return list, nil } // 将所给定的路径转换为绝对路径 diff --git a/g/os/gfsnotify/gfsnotify.go b/g/os/gfsnotify/gfsnotify.go index ec21ab9ad..d64b6430c 100644 --- a/g/os/gfsnotify/gfsnotify.go +++ b/g/os/gfsnotify/gfsnotify.go @@ -113,8 +113,9 @@ func (w *Watcher) addWatch(path string, callback func(event *Event)) error { // 递归添加监控 func (w *Watcher) Add(path string, callback func(event *Event)) error { if gfile.IsDir(path) { - list := []string{path} - list = append(list, gfile.ScanDir(path, true)...) + paths, _ := gfile.ScanDir(path, "*", true) + list := []string{path} + list = append(list, paths...) for _, v := range list { if err := w.addWatch(v, callback); err != nil { return err @@ -136,8 +137,9 @@ func (w *Watcher) removeWatch(path string) error { // 递归移除监听 func (w *Watcher) Remove(path string) error { if gfile.IsDir(path) { + paths, _ := gfile.ScanDir(path, "*", true) list := []string{path} - list = append(list, gfile.ScanDir(path, true)...) + list = append(list, paths...) for _, v := range list { if err := w.removeWatch(v); err != nil { return err diff --git a/geg/os/gfile/gfile.go b/geg/os/gfile/gfile.go index 963495178..70784be2f 100644 --- a/geg/os/gfile/gfile.go +++ b/geg/os/gfile/gfile.go @@ -4,6 +4,7 @@ package main import ( "gitee.com/johng/gf/g/os/gfile" "fmt" + "gitee.com/johng/gf/g/util/gutil" ) var dirpath1 = "/home/john/Workspace/temp/" @@ -22,8 +23,7 @@ func info () { } func scanDir() { - files := gfile.ScanDir(dirpath1) - fmt.Println(files) + gutil.Dump(gfile.ScanDir(dirpath1, "*")) } func getContents() { diff --git a/geg/os/gfile/gfile_scan.go b/geg/os/gfile/gfile_scan.go index f47f918b0..20c4efecb 100644 --- a/geg/os/gfile/gfile_scan.go +++ b/geg/os/gfile/gfile_scan.go @@ -6,6 +6,6 @@ import ( ) func main() { - gutil.Dump(gfile.ScanDir("/home/john/Documents")) - gutil.Dump(gfile.ScanDir("/home/john/temp/newproject", true)) + gutil.Dump(gfile.ScanDir("/home/john/Documents", "*")) + gutil.Dump(gfile.ScanDir("/home/john/temp/newproject", "*", true)) } \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 1268228e6..352b493ed 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -8,7 +8,7 @@ import ( ) func main() { - files := gfile.ScanDir("/home/john/Workspace/med3-svr", true) + files, _ := gfile.ScanDir("/home/john/Workspace/med3-svr", "*", true) for _, file := range files { if strings.Index(gfcache.GetContents(file), "ENV") != -1 { fmt.Println(file) diff --git a/geg/other/test2.go b/geg/other/test2.go new file mode 100644 index 000000000..fa943700f --- /dev/null +++ b/geg/other/test2.go @@ -0,0 +1,11 @@ +package main + +import ( + "gitee.com/johng/gf/g/util/gutil" + "gitee.com/johng/gf/g/os/gfile" +) + +func main() { + gutil.Dump(gfile.ScanDir("/tmp", "*test*")) + gutil.Dump(gfile.Glob("/tmp/*", true)) +} \ No newline at end of file