go basic, golang basic

go basic, golang basic The Go Programming Language, Go 语言虽然是静态编译型语言, 但是它却拥有脚本化的语法, 支持多种编程范式(函数式和面向对象)。 Go 是 Google 开发的一种静态强类型、编译型、并发型, 并具有垃圾回收功能的编程语言。 罗伯特·格瑞史莫, 罗勃·派克及肯·汤普逊于 2007年9月开始设计 Go, 稍后 Ian Lance Taylor, Russ Cox 加入项目。 Go 是基于 Inferno 操作系统所开发的。 Go 语言是静态类型的编程语言 go source code https://github.com/golang/go version latest: 1.23.0 current: 1.17.7 install # macos brew search golang brew upgrade golang hello world package main import "fmt" func main() { fmt.Println("hello world") } go run hello-world.go go build hello-world.go ./hello-world 升级依赖包版本, upgrade package version go list -m all|grep redis # go list 返回: github.com/redis/go-redis/v9 v9.0.2 # 升级 go-redis 版本 go get -u github.com/redis/go-redis/v9 # -u The -u flag instructs get to update modules providing dependencies of packages named on the command line # to use newer minor or patch releases when available. # 不带 -u 的 go get 在发现依赖包已经存在的时候不会更新, 加 -u 参数会检查依赖包是否有更新然后下载新版本. # 升级 go-redis 到 9.0.4 go get -u github.com/redis/go-redis/v9@v9.0.4 -u 更新包,包括他们的依赖项 -v 输出详细信息 http://studygolang.com/articles/1941 ...

2022-12-11 · 7 min · 1425 words · -

athens

athens, go package cache/repo 私服, go package 私有仓库, go package 服务器 Athens: Go Packages 服务器, 私服, go package 私有仓库, 其它可选方案 Nexus. nerdctl nerdctl volume create athens-data nerdctl run -d \ --name athens \ -p 4000:3000 \ -v athens-data:/var/lib/athens \ -e ATHENS_STORAGE_TYPE=disk \ -e ATHENS_DISK_STORAGE_ROOT=/var/lib/athens \ gomods/athens:v0.16.0 docker docker volume create athens-data docker run -d \ --name athens \ -p 4000:3000 \ -v athens-data:/var/lib/athens \ -e ATHENS_STORAGE_TYPE=disk \ -e ATHENS_DISK_STORAGE_ROOT=/var/lib/athens \ gomods/athens:v0.15.4 podman volume create athens-data vim /var/lib/containers/storage/volumes/athens-data/_data/download-mode downloadURL = "https://goproxy.cn" mode = "async_redirect" podman run -d \ --name athens \ -p 4000:3000 \ -v athens-data:/var/lib/athens \ -e ATHENS_DISK_STORAGE_ROOT=/var/lib/athens \ -e ATHENS_STORAGE_TYPE=disk \ -e ATHENS_DOWNLOAD_MODE=file:/var/lib/athens/download-mode \ gomods/athens:v0.15.4 GOPROXY=http://127.0.0.1:4000 && go get github.com/google/uuid@v1.4.0 GOPROXY=http://192.168.50.63:4000 && go get github.com/google/uuid@v1.4.0 https://docs.gomods.io/ ...

2022-11-29 · 1 min · 118 words · -

ent

“ent” ent 是由 Facebook Connectivity 团队创建的 ORM 框架。迫于 Go 社区中缺少能够像图一样查询数据的工具,同时也缺少 100% 类型安全的 ORM https://entgo.io/

2021-09-03 · 1 min · 14 words · -

go metrics

go metrics import "github.com/jregovic/go-metrics-influxdb" go influxdb.InfluxDB( metrics.DefaultRegistry, time.Duration(10)*time.Second, "http://192.168.50.244:8086", "database0", "measurement0", "", "", false, ) go metrics.Log( metrics.DefaultRegistry, time.Duration(metricsOutputDuration)*time.Second, logger.GetLogger(), ) meter := metrics.GetOrRegisterMeter("foo", nil) meter.Mark(1) https://github.com/rcrowley/go-metrics

2020-04-15 · 1 min · 27 words · -

Golang,Signal

Golang,Signal https://colobu.com/2015/10/09/Linux-Signals/ 信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方式。一个信号就是一个异步的通知,发送给某个进程,或者同进程的某个线程,告诉它们某个事件发生了。 当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。 如果目标进程先前注册了某个信号的处理程序(signal handler),则此处理程序会被调用,否则缺省的处理程序被调用。 signals := make(chan os.Signal) signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM) // ... for s := range signals { if s == os.Interrupt || s == os.Kill || s == syscall.SIGTERM { break } } signal.Stop(signals) Go中的Signal发送和处理 有时候我们想在Go程序中处理Signal信号,比如收到SIGTERM信号后优雅的关闭程序(参看下一节的应用)。 Go信号通知机制可以通过往一个channel中发送os.Signal实现。 首先我们创建一个os.Signal channel,然后使用signal.Notify注册要接收的信号。 package main import “fmt” import “os” import “os/signal” import “syscall” func main() { // Go signal notification works by sending os.Signal // values on a channel. We’ll create a channel to // receive these notifications (we’ll also make one to // notify us when the program can exit). sigs := make(chan os.Signal, 1) done := make(chan bool, 1) // signal.Notify registers the given channel to // receive notifications of the specified signals. signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) // This goroutine executes a blocking receive for // signals. When it gets one it’ll print it out // and then notify the program that it can finish. go func() { sig := <-sigs fmt.Println() fmt.Println(sig) done <- true }() // The program will wait here until it gets the // expected signal (as indicated by the goroutine // above sending a value on done) and then exit. fmt.Println(“awaiting signal”) <-done fmt.Println(“exiting”) } go run main.go执行这个程序,敲入ctrl-C会发送SIGINT信号。 此程序接收到这个信号后会打印退出。 ...

2020-02-24 · 2 min · 368 words · -

Go SOCKS5 proxy, http proxy

golang SOCKS5 proxy, http proxy https://segmentfault.com/a/1190000038247560 https://github.com/wiloon/pingd-proxy https://golangnote.com/topic/258.html

2020-02-23 · 1 min · 8 words · -

golang init

golang init init函数的主要作用: 初始化不能采用初始化表达式初始化的变量。 程序运行前的注册。 实现sync.Once功能。 其他 init函数的主要特点: init函数先于main函数自动执行,不能被其他函数调用; init函数没有输入参数、返回值; 每个包可以有多个init函数; 包的每个源文件也可以有多个init函数,这点比较特殊; 同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。 不同包的init函数按照包导入的依赖关系决定执行顺序。 golang程序初始化 golang程序初始化先于main函数执行,由runtime进行初始化,初始化顺序如下: 初始化导入的包(包的初始化顺序并不是按导入顺序(“从上到下”)执行的,runtime需要解析包依赖关系,没有依赖的包最先初始化,与变量初始化依赖关系类似,参见golang变量的初始化); 初始化包作用域的变量(该作用域的变量的初始化也并非按照“从上到下、从左到右”的顺序,runtime解析变量依赖关系,没有依赖的变量最先初始化,参见golang变量的初始化); 执行包的init函数;

2020-02-18 · 1 min · 19 words · -

golang interface

golang interface interface 是一种类型 type foo interface { Get() int } 首先 interface 是一种类型,从它的定义可以看出来用了 type 关键字,更准确的说 interface 是一种具有一组方法的类型,这些方法定义了 interface 的行为。 go 允许不带任何方法的 interface ,这种类型的 interface 叫 empty interface。 如果一个类型实现了一个 interface 中所有方法,我们说类型实现了该 interface,所以所有类型都实现了 empty interface,因为任何一种类型至少实现了 0 个方法。go 没有显式的关键字用来实现 interface,只需要实现 interface 包含的方法即可。 ‘golang 获取interface{} 的数据类型’ https://blog.csdn.net/xia_xing/article/details/49423771 interface{} 可以接受任何类型的对象值 获取interface{}队形的数据类型,可以使用断言,或者 switch type 来实现 // Assertion project main.go package main import ( “fmt” ) type Bag struct { Key string } type Bag2 struct { Key int } func main() { ...

2020-02-11 · 1 min · 183 words · -

go 调试, pprof, go tool trace

go 调试, pprof, go tool trace 做 Profiling 第一步就是怎么获取应用程序的运行情况数据。go 语言提供了 runtime/pprof 和 net/http/pprof 两个库 http api // pprof 的init函数会将pprof里的一些handler注册到http.DefaultServeMux上 // 当不使用http.DefaultServeMux来提供http api时,可以查阅其init函数,自己注册handler import _ "net/http/pprof" go func() { http.ListenAndServe("0.0.0.0:8080", nil) }() http://localhost:8080/debug/pprof/ cpu go tool pprof http://localhost:8080/debug/pprof/profile?seconds=60 mem go tool pprof http://localhost:6060/debug/pprof/heap block go tool pprof http://localhost:8080/debug/pprof/block mutex go tool pprof http://localhost:6060/debug/pprof/mutex runtime/pprof // CPUProfile enables cpu profiling. Note: Default is CPU defer profile.Start(profile.CPUProfile).Stop() // GoroutineProfile enables goroutine profiling. // It returns all Goroutines alive when defer occurs. defer profile.Start(profile.GoroutineProfile).Stop() // BlockProfile enables block (contention) profiling. defer profile.Start(profile.BlockProfile).Stop() // ThreadcreationProfile enables thread creation profiling. defer profile.Start(profile.ThreadcreationProfile).Stop() // MemProfile changes which type of memory profiling to // profile the heap. defer profile.Start(profile.MemProfile).Stop() // MutexProfile enables mutex profiling. defer profile.Start(profile.MutexProfile).Stop() golang CPU profiling CPU 性能分析(CPU profiling) 是最常见的性能分析类型。 ...

2019-11-20 · 2 min · 298 words · -

go new make

go new make https://sanyuesha.com/2017/07/26/go-make-and-new/ new 和 make 都可以用来分配空间,初始化类型,但是它们确有不同。 new(T) 返回的是 T 的指针 new(T) 为一个 T 类型新值分配空间并将此空间初始化为 T 的零值,返回的是新值的地址,也就是 T 类型的指针 *T,该指针指向 T 的新分配的零值。 p1 := new(int) fmt.Printf("p1 -> %#v \n ", p1) //(*int)(0xc42000e250) fmt.Printf("p1 point to -> %#v \n ", *p1) //0 var p2 _int i := 0 p2 = &i fmt.Printf("p2 -> %#v \n ", p2) //(_int)(0xc42000e278) fmt.Printf("p2 point to -> %#v \n ", *p2) //0 上面的代码是等价的, new(int) 将分配的空间初始化为 int 的零值, 也就是 0, 并返回 int 的指针, 这和直接声明指针并初始化的效果是相同的. ...

2019-09-21 · 3 min · 582 words · -

golang, function types, 函数类型

golang, function types, 函数类型 function types A function type denotes the set of all functions with the same parameter and result types. 示例 package main import "fmt" // function types type Greeting func(name string) string func say(g Greeting, n string) { fmt.Println(g(n)) } func english(name string) string { return "Hello, " + name } func main() { say(english, "World") } 输出Hello, World ...

2019-07-26 · 1 min · 176 words · -

go module proxy, goproxy, athens

go module proxy, goproxy, athens goproxy.io export GOPROXY=https://goproxy.io,direct goproxy.cn export GO111MODULE=on export GOPROXY=https://goproxy.cn GO 版本大于 1.13,可以直接使用 go env -w 命令设置 GOPROXY go env -w GOPROXY=https://goproxy.io,direct # 清除 go env go env -u GOPROXY # 阿里云 export GOPROXY=https://mirrors.aliyun.com/goproxy/ # goproxy.io export GOPROXY=https://goproxy.io,direct # goproxy.cn export GOPROXY=https://goproxy.cn athens docker run -p '3000:3000' gomods/athens:latest https://goproxy.io/zh/ https://blog.wiloon.com/?p=15941 https://shockerli.net/post/go-get-golang-org-x-solution/ https://github.com/goproxy/goproxy.cn GOPRIVATE 环境变量 完成设置后,go 命令会从公共镜像 goproxy.io 上下载依赖包,并且会对下载的软件包和代码库进行安全校验,当你的代码库是公开的时候,这些功能都没什么问题。但是如果你的仓库是私有的怎么办呢? 环境变量 GOPRIVATE 用来控制 go 命令把哪些仓库看做是私有的仓库,这样的话,就可以跳过 proxy server 和校验检查,这个变量的值支持用逗号分隔,可以填写多个值,例如: GOPRIVATE=*.corp.example.com,rsc.io/private 这样 go 命令会把所有包含这个后缀的软件包,包括 git.corp.example.com/xyzzy , rsc.io/private, 和 rsc.io/private/quux 都以私有仓库来对待。 ...

2019-06-20 · 1 min · 120 words · -

Go gc

Go gc gctrace GODEBUG='gctrace=1' go run main.go gc 15 @941.135s 0%: 0.058+91+0.002 ms clock, 0.058+0.35/26/61+0.002 ms cpu, 75->75->38 MB, 77 MB goal, 1 P gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P where the fields are as follows: gc # the GC number, incremented at each GC @#s time in seconds since program start #% percentage of time spent in GC since program start #+…+# wall-clock/CPU times for the phases of the GC #->#-># MB heap size at GC start, at GC end, and live heap # MB goal goal heap size # P number of processors used The phases are stop-the-world (STW) sweep termination, concurrent mark and scan, and STW mark termination. The CPU times for mark/scan are broken down in to assist time (GC performed in line with allocation), background GC time, and idle GC time. If the line ends with “(forced)”, this GC was forced by a runtime.GC() call and all phases are STW. ...

2019-06-11 · 2 min · 392 words · -

golang bytes.buffer

golang bytes.buffer buf := bytes.NewBuffer([]byte{}) bytes.buffer 是一个缓冲 byte 类型的缓冲器,这个缓冲器里存放着都是 byte A buffer is a variable-sized buffer of bytes with Read and Write methods. The zero value for Buffer is an empty buffer ready to use. 创建 Buffer 缓冲器 var b bytes.Buffer // 定义一个 Buffer 变量,不用初始化 b.Writer([]byte("Hello ")) // 可以直接使用 b1 := new(bytes.Buffer) //直接使用 new 初始化,可以直接使用 // 其它两种定义方式 func NewBuffer(buf []byte) *Buffer func NewBufferString(s string) *Buffer NewBufferString 还可以用 bytes.NewBufferString(“hello”) 来建立一个内容是 hello 的缓冲器 ...

2019-03-21 · 1 min · 149 words · -

go mod

go mod create project mkdir project-0 go mod init project-0 go mod edit [editing flags] [go.mod] commands # upgrade go version to 1.21.4 go mod edit -go 1.21.4 export GO111MODULE=on go mod init project0 # 初始化 go mod tidy # 拉取缺少的模块, 移除不用的模块。 go mod download # 手动下载依赖包, 根据 go.mod 下载 go mod graph # 打印模块依赖图 go mod vendor # 将依赖复制到 vendor 下 go mod verify # 校验依赖 go mod why # 打印为什么需要依赖 go list all # 打印所有的 package go list -m all # 打印所有的 module, The -m flag causes list to list modules instead of packages go list -m -json all # 依赖详情, json 格式 go mod edit -go=1.15 # 添加新模块 go get <package>[@<version>] go.mod 如何编辑 在 Go 1.16 中,另一个行为变更是 go build 和 go test 不会自动编辑 go.mod 了,基于以上信息,Go 1.16 中将进行如下处理: ...

2018-10-20 · 2 min · 343 words · -

golang lock, sync.RWMutex, sync.Mutex, 锁

golang lock, sync.RWMutex, sync.Mutex, 锁 在 Go 语言并发编程中,倡导使用通信共享内存,不要使用共享内存通信,而这个通信的媒介就是 Channel, Channel 是线程安全的,不需要考虑数据冲突问题,面对并发问题,我们始终应该优先考虑使用Channel,它是 first class 级别的,但是纵使有主角光环加持,Channel也不是万能的,它也需要配角,这也是共享内存存在的价值,其他语言中主流的并发编程都是通过共享内存实现的,共享内存必然涉及并发过程中的共享数据冲突问题,而为了解决数据冲突问题,Go 语言沿袭了传统的并发编程解决方案 - 锁机制,这些锁都位于 sync 包中。 golang 中 sync 包提供了两种锁 Mutex (互斥锁) 和 RWMutex (读写锁), 其中 RWMutex 是基于 Mutex 实现的, 只读锁的实现使用类似引用计数器(Reference Counting)的功能. Mutex: 互斥锁 RWMutex: 读写锁 锁的作用都是为了解决并发情况下共享数据的原子操作和最终一致性问题 Mutex, 互斥锁 type Mutex struct { // contains filtered or unexported fields } func (m *Mutex) Lock() func (m *Mutex) Unlock() sync.Mutex 用于多个 goroutine 对共享资源的互斥访问。使用要点如下: Lock() 加锁,Unlock() 解锁; 对未解锁的 Mutex 使用 Lock() 会阻塞; 对未上锁的 Mutex 使用 Unlock() 会导致 panic 异常。 加锁之后未解锁, 再次加锁会导致死锁 使用 Lock() 加锁后, 便不能再次对其进行加锁, 直到用 Unlock() 解锁对其解锁后, 才能再次加锁. 适用于读写不确定场景, 即读写次数没有明显的区别, 并且只允许只有一个读或者写的场景, 所以该锁也叫做全局锁. ...

2018-04-13 · 3 min · 453 words · -

golang select

golang select package main import ( "fmt" "time" ) func main() { fmt.Println("selectx") ch0 := make(chan struct{}) ch1 := make(chan struct{}) go func() { for { select { case v0 := <-ch0: fmt.Println("ch0: ", v0) case v1 := <-ch1: fmt.Println("ch1: ", v1) } } }() time.Sleep(1 * time.Second) ch0 <- struct{}{} ch1 <- struct{}{} ch0 <- struct{}{} ch1 <- struct{}{} ch1 <- struct{}{} ch1 <- struct{}{} time.Sleep(3 * time.Second) fmt.Println("selectx end") } // output: // ch0: {} // ch1: {} // ch0: {} // ch1: {} // ch1: {} // ch1: {} 代码执行到 select 时默认会阻塞, 直到任意一个 case 评估通过, 如果有多个 case 符合执行条件就从里面随机选一个执行. ...

2017-11-17 · 2 min · 386 words · -

go channel

go channel channel 是 Go 中的一个核心类型, 可以把它看成一个管道, 通过它并发核心单元就可以发送或者接收数据进行通讯。goroutine 是 Go 语言的基本调度单位, 而 channels 则是它们之间的通信机制。操作符 <- 用来指定管道的方向,发送或接收。如果未指定方向,则为双向管道。golang 的 channel 就是一个 环形队列/ringbuffer 的实现。 我们称 chan 为管理结构,channel 里面可以放任何类型的对象,我们称之为元素。 Channel 定义 ChannelType = ( "chan" | "chan<-" | "<-chan" ) ElementType . 可选的 <- 代表 channel 的方向 (是数据的流向)。如果没有指定方向,那么 Channel 就是双向的,既可以接收数据,也可以发送数据。 <- // channel 的操作符 ch <- v // 发送值 v 到 Channel ch 中 v := <-ch // 从 Channel ch 中接收数据, 并将数据赋值给 v var foo chan T // 可以接收和发送类型为 T 的数据 var foo chan<- float64 // 只可以用来发送 float64 类型的数据 var foo <-chan int // 只可以用来接收 int 类型的数据 // <- 总是优先和最左边的类型结合。(The <- operator associates with the leftmost chan possible) chan<- chan int // 等价 chan<- (chan int) chan<- <-chan int // 等价 chan<- (<-chan int) <-chan <-chan int // 等价 <-chan (<-chan int) chan (<-chan int) // channel 定义 var dataChan <-chan []byte // 无缓冲的 channel // 使用 make 初始化 Channel, 并且可以设置容量, channel 初始化, 初始化之后才能使用 // 未设置容量的 channel, 如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯 // 无缓冲的 channel 由于没有缓冲发送和接收需要同步. // channel 无缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据。 dataChan := make(<-chan []byte) // 容量为100的 channel ch := make(chan int, 100) // 容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。 // 创建一个双向channel, interface{}表示chan可以为任何类型 foo := make(chan interface{}) // 可以通过内建的close方法可以关闭Channel。 close(foo) // channel的 receive支持 multi-valued assignment,如 v, ok := <-ch 往一个已经被 close 的 channe l中继续发送数据会导致 run-time panic。 ...

2017-11-09 · 3 min · 450 words · -

golang 各种数据类型转换

golang 各种数据类型转换 Conversions are expressions of the form T(x) where T is a type and x is an expression that can be converted to type T. string > duration d, e := time.ParseDuration("-1h") d, e := time.ParseDuration("1000ms") array > slice arr := [3]int{1,2,3} sli := arr[:] hex > int, big.Int // int n, err := strconv.ParseUint(val, 16, 32) // big.Int n := new(big.Int) n, _ = n.SetString(hex[2:], 16) float > int //float64 转成转成int64 var x float64 = 5.7 var y int = int64(x) int(math.Floor(f + 0.5)) base64 > hex p, err := base64.StdEncoding.DecodeString("QVJWSU4=") if err != nil { // handle error } h := hex.EncodeToString(p) fmt.Println(h) // prints 415256494e bytes > hex hex.EncodeToString(foo) string, float s := "3.1415926535" v1, err := strconv.ParseFloat(v, 32) v2, err := strconv.ParseFloat(v, 64) // float64 > string valueStr = strconv.FormatFloat(v, 'f', 3, 64) int > float i:=5 f:=float32(i) bytes > int var ba = []byte{ 56, 66, 73, 77 } var value int32 value |= int32(ba[0]) value |= int32(ba[1]) << 8 value |= int32(ba[2]) << 16 value |= int32(ba[3]) << 24 reverse the indexing order to switch between big and little endian. int8 > byte 因为两者间的类型及取值范围这些都不相同,不能直接进行转换。int8取值范围为: -128~127,如果要转化的话需要使用bytevalue=256+int8value ...

2017-10-31 · 5 min · 894 words · -

go regex, 正则

go regex, 正则 nodes := []string{"foo.bar-000.x", "foo.t0.bar","foo.bar"} reg := regexp.MustCompile(`foo\.(.[^\.]*)\.{0,1}.*`) for _, v := range nodes { result := reg.FindSubmatch([]byte(v)) fmt.Println("output: ", string(result[1])) } [^a-zA-Z ]+, 匹配所有不是小写大写字母和空格的字符 . 匹配任意一个字符, 如果设置 s = true, 则可以匹配换行符 [字符类] 匹配"字符类"中的一个字符,"字符类"见后面的说明 [^字符类] 匹配"字符类"外的一个字符,"字符类"见后面的说明 \小写Perl标记 匹配"Perl类"中的一个字符,"Perl类"见后面的说明 \大写Perl标记 匹配"Perl类"外的一个字符,"Perl类"见后面的说明 [:ASCII类名:] 匹配"ASCII类"中的一个字符,"ASCII类"见后面的说明 [:^ASCII类名:] 匹配"ASCII类"外的一个字符,"ASCII类"见后面的说明 \pUnicode普通类名 匹配"Unicode类"中的一个字符(仅普通类),"Unicode类"见后面的说明 \PUnicode普通类名 匹配"Unicode类"外的一个字符(仅普通类),"Unicode类"见后面的说明 \p{Unicode类名} 匹配"Unicode类"中的一个字符,"Unicode类"见后面的说明 \P{Unicode类名} 匹配"Unicode类"外的一个字符,"Unicode类"见后面的说明 复合: xy 匹配 xy (x 后面跟随 y) x|y 匹配 x 或 y (优先匹配 x) 重复: x* 匹配零个或多个 x,优先匹配更多(贪婪) x+ 匹配一个或多个 x,优先匹配更多(贪婪) x? 匹配零个或一个 x,优先匹配一个(贪婪) x{n,m} 匹配 n 到 m 个 x,优先匹配更多(贪婪) x{n,} 匹配 n 个或多个 x,优先匹配更多(贪婪) x{n} 只匹配 n 个 x x*? 匹配零个或多个 x,优先匹配更少(非贪婪) x+? 匹配一个或多个 x,优先匹配更少(非贪婪) x?? 匹配零个或一个 x,优先匹配零个(非贪婪) x{n,m}? 匹配 n 到 m 个 x,优先匹配更少(非贪婪) x{n,}? 匹配 n 个或多个 x,优先匹配更少(非贪婪) x{n}? 只匹配 n 个 x 分组: ...

2017-10-12 · 6 min · 1122 words · -