秀才


  • Home

  • About

  • Tags

  • Archives

  • Search

导入导出

Posted on 2018-09-25 | Visitors:

拿阿里云MongoDB举例

Keys:mongoexport,mongoimport

  1. 导出:
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    mongoexport -h dds-********.mongodb.rds.aliyuncs.com:3717 --authenticationDatabase admin -u root -p **** -d ranger -c players -o players.json -q '{id: {"$in": [16398, 10001]}}'

    mongoexport -h dds-********.mongodb.rds.aliyuncs.com:3717 --authenticationDatabase admin -u root -p **** -d pigcome -c dollOrders -o orders.csv -q '{orderId: {"$gte": 1}}' --type csv -f uid,orderId,dollId,name,weChat,phone,address,mark,status

    mongoexport -h 10.10.100.100:27017 --authenticationDatabase admin -u mongouser -p ******** -d **** -c robots -o robot.json

    参数说明:
    -h MongoDB服务器地址
    --authenticationDatabase 验证角色数据库
    -u 登录帐号
    -p 登录密码
    -d 连接数据库
    -c 默认集合
    -o 导出文件名
    -q 查询语句
    -type 导出类型
    -f 导出字段
  2. 导入:
    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mongoimport -h dds-m5e7bc768a732d041.mongodb.rds.aliyuncs.com:3717 --authenticationDatabase admin -u root -p ******** -d ranger -c players players.json

    参数说明:
    -h MongoDB服务器地址
    --authenticationDatabase 验证角色数据库
    -u 登录帐号
    -p 登录密码
    -d 连接数据库
    -c 默认集合

论成熟的男银

Posted on 2018-09-25 | Visitors:

摘自知乎:吴遇安

能够解决问题的男人就是成熟的男人

这个题目看似很容易写,因为每个人都可以有自己对于“成熟”的理解,但答主更崇尚用较为理性的方式来分析这个命题,所以下面,我会尽可能用一种严谨的方式来解构这个命题。

我们列出了某个界定标准,符合这个标准的,便是“成熟男人”,而这个问题便是在探讨这个标准是什么,其实这个问题下的许多回答中提出的标准,反例都太好举了,一个挖鼻孔的大色狼坂田银时,恐怕难以符合这里的大多数标准,但是难道有人认为他“不成熟”吗?

再举一个著名的范例
君子
君子XXX
君子应该XXX
君子不应该XXX
XXX样的人才能算君子

类似的叙述,占据了我国古代儒家著述的很大一部分,很熟悉吧?古人讨论君子的时候,其实和我们现在讨论成熟男人的情形是一样的,大家都在纷纷说,梨子是水果,苹果是水果,却没能很好的给水果做一个足够精确的描述。
本楼中的回答,男人们的大部分都在描述一个想象中的完美人格,甚至是想象中的自己(如同古人想象“君子”)
而女孩子们的回答,则大多都在描述自己理想中的爱人形象。

须知,这个家伙出现在现实生活中的时候,许多女孩子也会觉得他是成熟男人呢。

用这样的枚举法得到的结论,我认为并不具有很强的说服力。因此,我在这份回答里,就是希望探讨一条具有普适效用的标准,以此来界定成熟男人的边界。

我们在生活中普遍认同的成熟男人形象,包含了许多的要素,冷静,善良,宽容,等等等等,那么,到底其中的哪些,才是造成“成熟男人”与“不成熟男人”的根本分界点呢,成熟男人的定义,对于实践有什么指导意义?

我以前刷知乎的时候看过一个问题,问“为什么鸡汤文受人讨厌”,下面排名第一的回答叫做“因为没给勺子”。这其实就是本楼中许多答案的问题,只给了一个目标,“做到XX样就是成熟男人”,但是,为什么这样就是成熟男人,怎样才能成为成熟男人,对于这两个关键的问题,却没有进行解答,就是典型的“送汤不送勺”。而本文,则致力于解析“成熟”的本质,并且给出到达彼岸的路径。

我得到的答案是:

成熟与不成熟的根本分界点,在于“对于规律的理解和运用能力”

在对于成熟进行解析之前,我们首先要解决的,是这个问题。
成熟的目的是什么?

不要忽视这一点,成熟往往出现在两种语境之中,一曰”XX性格成熟“,二曰”XX处理某项事务的手法成熟‘,为什么我们追求成熟?因为在我们的想象和实践中,越”成熟(包括性格和处理手法)“,往往代表了对问题更强的解决能力

不论这个问题是追求更高的社会地位,更多的财富,更好的处理与他人的关系,更愉悦的生活和心态,或者是与这个世界更和谐地相处。以题目的题主为例,他感慨自己不成熟,其实是在感慨“处理两性关系的能力不强”!

我们说,在某个领域内,张三比李四更成熟,指的是在该领域内,张三比李四有更强的解决问题的能力,而我们说张三比李四的性格更成熟,指的是张三在多个领域内均具有比李四更强的解决问题的能力。

在明了成熟的目的之后,我们接下来研究的,就是,在成熟包含的许多特质之中,有哪些对于达成这个目的具有最关键的影响,那么,这个(或者这些)特质就可以被认定为是决定某人“成熟度”的核心,是区分成熟与幼稚的根本分界点。

我们之前探讨的“成熟”,大多是出自对于一个完美人格的想象,这个完美的人格中包涵了许多要素,沉稳,冷静,善良,宽厚,仁爱,温柔,坚强,慷慨,自立,目标明确胸有大志等等等等。那么,如何确定哪一项特质才是区分成熟与幼稚的根本分界点呢?下面,我来介绍一种我本人分析问题时的常用方法,即“去除噪声法”,分为四步

第一步,收集,
收集所有我们对于“成熟”特质的想象,加以总结归纳。
第二步,筛选,这许多的特质中,有哪些是“成熟”特有的,即“幼稚”的人几乎不可能具备的。
第三步,对比,“成熟”的人特有的素质中,共性在于哪里。
第四步,分析,“成熟”素质的共性,应当如何锻炼?

第一步,收集
稳重,善良,冷静,宽厚,目标明确,坚强,勇敢,自立,慷慨,礼貌,低调,智慧,果断,有责任感。
统计了这个楼里的三百多个答案,归纳出的特质大致如此,这些素质,如果一个男人具备了,毫无疑问他是成熟的,一个女人要想找到这样的男人,做梦也要笑醒。

第二步,筛选。
在诸多特质中,有一些是“幼稚”的男人也能够拥有的,有一些是并非是“成熟男人”必备的,(例如坂田银时不礼貌,古美门律师。。。算了我不说了。。半泽直树君爱记仇,但是我们也普遍认为这几位都是成熟男人),这些特质,我将在下面列出并加以剔除。
善良,幼稚男也可以有,pass 坚强,pass 礼貌,pass 有责任感,pass 勇敢,pass 宽厚,pass慷慨,pass
那么剩下的有 稳重 冷静 目标明确 自立 低调 智慧 果断
稳重,代表考虑问题全面的能力
冷静,代表处变不惊的能力
目标明确,低调和自立三种特质可以合并,为图方便,下面均以“自立”代替三者,代表不以物喜,不以己悲,内心抗击外界干扰的能力,清楚自己的需求,并且坚定不移地加以追求智慧,稳重代表考虑问题全面,而智慧则又多了一种素质,能够抓住问题的关键点,果断,代表面临选择时的决断能力。

第三步,找出共性
第二步中提到,这五项素质,是成熟男人必备,也是成熟男人特有的能力,在这个步骤之中,我将解析,这五种素质的核心共性是什么。
换句话来说,你做到了某件事情,就能够自然而然的拥有这五种特质,我的任务就是解析出“这件事情”是什么。

如果把团队中的工作分为”智“与”力“(可理解为思考和执行),那么我的属性就是”智“。
我曾经不止一次的思考过,“智”与“力”的极限在哪里,“力”的极限,可描述为“专拆南墙”,就像成语故事里无坚不摧的矛,没有捅不穿的防御。
而“智”的极限,我认为,是“预知”。
对于智者而言,学习和实践都是为了找寻事物发展变化的规律,而找寻规律,则是为了推测日后事物的形态。
因此,“智”的极限,即是“预知”。

稳重,冷静,目标明确,智慧,果断。
这五种素质都有一个共性,即它们的主人,已经“知道”了。
曾经经历过,知道接下来会发生什么,并且对可能出现出现的几种结果都做好了准备。
因为他已经“知道”了,所以,自然能够全面的考虑问题
因为已经经历过了,所以不会惊慌失措,从而能够处变不惊
因为“知道了”,所以能够准确的找出问题的核心
因为已经对于各种结果都做好了应对的准备,或者说能够接受失败,所以成熟男人才能果断地下决定
成熟男人的目标很明确,不会被外物所扰,因为他已经“经历过”,并且“知道了”。
所以,这五种素质的核心,即在于“经历”,和“知道”。

从实践和理论中领悟出规律,并且能够在实践中运用规律,从而获得强大的解决问题的能力,即为成熟。

成熟区别于幼稚的的核心,即,对于“规律”的理解和运用。

最后,是第四步,应当如何锻炼。
多实践,多读书,多思考。
不是烂尾,而是我实在找不到比这九个字更好的词来描述了。
主动地去经历事物,积极地从书中和自己的实践中总结规律,思考如何应用规律,就是达到“成熟”彼岸的通道。

成熟”从本质上来说,是一种技能而非性格,性格的养成来源于技能的提升。我们从小到大被说了那么多年的不成熟,究竟传说中的成熟是什么东西?

成熟绝不只是停留于高僧,老头子,中年大叔身上的专利,我们更没有必要将之神化,“鸡汤化”,
成熟不是性格,只是一种技能,古美门律师在处理案件的时候手法非常成熟,对自己作为律师的“道”达到了坚信不疑的程度,不可不谓之境界高深,但是在处理人际关系的时候态度却不成熟,暴躁,没礼貌,不稳重。银他妈经历了丰富的人生,面对危局处事不惊,战场上白夜叉几度血战来回,却连区区甜食的诱惑也抵挡不了。
他们都成熟,他们也都不成熟。

维特根斯坦说过,说不出来的东西就当它不存在,我认为包括成熟男人在内,好多我们日常的概念都被过分神化了,明明是可以解析的事情,搞得神神叨叨,令人甚为不爽
对我们这些不完美的人来说,要求一个人“成熟”起来,并非要求他“能够解决一切事情”,而是要求他“在总体解决问题的能力上达到他这个年纪的人的均值”。
何必急于一时?何必用完美的标准去要求所有人?

对于我们少年人来说,这几个家伙何尝不是成熟?

我是要成为海贼王的男人,就算为此而死,也无所谓
                       ———Monkey D Luffy

因为我是天才
                       ———樱木花道
为什么你这么不成熟?
因为我是天才啊哈哈哈

reflect

Posted on 2018-09-25 | Visitors:

Table Of Content

  • Read struct tags
  • Get and set struct fields
  • Fill slice with values
  • Set a value of a number
  • Decode key-value pairs into map
  • Decode key-value pairs into struct
  • Encode struct into key-value pairs
  • Check if the underlying type implements an interface
  • Function calls
    • Call to a method without prameters, and without return value
    • Call to a function with list of arguments, and validate return values
    • Call to a function dynamically. similar to the template/text package
    • Call to a function with variadic parameter
    • Create function at runtime

Read struct tags

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
"reflect"
)

type User struct {
Email string `mcl:"email"`
Name string `mcl:"name"`
Age int `mcl:"age"`
Github string `mcl:"github" default:"a8m"`
}

func main() {
var u interface{} = User{}
// TypeOf returns the reflection Type that represents the dynamic type of u.
t := reflect.TypeOf(u)
// Kind returns the specific kind of this type.
if t.Kind() != reflect.Struct {
return
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Println(f.Tag.Get("mcl"), f.Tag.Get("default"))
}
}

Calling to Kind() can returns one of this list.

Get and set struct fields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"reflect"
)

type User struct {
Email string `mcl:"email"`
Name string `mcl:"name"`
Age int `mcl:"age"`
Github string `mcl:"github" default:"a8m"`
}

func main() {
u := &User{Name: "Ariel Mashraki"}
// Elem returns the value that the pointer u points to.
v := reflect.ValueOf(u).Elem()
f := v.FieldByName("Github")
// make sure that this field is defined, and can be changed.
if !f.IsValid() || !f.CanSet() {
return
}
if f.Kind() != reflect.String || f.String() != "" {
return
}
f.SetString("a8m")
fmt.Printf("Github username was changed to: %q\n", u.Github)
}

Fill slice with strings, without knowing its type. Use case: decoder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
"fmt"
"io"
"reflect"
)

func main() {
var (
a []string
b []interface{}
c []io.Writer
)
fmt.Println(fill(&a), a) // pass
fmt.Println(fill(&b), b) // pass
fmt.Println(fill(&c), c) // fail
}

func fill(i interface{}) error {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("non-pointer %v", v.Type())
}
// get the value that the pointer v points to.
v = v.Elem()
if v.Kind() != reflect.Slice {
return fmt.Errorf("can't fill non-slice value")
}
v.Set(reflect.MakeSlice(v.Type(), 3, 3))
// validate the type of the slice. see below.
if !canAssign(v.Index(0)) {
return fmt.Errorf("can't assign string to slice elements")
}
for i, w := range []string{"foo", "bar", "baz"} {
v.Index(i).Set(reflect.ValueOf(w))
}
return nil
}

// we accept strings, or empty interfaces.
func canAssign(v reflect.Value) bool {
return v.Kind() == reflect.String || (v.Kind() == reflect.Interface && v.NumMethod() == 0)
}

Set a value of a number. Use case: decoder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main

import (
"fmt"
"reflect"
)

const n = 255

func main() {
var (
a int8
b int16
c uint
d float32
e string
)
fmt.Println(fill(&a), a)
fmt.Println(fill(&b), b)
fmt.Println(fill(&c), c)
fmt.Println(fill(&d), c)
fmt.Println(fill(&e), e)
}

func fill(i interface{}) error {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("non-pointer %v", v.Type())
}
// get the value that the pointer v points to.
v = v.Elem()
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.OverflowInt(n) {
return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
}
v.SetInt(n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if v.OverflowUint(n) {
return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
}
v.SetUint(n)
case reflect.Float32, reflect.Float64:
if v.OverflowFloat(n) {
return fmt.Errorf("can't assign value due to %s-overflow", v.Kind())
}
v.SetFloat(n)
default:
return fmt.Errorf("can't assign value to a non-number type")
}
return nil
}

Decode key-value pairs into map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
"fmt"
"reflect"
"strconv"
"strings"
)

func main() {
var (
m0 = make(map[int]string)
m1 = make(map[int64]string)
m2 map[interface{}]string
m3 map[interface{}]interface{}
m4 map[bool]string
)
s := "1=foo,2=bar,3=baz"
fmt.Println(decodeMap(s, &m0), m0) // pass
fmt.Println(decodeMap(s, &m1), m1) // pass
fmt.Println(decodeMap(s, &m2), m2) // pass
fmt.Println(decodeMap(s, &m3), m3) // pass
fmt.Println(decodeMap(s, &m4), m4) // fail
}

func decodeMap(s string, i interface{}) error {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("non-pointer %v", v.Type())
}
// get the value that the pointer v points to.
v = v.Elem()
t := v.Type()
// allocate a new map, if v is nil. see: m2, m3, m4.
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
// assume that the input is valid.
for _, kv := range strings.Split(s, ",") {
s := strings.Split(kv, "=")
n, err := strconv.Atoi(s[0])
if err != nil {
return fmt.Errorf("failed to parse number: %v", err)
}
k, e := reflect.ValueOf(n), reflect.ValueOf(s[1])
// get the type of the key.
kt := t.Key()
if !k.Type().ConvertibleTo(kt) {
return fmt.Errorf("can't convert key to type %v", kt.Kind())
}
k = k.Convert(kt)
// get the element type.
et := t.Elem()
if et.Kind() != v.Kind() && !e.Type().ConvertibleTo(et) {
return fmt.Errorf("can't assign value to type %v", kt.Kind())
}
v.SetMapIndex(k, e.Convert(et))
}
return nil
}

Decode key-value pairs into struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
"fmt"
"reflect"
"strings"
)

type User struct {
Name string
Github string
private string
}

func main() {
var (
v0 User
v1 *User
v2 = new(User)
v3 struct{ Name string }
s = "Name=Ariel,Github=a8m"
)
fmt.Println(decode(s, &v0), v0) // pass
fmt.Println(decode(s, v1), v1) // fail
fmt.Println(decode(s, v2), v2) // pass
fmt.Println(decode(s, v3), v3) // fail
fmt.Println(decode(s, &v3), v3) // pass
}

func decode(s string, i interface{}) error {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr || v.IsNil() {
return fmt.Errorf("decode requires non-nil pointer")
}
// get the value that the pointer v points to.
v = v.Elem()
// assume that the input is valid.
for _, kv := range strings.Split(s, ",") {
s := strings.Split(kv, "=")
f := v.FieldByName(s[0])
// make sure that this field is defined, and can be changed.
if !f.IsValid() || !f.CanSet() {
continue
}
// assume all the fields are type string.
f.SetString(s[1])
}
return nil
}

Encode struct into key-value pairs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package main

import (
"fmt"
"reflect"
"strings"
)

type User struct {
Email string `kv:"email,omitempty"`
Name string `kv:"name,omitempty"`
Github string `kv:"github,omitempty"`
private string
}

func main() {
var (
u = User{Name: "Ariel", Github: "a8m"}
v = struct {
A, B, C string
}{
"foo",
"bar",
"baz",
}
w = &User{}
)
fmt.Println(encode(u))
fmt.Println(encode(v))
fmt.Println(encode(w))
}

// this example supports only structs, and assume their
// fields are type string.
func encode(i interface{}) (string, error) {
v := reflect.ValueOf(i)
t := v.Type()
if t.Kind() != reflect.Struct {
return "", fmt.Errorf("type %s is not supported", t.Kind())
}
var s []string
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
// skip unexported fields. from godoc:
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
if f.PkgPath != "" {
continue
}
fv := v.Field(i)
key, omit := readTag(f)
// skip empty values when "omitempty" set.
if omit && fv.String() == "" {
continue
}
s = append(s, fmt.Sprintf("%s=%s", key, fv.String()))
}
return strings.Join(s, ","), nil
}

func readTag(f reflect.StructField) (string, bool) {
val, ok := f.Tag.Lookup("kv")
if !ok {
return f.Name, false
}
opts := strings.Split(val, ",")
omit := false
if len(opts) == 1 {
omit = opts[1] == "omitempty"
}
return opts[0], omit
}

Check if the underlying type implements an interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"reflect"
)

type Marshaler interface {
MarshalKV() (string, error)
}

type User struct {
Email string `kv:"email,omitempty"`
Name string `kv:"name,omitempty"`
Github string `kv:"github,omitempty"`
private string
}

func (u User) MarshalKV() (string, error) {
return fmt.Sprintf("name=%s,email=%s,github=%s", u.Name, u.Email, u.Github), nil
}

func main() {
fmt.Println(encode(User{"boring", "Ariel", "a8m", ""}))
fmt.Println(encode(&User{Github: "posener", Name: "Eyal", Email: "boring"}))
}

var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()

func encode(i interface{}) (string, error) {
t := reflect.TypeOf(i)
if !t.Implements(marshalerType) {
return "", fmt.Errorf("encode only supports structs that implement the Marshaler interface")
}
m, _ := reflect.ValueOf(i).Interface().(Marshaler)
return m.MarshalKV()
}

Function calls

Call method without prameters, and without return value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"reflect"
)

type A struct{}

func (A) Hello() { fmt.Println("World") }

func main() {
// ValueOf returns a new Value, which is the reflection interface to a Go value.
v := reflect.ValueOf(A{})
m := v.MethodByName("Hello")
if m.Kind() != reflect.Func {
return
}
m.Call(nil)
}

Call function with list of arguments, and validate return values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"reflect"
)

func Add(a, b int) int { return a + b }

func main() {
v := reflect.ValueOf(Add)
if v.Kind() != reflect.Func {
return
}
t := v.Type()
argv := make([]reflect.Value, t.NumIn())
for i := range argv {
// validate the type of parameter "i".
if t.In(i).Kind() != reflect.Int {
return
}
argv[i] = reflect.ValueOf(i)
}
// note that, len(result) == t.NumOut()
result := v.Call(argv)
if len(result) != 1 || result[0].Kind() != reflect.Int {
return
}
fmt.Println(result[0].Int())
}

Call to a function dynamically. similar to the template/text package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package main

import (
"fmt"
"html/template"
"reflect"
"strconv"
"strings"
)

func main() {
funcs := template.FuncMap{
"trim": strings.Trim,
"lower": strings.ToLower,
"repeat": strings.Repeat,
"replace": strings.Replace,
}
fmt.Println(eval(`{{ "hello" 4 | repeat }}`, funcs))
fmt.Println(eval(`{{ "Hello-World" | lower }}`, funcs))
fmt.Println(eval(`{{ "foobarfoo" "foo" "bar" -1 | replace }}`, funcs))
fmt.Println(eval(`{{ "-^-Hello-^-" "-^" | trim }}`, funcs))
}

// evaluate an expression. note that this implemetation is assuming that the
// input is valid, and also very limited. for example, whitespaces are not allowed
// inside a quoted string.
func eval(s string, funcs template.FuncMap) (string, error) {
args, name := parseArgs(s)
fn, ok := funcs[name]
if !ok {
return "", fmt.Errorf("function %s is not defined", name)
}
v := reflect.ValueOf(fn)
t := v.Type()
if len(args) != t.NumIn() {
return "", fmt.Errorf("invalid number of arguments. got: %v, want: %v", len(args), t.NumIn())
}
argv := make([]reflect.Value, len(args))
// go over the arguments, validate and build them.
// note that we support only int, and string in this simple example.
for i := range argv {
var argType reflect.Kind
// if the argument "i" is string.
if strings.HasPrefix(args[i], "\"") {
argType = reflect.String
argv[i] = reflect.ValueOf(strings.Trim(args[i], "\""))
} else {
argType = reflect.Int
// assume that the input is valid.
num, _ := strconv.Atoi(args[i])
argv[i] = reflect.ValueOf(num)
}
if t.In(i).Kind() != argType {
return "", fmt.Errorf("Invalid argument. got: %v, want: %v", argType, t.In(i).Kind())
}
}
result := v.Call(argv)
// in real-world code, we validate it before executing the function,
// using the v.NumOut() method. similiar to the text/template package.
if len(result) != 1 || result[0].Kind() != reflect.String {
return "", fmt.Errorf("function %s must return a one string value", name)
}
return result[0].String(), nil
}

// parseArgs is an auxiliary function, that extract the function and its
// parameter from the given expression.
func parseArgs(s string) ([]string, string) {
args := strings.Split(strings.Trim(s, "{ }"), "|")
return strings.Split(strings.Trim(args[0], " "), " "), strings.Trim(args[len(args)-1], " ")
}

Call function with variadic parameter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"math/rand"
"reflect"
)

func Sum(x1, x2 int, xs ...int) int {
sum := x1 + x2
for _, xi := range xs {
sum += xi
}
return sum
}

func main() {
v := reflect.ValueOf(Sum)
if v.Kind() != reflect.Func {
return
}
t := v.Type()
argc := t.NumIn()
if t.IsVariadic() {
argc += rand.Intn(10)
}
argv := make([]reflect.Value, argc)
for i := range argv {
argv[i] = reflect.ValueOf(i)
}
result := v.Call(argv)
fmt.Println(result[0].Int()) // assume that t.NumOut() > 0 tested above.
}

Create function at runtime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"reflect"
)

type Add func(int64, int64) int64

func main() {
t := reflect.TypeOf(Add(nil))
mul := reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
a := args[0].Int()
b := args[1].Int()
return []reflect.Value{reflect.ValueOf(a+b)}
})
fn, ok := mul.Interface().(Add)
if !ok {
return
}
fmt.Println(fn(2,3))
}

net-smtp

Posted on 2018-09-25 | Visitors:

邮件的一般格式

1
2
3
4
5
6
From:chimps@example.com
To:to@163.com
Subject:TEST EMAIL
Content-Type: text/html; chatset=UTF-8

<h1>hello go</h1>

使用阿里云企业邮注意事项

如果你的域名为example.com
在阿里的域名那里都配好之后,发邮件的配置应该如下:

1
2
3
4
HOST: smtp.example.com
HOST_ADDR: smtp.example.com:25
USER: 该企业邮箱所创建的子邮箱
PASSWORD:user的password

代码:

主要是使用net/smtp包的SendMail方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"fmt"
"net/smtp"
"strings"
)

const (
HOST = "smtp.example.com"
ADDR = "smtp.example.com:25"
USER = "chimps@example.com"
PASSWORD = "xxx"
)

func main() {
to := "to1@163.com;to1@163.com"
subject := "TEST EMAIL"
email_type := "html"
message := "<h1>hello go</h1>"
if err := SendEmail(to, subject, email_type, message); err != nil {
fmt.Println("发送失败, reason:" + err.Error())
}
}

func SendEmail(to string, subject string, email_type string, message string) (err error) {
var content_type string
switch email_type {
case "html":
content_type = "Content-Type: text/html; chatset=UTF-8"
case "text":
content_type = "Content-Type: text/plain; chatset=UTF-8"
}
auth := smtp.PlainAuth("", USER, PASSWORD, HOST)
send_to := strings.Split(to, ";")
for _, v := range send_to {
var msg []string
msg = append(msg, "From:"+USER, "To:"+v, "Subject:"+subject, content_type, "\r\n"+message)
msg_string := strings.Join(msg, "\r\n")
fmt.Println(msg_string)

if err = smtp.SendMail(ADDR, auth, USER, []string{v}, []byte(msg_string)); err != nil {
fmt.Println("send fail , reason: " + err.Error())
return
} else {
fmt.Println("send ok :" + v)
}
}
return
}

改进

一般情况下,发邮件时间比较耗时的事情,一般走异步处理

数组与切片

Posted on 2018-09-25 | Visitors:

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 索引数组
a := [10]int{2:20}
// 不指定数组长度
a := [...]int{1,2,3,4}
// 多维数组,只有第一维可以使用...
a := [...][3]int{
{1,2,3},
{2,3,4}
}
a := [2][3]int{
{1,2,3},
{2,3,4}
}
//指针数组
x,y := 1,2
a := [...]*int{&x, &y}
//指向数组的指针
a :=[...]int{99:1}
var p *[100]int = &a
// 数组之间可以使用 == 或 != 比较,只有长度和类型相同才算相同的数组,才能比较
a :=[2]int{1,2}
b :=[2]int{3,2}
// 使用new关键字,返回指向数组的指针
a :=[2]int{}
a[1] = 2
b = new([2]int)
b[1] = 2
// 不论是指向数组的指针还是数组本身,都可以使用a[1]的形式给数组赋值
// go语言的数组是值类型,不是引用类型
//冒泡排序
func main() {
list := [...]int{12, 323, 4, 565, 67, 3}
fmt.Println(list)
var lenth = len(list)
for i := 0; i < lenth; i++ {
for j := i + 1; j < lenth; j++ {
if list[i] > list[j] {
tmp := list[i]
list[i] = list[j]
list[j] = tmp
}
}
}
fmt.Println(list)
}
// 结果:
➜ first go run helloworld.go
[12 323 4 565 67 3]
[3 4 12 67 323 565]

切片

切片与数组的定义区别

1
2
3
4
5
6
7
8
vs:=[]interface{}{
[]int{},// slice 切片
[]int{1,2,3},// slice 切片
[]int{1,2,3}[:],//切片再切还是切片
make([]int,3,10),//标准的slice 定义方式
[3]int{1,2,3},//array 数组,确定数组长度
[...]int{1,2,3},//array 数组,由编译器自动计算数组长度。
}

Reslice

  • 切片本质上是指向类型
  • 切片不能越界,如果超过底层数组,则报错,而不是追加数组长度
  • 索引不可超多数组容量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    a := [10]int{1,2,3,4,5,6,7,8,9}
    fpm.Prinln(a)
    // 0~4 容量为10
    s1 := a[:5]
    // 5-9, 索引5到索引9,索引10不取, 容量为5
    s1 := a[5,10]
    // 使用make,
    // 参数1:类型,
    // 参数2:包含几个元素,
    // 参数3:容量(先分配10个连续内存地址,如果增加到11个时,则需要从新分配,每次增加一倍,扩容至20,下次40,在下次80,造成这种现象的原因是因为:从新分配内存是一件效率非常低的)如果留空,则和参数相等
    s1 := make([]int,3,10)
    // 获取切片的长度,
    len(s1)
    // 获取切片容量
    map(s1)

Append

  • 可以在slice尾部追加元素
  • 可以将一个slice追加到另一个slice
  • 如果最终长度未超过追加到slice的容量,则返回原始slice
  • 如果超过追加的slice的容量,将重新分配数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    s1 := make([]int, 3, 6)
    fmt.Printf("%p\n", s1)
    s1 = append(s1, 1, 2, 3)
    fmt.Printf("%v %p\n", s1, s1)
    s1 = append(s1, 1, 2, 3)
    fmt.Printf("%v %p\n", s1, s1)
    // 对于指向同一个数组的slice,如果数组改变了,则两个slice指向的部分同样改变
    a := []int{1, 2, 3, 4, 5}
    s1 := a[2:5]
    s2 := a[1:3]
    fmt.Println(s1, s2)
    s1[0] = 9
    fmt.Println(s1, s2)
    // 结果:
    ➜ first go run helloworld.go
    [3 4 5] [2 3]
    [9 4 5] [2 9]
  • 如果一个slice通过append添加时,如果超过了容量,那么就不在指向原来的数组,而是从新分配了一个
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    func main() {
    a := []int{1, 2, 3, 4, 5}
    s1 := a[2:5]
    s2 := a[1:3]
    fmt.Println(s1, s2)
    s2 = append(s2, 1, 2, 3, 4, 5, 5, 6, 6, 7)
    s1[0] = 9
    fmt.Println(s1, s2)
    }
    // 结果:
    ➜ first go run helloworld.go
    [3 4 5] [2 3]
    [9 4 5] [2 3 1 2 3 4 5 5 6 6 7]

copy

  • 从参数2复制到参数1(覆盖)
  • 该函数主要是切片(slice)的拷贝,不支持数组
  • 将第二个slice里的元素拷贝到第一个slice里,拷贝的长度为两个slice中长度较小的长度值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    s1 := []int{1, 2, 3, 4, 5, 6}
    s2 := []int{7, 8, 9}
    copy(s2, s1)
    copy(s1, s2)
    fmt.Println(s2, s1)
    // 结果
    ➜ first go run helloworld.go
    [1 2 3] [1 2 3 4 5 6]
    // 当然可以指定位置
    s1 := []int{1, 2, 3, 4, 5, 6}
    s2 := []int{7, 8, 9}
    copy(s1[3:6], s2)
    fmt.Println(s1)
    // 结果:
    ➜ first go run helloworld.go
    [1 2 3 7 8 9]

删除切片

1
2
// 比如想删除下标为k的切片
arr = append(arr[:k], arr[k+1:]...)

map

  • map是无序的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 初始化map
    m := map[int]string{1: "zhangsan", 2: "lisi", 3: "wangwu", 4: "zhaoliu"}
    // 初始化slice
    s := make([]int, len(m))
    i := 0
    // range 可遍历slice或map
    for k, _ := range m {
    // 将map的键赋给slice
    s[i] = k
    i++
    }
    fmt.Println(s)
    //结果(体现了map的无序性)
    ➜ first go run helloworld.go
    [1 2 3 4]
    ➜ first go run helloworld.go
    [4 1 2 3]

    // 如果使用range遍历slice和map,
    func main() {
    m := map[int]string{1: "zhangsan", 2: "lisi", 3: "wangwu", 4: "zhaoliu"}
    s := make([]int, len(m))
    i := 0
    for k, v := range m {
    s[i] = k
    i++
    fmt.Println(k, v)
    }
    fmt.Println(s)
    for k, v := range s {
    fmt.Println(k, v)
    }
    }
    // 结果如下:
    ➜ first go run helloworld.go
    4 zhaoliu
    1 zhangsan
    2 lisi
    3 wangwu
    [4 1 2 3]
    0 4
    1 1
    2 2
    3 3

并发编程(1)

Posted on 2018-09-25 | Visitors:

Goroutine

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
type c struct {
word string
wordId int
err error
place int
}

func main() {
cpunum := runtime.NumCPU()
runtime.GOMAXPROCS(cpunum)
ch := make(chan c, 100)
t1 := time.Now().Unix()
for i := 0; i < 300; i++ {
go test(i, ch)
}
for i := 0; i < 300; i++ {
select {
case res := <-ch:
fmt.Println(res)
}
}
t2 := time.Now().Unix()
log.Println(t2, t1)
}

func test(i int, ch chan c) {
time.Sleep(time.Second)
res := c{"test", i, nil, 0}
ch <- res
return
}

Channel

操作符<-

如果通过管道发送一个值,则将<-作为二元操作符使用。通过管道接收一个值,则将其作为一元操作符使用:

1
2
ic <- 3        // 往管道发送3
work := <-wc // 从管道接收一个指向Work类型值的指针

channel特点

  • Channel是Goroutine沟通的桥梁,大部分是阻塞同步的
  • 通过make创建,close关闭
  • channel是引用类型
  • 可以使用for range来迭代不断的操作
  • 可以设置单向或双向通道
  • 可以设置缓存大小,在未被填满之前不会发生阻塞

通道阻塞

对于无缓冲的channel,通道的发送/接收操作在对方准备好之前是阻塞

  • 对于同一个通道,发送操作(协程或者函数中的),在接收者准备好之前是阻塞的:如果ch中的数据无人接收,就无法再给通道传入其他数据:新的输入无法在通道非空的情况下传入。所以发送操作会等待 ch 再次变为可用状态:就是通道值被接收时(可以传入变量)。
  • 对于同一个通道,接收操作是阻塞的(协程或函数中的),直到发送者可用:如果通道中没有数据,接收者就阻塞了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main

    import "fmt"

    func main() {
    ch1 := make(chan int)
    go pump(ch1) // pump hangs
    // 只是接受结果一次,也就是说,下面的send方再插入一个1,2就插入不进去了,阻塞了
    fmt.Println(<-ch1) // prints only 0
    }

    func pump(ch chan int) {
    for i := 0; ; i++ {
    ch <- i
    }
    }
    // 结果:0

常见错误示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
)

func f1(in chan int) {
fmt.Println(<-in)
}

func main() {
out := make(chan int)
out <- 2
go f1(out)
}

上述例子会照成死锁,原因很简单,一般通讯的话,有两种情况

  • 没有缓冲时,需要最少两个goroutine准备好
  • 有缓冲时,则是goroutine - 中间缓冲层 - goroutine之间的连接。

demo实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
// 使用make创建一个channel
channel := make(chan bool)
go func() {
fmt.Println("gogogo")
// 向channel存数据
channel <- true
// 关闭channel
close(channel)
}()
// 迭代channel
for v := range channel {
fmt.Println(v)
}
fmt.Println("this is base")
}
// 结果
➜ first go run helloworld.go
gogogo
true
this is base

由结果可以看出:先迭代完成才会进行下一步操作, 也就是说,迭代的过程是阻塞的,如果channel中没有值,迭代就会一直等待,一直等到它有值,
如果channel没有关闭,则会发生死锁(deadlock)

带缓冲的channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
ch := make(chan string, 4)
ch <- "zhangsan1"
ch <- "zhangsan2"
ch <- "zhangsan3"
ch <- "zhangsan4"
// 如果加上这一行,则会死锁,是因为当超过缓冲区的时候,就成阻塞模式了
//ch <- "zhangsan5"
close(ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
// 结果
➜ first go run helloworld.go
zhangsan1
zhangsan2
zhangsan3
zhangsan4
// 此处为string零值

可以看出带缓冲的channel略有不同。尽管已经close了,但我们依旧可以从中读出关闭前写入的3个值。第四次读取时,则会返回该channel类型的零值。向这类channel写入操作也会触发panic。

channel常与range配合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
ch := make(chan string)
go generate(ch)
for v := range ch {
fmt.Printf("%s\n", v)
}
}

func generate(ch chan string) {
ch <- "zhangsan1"
ch <- "zhangsan2"
ch <- "zhangsan3"
ch <- "zhangsan4"
ch <- "zhangsan5"
close(ch)
}

channel实际用途

可用作信号源

1
2
3
4
5
6
7
8
9
10
func main() {
fmt.Println("Begin doing something!")
c := make(chan bool)
go func() {
fmt.Println("Doing something…")
close(c)
}()
<-c
fmt.Println("Done!")
}

协同多个Goroutines

同上,close channel还可以用于协同多个Goroutines,比如下面这个例子,我们创建了100个Worker Goroutine,这些Goroutine在被创建出来后都阻塞在”<-start”上,直到我们在main goroutine中给出开工的信号:”close(start)”,这些goroutines才开始真正的并发运行起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
func worker(start chan bool, index int) {
<-start
fmt.Println("This is Worker:", index)
}

func main() {
start := make(chan bool)
for i := 1; i <= 100; i++ {
go worker(start, i)
}
close(start)
select {} //deadlock we expected
}

唯一的ID服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import "fmt"

func newUniqueIDService() <-chan string {
id := make(chan string)
go func() {
var counter int64 = 0
for {
// 无缓冲 -> 阻塞
id <- fmt.Sprintf("%x", counter)
counter += 1
}
}()
return id
}
func main() {
id := newUniqueIDService()
for i := 0; i < 10; i++ {
// 这边取一个,id插入一个(自增)
fmt.Println(<-id)
}
}

$ go run testuniqueid.go
0
1
2
3
4
5
6
7
8
9

select

select特点

  • 可以处理一个或多个channel的发送与接收
  • 同时有多个可用的channel时按随机顺序处理
  • 可用空的select阻塞main函数
  • 可设置超时

惯用法:for/select

我们在使用select时很少只是对其进行一次evaluation,我们常常将其与for {}结合在一起使用,并选择适当时机从for{}中退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for {
select {
case x := <- somechan:
// … 使用x进行一些操作

case y, ok := <- someOtherchan:
// … 使用y进行一些操作,
// 检查ok值判断someOtherchan是否已经关闭

case outputChan <- z:
// … z值被成功发送到Channel上时

default:
// … 上面case均无法通信时,执行此分支
}
}

终结workers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
ch, die := make(chan bool), make(chan bool)
for i := 0; i < 10; i++ {
go worker(die, i, ch)
ch <- true
}
time.Sleep(1 * time.Second)
// 此处终结worker
close(die)
}

func worker(die chan bool, index int, ch chan bool) {
fmt.Println("Begin: This is Worker:", index)
for {
select {
case <-ch:
fmt.Println("already worded:", index)
case <-die:
return
}
}
}

终结验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main

import (
"fmt"
//"time"
)

func worker(die chan bool) {
fmt.Println("Begin: This is Worker")
for {
select {
//case xx:
//做事的分支
// get channel message
case <-die:
fmt.Println("Done: This is Worker")
// send channel message
die <- true
return
}
}
}

func main() {
die := make(chan bool)
go worker(die)
// send channel message
die <- true
// get channel message
<-die
fmt.Println("Worker goroutine has been terminated")
}
```

# 等待goroutine完成
使用`sync`包
```go
func correct() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(n int) { // 使用局部变量
fmt.Print(n)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println()
}

上述只是等待goroutine完成,却没有进行通讯。有错误值也无法返回。所以说,最好还是使用channel
如果我们希望通讯,只需要在此基础上增加channel通讯即可
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func main() {
people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"}
match := make(chan string, 1) // 为一个未匹配的发送操作提供空间
wg := new(sync.WaitGroup)
wg.Add(len(people))
for _, name := range people {
go Seek(name, match, wg)
}
wg.Wait()
select {
case name := <-match:
fmt.Printf("No one received %s’s message.\n", name)
default:
// 没有待处理的发送操作
}
}

// 函数Seek 发送一个name到match管道或从match管道接收一个peer,结束时通知wait group
func Seek(name string, match chan string, wg *sync.WaitGroup) {
select {
case peer := <-match:
fmt.Printf("%s sent a message to %s.\n", peer, name)
case match <- name:
// 等待某个goroutine接收我的消息
}
wg.Done()
}

利用多核

1
2
3
4
func init() {
numcpu := runtime.NumCPU()
runtime.GOMAXPROCS(numcpu) // 尝试使用所有可用的CPU
}

builtin

Posted on 2018-09-25 | Visitors:

int int64 string之间转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#string到int  
int,err:=strconv.Atoi(string)
#string到int64
int64, err := strconv.ParseInt(string, 10, 64)
#int到string
string:=strconv.Itoa(int)
#int64到string
string:=strconv.FormatInt(int64,10)
```in
# 断言
`value, ok = element.(T)`

demo1
```go
var a interface{}
a = "zhangsan"
// interface{} => string (断言)
a = a.(string)
// 如果直接string(a), 则报错:
cannot convert a (type interface {}) to type string: need type assertion

demo2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {

for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
} else {
fmt.Println("list[%d] is of a different type", index)
}
}
}

demo3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {


for index, element := range list{
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}

并发编程(2)

Posted on 2018-09-25 | Visitors:

并发编程示例

定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func demo(input chan interface{}) {
t1 := time.NewTimer(time.Second * 5)
t2 := time.NewTimer(time.Second * 10)

for {
select {
case msg <- input:
println(msg)

case <-t1.C:
println("5s timer")
t1.Reset(time.Second * 5)

case <-t2.C:
println("10s timer")
t2.Reset(time.Second * 10)
}
}
}

断续器

1
2
3
4
5
6
func main(){
ticker := time.NewTicker(time.Second)
for t := range ticker.C {
fmt.Println("ticker", t)
}
}

超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main(){
ch1 := make(chan int, 1)
ch2 := make(chan int, 1)

select {
case e1 := <-ch1:
//如果ch1通道成功读取数据,则执行该case处理语句
fmt.Printf("1th case is selected. e1=%v",e1)
case e2 := <-ch2:
//如果ch2通道成功读取数据,则执行该case处理语句
fmt.Printf("2th case is selected. e2=%v",e2)
case <- time.After(2 * time.Second):
fmt.Println("Timed out")
}
}

自定义定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
func main(){
var t *time.Timer

f := func(){
fmt.Printf("Expiration time : %v.\n", time.Now())
fmt.Printf("C`s len: %d\n", len(t.C))
}

t = time.AfterFunc(1*time.Second, f)
//让当前Goroutine 睡眠2s,确保大于内容的完整
//这样做原因是,time.AfterFunc的调用不会被阻塞。它会以一部的方式在到期事件来临执行我们自定义函数f。
time.Sleep(2 * time.Second)
}

使用时间控制停止ticker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main(){
//初始化断续器,间隔2s
var ticker *time.Ticker = time.NewTicker(1 * time.Second)

go func() {
for t := range ticker.C {
fmt.Println("Tick at", t)
}
}()

time.Sleep(time.Second * 5) //阻塞,则执行次数为sleep的休眠时间/ticker的时间
ticker.Stop() //停止ticker
fmt.Println("Ticker stopped")
}

并发读取文件夹所有文件的长度(使用通道)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func test1() {
// 遍历文件夹,获取文件名
dir := "./db"
paths, err := ioutil.ReadDir(dir)
check(err)
var files []string
for _, path := range paths {
if !path.IsDir() {
files = append(files, path.Name())
}
}

type FileInfo struct {
FileName string
Len int
Err error
}
way := make(chan FileInfo, 100)
for _, file := range files {
go func(fileName string) {
content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, fileName))
// 无论对错,都交给通道收取方
way <- FileInfo{
FileName: fileName,
Len: len(content),
Err: err,
}
}(file)
}

// 我们知道需要从通道中取多少条数据
for i := 0; i < len(files); i++ {
fileInfo := <-way
if fileInfo.Err != nil {
fmt.Printf("[err] file:%s, err: %s \n", fileInfo.FileName, fileInfo.Err.Error())
return
}
fmt.Printf("file:%s, len: %d \n", fileInfo.FileName, fileInfo.Len)
}
}

并发读取 文件夹所有文件的长度(使用sync.WaitGroup)

这个使用的其实就是计数器的道理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func test1() {
dir := "./db"
paths, err := ioutil.ReadDir(dir)
check(err)
var files []string
for _, path := range paths {
if !path.IsDir() {
files = append(files, path.Name())
}
}

// sizes用来计算总长度和阻塞直至所有goroutine走完
sizes := make(chan int64)
// 计数器
var wg sync.WaitGroup
for _, file := range files {
wg.Add(1)
go func(fileName string) {
defer wg.Done()
fileInfo, _ := os.Stat(fmt.Sprintf("%s/%s", dir, fileName))
sizes <- fileInfo.Size()
}(file)
}
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
fmt.Printf("size:%d\n", size)
total += size
}
fmt.Printf("total size: %d\n", total)
}

令牌-另一种信号计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
func check(err error) {
if err != nil {
panic(err)
}
}

func main() {
// 链接列表
workList := make(chan []string)
// 存储 已经处理的 链接,使用 map是为了去重
seen := make(map[string]bool)
// n是为了保证 爬虫一直 爬
n := 1
// 初始化
go func() { workList <- []string{"http://www.jianshu.com"} }()
for i := n; i > 0; i++ {
list := <-workList
for _, link := range list {
if !seen[link] {
seen[link] = true
n++
go func(link string) {
workList <- crawl(link)
}(link)

}
}
}
}

var tokens = make(chan struct{}, 20)

// 获取网页的链接 通过令牌
func crawl(url string) (list []string) {
fmt.Println(url)
tokens <- struct{}{}
list, err := Extract(url)
<-tokens
if err != nil {
log.Print(err)
return
}
return
}

// 获取网页的链接
func Extract(url string) (list []string, err error) {
resp, err := http.Get(url)
if err != nil {
return
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s, err %s", url, err.Error())
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML, err %s", url, err.Error())
}
visitNode := func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key != "href" {
continue
}
link, err := resp.Request.URL.Parse(a.Val)
if err != nil {
continue
}
list = append(list, link.String())
}

}
}
// 将函数作为参数传入 (匿名函数)
forEachNode(doc, visitNode, nil)
return
}

func forEachNode(n *html.Node, pre, next func(node *html.Node)) {
if pre != nil {
pre(n)
}
for n := n.FirstChild; n != nil; n = n.NextSibling {
forEachNode(n, pre, next)
}
if next != nil {
next(n)
}

}

os

Posted on 2018-09-25 | Visitors:

遍历目录

使用ioutil标准包

1
2
3
4
5
6
7
8
9
10
11
12
func ListDir(filePath string) (files []string, err error) {
paths, err := ioutil.ReadDir(filePath)
if err != nil {
return
}
for _, f := range paths {
if !f.IsDir() {
files = append(files, f.Name())
}
}
return
}

按行读取文件

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
f, err := os.Open("bugIds.txt")
utils.Check("os.Open", err)
bf := bufio.NewReader(f)
for {
line, _, e := bf.ReadLine()
if e == io.EOF {
break
}
fix(string(line))
}
}

正则表达式

Posted on 2018-09-25 | Visitors:

用法

单一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.                   匹配任意一个字符,如果设置 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类”见后面的说明

复合

1
2
xy             匹配 xy(x 后面跟随 y)
x|y 匹配 x 或 y (优先匹配 x)

重复

1
2
3
4
5
6
7
8
9
10
11
12
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

分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(子表达式)            被捕获的组,该组被编号 (子匹配)
(?P<命名>子表达式) 被捕获的组,该组被编号且被命名 (子匹配)
(?:子表达式) 非捕获的组 (子匹配)
(?标记) 在组内设置标记,非捕获,标记影响当前组后的正则表达式
(?标记:子表达式) 在组内设置标记,非捕获,标记影响当前组内的子表达式

标记的语法是:
xyz (设置 xyz 标记)
-xyz (清除 xyz 标记)
xy-z (设置 xy 标记, 清除 z 标记)

可以设置的标记有:
i 不区分大小写 (默认为 false)
m 多行模式:让 ^ 和 $ 匹配整个文本的开头和结尾,而非行首和行尾(默认为 false)
s 让 . 匹配 \n (默认为 false)
U 非贪婪模式:交换 x* 和 x*? 等的含义 (默认为 false)

位置标记:

1
2
3
4
5
6
^              如果标记 m=true 则匹配行首,否则匹配整个文本的开头(m 默认为 false)
$ 如果标记 m=true 则匹配行尾,否则匹配整个文本的结尾(m 默认为 false)
\A 匹配整个文本的开头,忽略 m 标记
\b 匹配单词边界
\B 匹配非单词边界
\z 匹配整个文本的结尾,忽略 m 标记

转序标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
\a             匹配响铃符    (相当于 \x07)
注意:正则表达式中不能使用 \b 匹配退格符,因为 \b 被用来匹配单词边界,
可以使用 \x08 表示退格符。
\f 匹配换页符 (相当于 \x0C)
\t 匹配横向制表符(相当于 \x09)
\n 匹配换行符 (相当于 \x0A)
\r 匹配回车符 (相当于 \x0D)
\v 匹配纵向制表符(相当于 \x0B)
\123 匹配 8 進制编码所代表的字符(必须是 3 位数字)
\x7F 匹配 16 進制编码所代表的字符(必须是 3 位数字)
\x{10FFFF} 匹配 16 進制编码所代表的字符(最大值 10FFFF )
\Q...\E 匹配 \Q 和 \E 之间的文本,忽略文本中的正则语法

\\ 匹配字符 \
\^ 匹配字符 ^
\$ 匹配字符 $
\. 匹配字符 .
\* 匹配字符 *
\+ 匹配字符 +
\? 匹配字符 ?
\{ 匹配字符 {
\} 匹配字符 }
\( 匹配字符 (
\) 匹配字符 )
\[ 匹配字符 [
\] 匹配字符 ]
\| 匹配字符 |

可以将“命名字符类”作为“字符类”的元素:

1
2
3
4
5
6
7
8
[\d]           匹配数字 (相当于 \d)
[^\d] 匹配非数字 (相当于 \D)
[\D] 匹配非数字 (相当于 \D)
[^\D] 匹配数字 (相当于 \d)
[[:name:]] 命名的“ASCII 类”包含在“字符类”中 (相当于 [:name:])
[^[:name:]] 命名的“ASCII 类”不包含在“字符类”中 (相当于 [:^name:])
[\p{Name}] 命名的“Unicode 类”包含在“字符类”中 (相当于 \p{Name})
[^\p{Name}] 命名的“Unicode 类”不包含在“字符类”中 (相当于 \P{Name})

说明

字符类取值如下(字符类包含Perl类、ASCII类、Unicode类): x 单个字符 A-Z 字符范围(包含首尾字符) \小写字母 Perl类 [:ASCII类名:] ASCII类 \p{Unicode脚本类名} Unicode类 (脚本类) \pUnicode普通类名 Unicode类 (普通类)

“Perl 类”取值如下:

1
2
3
4
5
6
\d             数字 (相当于 [0-9])
\D 非数字 (相当于 [^0-9])
\s 空白 (相当于 [\t\n\f\r ])
\S 非空白 (相当于[^\t\n\f\r ])
\w 单词字符 (相当于 [0-9A-Za-z_])
\W 非单词字符 (相当于 [^0-9A-Za-z_])

“ASCII 类”取值如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[:alnum:]      字母数字 (相当于 [0-9A-Za-z])
[:alpha:] 字母 (相当于 [A-Za-z])
[:ascii:] ASCII 字符集 (相当于 [\x00-\x7F])
[:blank:] 空白占位符 (相当于 [\t ])
[:cntrl:] 控制字符 (相当于 [\x00-\x1F\x7F])
[:digit:] 数字 (相当于 [0-9])
[:graph:] 图形字符 (相当于 [!-~] 相当于 [A-Za-z0-9!"# $%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
[:lower:] 小写字母 (相当于 [a-z])
[:print:] 可打印字符 (相当于 [ -~] 相当于 [ [:graph:]])
[:punct:] 标点符号 (相当于 [!-/:-@[-`{-~])
[:space:] 空白字符(相当于 [\t\n\v\f\r ])
[:upper:] 大写字母(相当于 [A-Z])
[:word:] 单词字符(相当于 [0-9A-Za-z_])
[:xdigit:] 16 進制字符集(相当于 [0-9A-Fa-f])

“Unicode 类”取值如下—普通类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
C                 -其他-          (other)
Cc 控制字符 (control)
Cf 格式 (format)
Co 私人使用区 (private use)
Cs 代理区 (surrogate)
L -字母- (letter)
Ll 小写字母 (lowercase letter)
Lm 修饰字母 (modifier letter)
Lo 其它字母 (other letter)
Lt 首字母大写字母 (titlecase letter)
Lu 大写字母 (uppercase letter)
M -标记- (mark)
Mc 间距标记 (spacing mark)
Me 关闭标记 (enclosing mark)
Mn 非间距标记 (non-spacing mark)
N -数字- (number)
Nd 十進制数字 (decimal number)
Nl 字母数字 (letter number)
No 其它数字 (other number)
P -标点- (punctuation)
Pc 连接符标点 (connector punctuation)
Pd 破折号标点符号 (dash punctuation)
Pe 关闭的标点符号 (close punctuation)
Pf 最后的标点符号 (final punctuation)
Pi 最初的标点符号 (initial punctuation)
Po 其他标点符号 (other punctuation)
Ps 开放的标点符号 (open punctuation)
S -符号- (symbol)
Sc 货币符号 (currency symbol)
Sk 修饰符号 (modifier symbol)
Sm 数学符号 (math symbol)
So 其他符号 (other symbol)
Z -分隔符- (separator)
Zl 行分隔符 (line separator)
Zp 段落分隔符 (paragraph separator)
Zs 空白分隔符 (space separator)

“Unicode 类”取值如下—脚本类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Arabic                  阿拉伯文
Armenian 亚美尼亚文
Balinese 巴厘岛文
Bengali 孟加拉文
Bopomofo 汉语拼音字母
Braille 盲文
Buginese 布吉文
Buhid 布希德文
Canadian_Aboriginal 加拿大土著文
Carian 卡里亚文
Cham 占族文
Cherokee 切诺基文
Common 普通的,字符不是特定于一个脚本
Coptic 科普特文
Cuneiform 楔形文字
Cypriot 塞浦路斯文
Cyrillic 斯拉夫文
Deseret 犹他州文
Devanagari 梵文
Ethiopic 衣索比亚文
Georgian 格鲁吉亚文
Glagolitic 格拉哥里文
Gothic 哥特文
Greek 希腊
Gujarati 古吉拉特文
Gurmukhi 果鲁穆奇文
Han 汉文
Hangul 韩文
Hanunoo 哈鲁喏文
Hebrew 希伯来文
Hiragana 平假名(日语)
Inherited 继承前一个字符的脚本
Kannada 坎那达文
Katakana 片假名(日语)
Kayah_Li 克耶字母
Kharoshthi 卡罗须提文
Khmer 高棉文
Lao 老挝文
Latin 拉丁文
Lepcha 雷布查文
Limbu 林布文
Linear_B B类线形文字(古希腊)
Lycian 利西亚文
Lydian 吕底亚文
Malayalam 马拉雅拉姆文
Mongolian 蒙古文
Myanmar 缅甸文
New_Tai_Lue 新傣仂文
Nko Nko文
Ogham 欧甘文
Ol_Chiki 桑塔利文
Old_Italic 古意大利文
Old_Persian 古波斯文
Oriya 奥里亚文
Osmanya 奥斯曼亚文
Phags_Pa 八思巴文
Phoenician 腓尼基文
Rejang 拉让文
Runic 古代北欧文字
Saurashtra 索拉什特拉文(印度县城)
Shavian 萧伯纳文
Sinhala 僧伽罗文
Sundanese 巽他文
Syloti_Nagri 锡尔赫特文
Syriac 叙利亚文
Tagalog 塔加拉文
Tagbanwa 塔格巴努亚文
Tai_Le 德宏傣文
Tamil 泰米尔文
Telugu 泰卢固文
Thaana 塔安那文
Thai 泰文
Tibetan 藏文
Tifinagh 提非纳文
Ugaritic 乌加里特文
Vai 瓦伊文
Yi 彝文

注意

  对于 [a-z] 这样的正则表达式,如果要在 [] 中匹配 - ,可以将 - 放在 [] 的开头或结尾,例如 [-a-z] 或 [a-z-]

  可以在 [] 中使用转义字符:\f、\t、\n、\r、\v、\377、\xFF、\x{10FFFF}、\、\^、\$、.、*、+、\?、{、}、(、)、[、]、|(具体含义见上面的说明)

  如果在正则表达式中使用了分组,则在执行正则替换的时候,“替换内容”中可以使用 $1、${1}、$name、${name} 这样的“分组引用符”获取相应的分组内容。其中 $0 代表整个匹配项,$1 代表第 1 个分组,$2 代表第 2 个分组,……。

  如果“分组引用符”是 $name 的形式,则在解析的时候,name 是取尽可能长的字符串,比如:$1x 相当于 ${1x},而不是${1}x,再比如:$10 相当于 ${10},而不是 ${1}0。

  由于 $ 字符会被转义,所以要在“替换内容”中使用 $ 字符,可以用 \$ 代替。

  上面介绍的正则表达式语法是“Perl 语法”,除了“Perl 语法”外,Go 语言中还有另一种“POSIX 语法”,“POSIX 语法”除了不能使用“Perl 类”之外,其它都一样。

示例:

func main() { text := Hello 世界!123 Go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 查找连续的小写字母
reg := regexp.MustCompile(`[a-z]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["ello" "o"]

// 查找连续的非小写字母
reg = regexp.MustCompile(`[^a-z]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["H" " 世界!123 G" "."]

// 查找连续的单词字母
reg = regexp.MustCompile(`[\w]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello" "123" "Go"]

// 查找连续的非单词字母、非空白字符
reg = regexp.MustCompile(`[^\w\s]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["世界!" "."]

// 查找连续的大写字母
reg = regexp.MustCompile(`[[:upper:]]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["H" "G"]

// 查找连续的非 ASCII 字符
reg = regexp.MustCompile(`[[:^ascii:]]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["世界!"]

// 查找连续的标点符号
reg = regexp.MustCompile(`[\pP]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["!" "."]

// 查找连续的非标点符号字符
reg = regexp.MustCompile(`[\PP]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello 世界" "123 Go"]

// 查找连续的汉字
reg = regexp.MustCompile(`[\p{Han}]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["世界"]

// 查找连续的非汉字字符
reg = regexp.MustCompile(`[\P{Han}]+`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello " "!123 Go."]

// 查找 Hello 或 Go
reg = regexp.MustCompile(`Hello|Go`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello" "Go"]

// 查找行首以 H 开头,以空格结尾的字符串
reg = regexp.MustCompile(`^H.*\s`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello 世界!123 "]

// 查找行首以 H 开头,以空白结尾的字符串(非贪婪模式)
reg = regexp.MustCompile(`(?U)^H.*\s`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello "]

// 查找以 hello 开头(忽略大小写),以 Go 结尾的字符串
reg = regexp.MustCompile(`(?i:^hello).*Go`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello 世界!123 Go"]

// 查找 Go.
reg = regexp.MustCompile(`\QGo.\E`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Go."]

// 查找从行首开始,以空格结尾的字符串(非贪婪模式)
reg = regexp.MustCompile(`(?U)^.* `)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello "]

// 查找以空格开头,到行尾结束,中间不包含空格字符串
reg = regexp.MustCompile(` [^ ]*$`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// [" Go."]

// 查找“单词边界”之间的字符串
reg = regexp.MustCompile(`(?U)\b.+\b`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello" " 世界!" "123" " " "Go"]

// 查找连续 1 次到 4 次的非空格字符,并以 o 结尾的字符串
reg = regexp.MustCompile(`[^ ]{1,4}o`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello" "Go"]

// 查找 Hello 或 Go
reg = regexp.MustCompile(`(?:Hell|G)o`)
fmt.Printf("%q\n", reg.FindAllString(text, -1))
// ["Hello" "Go"]

// 查找 Hello 或 Go,替换为 Hellooo、Gooo
reg = regexp.MustCompile(`(?PHell|G)o`)
fmt.Printf("%q\n", reg.ReplaceAllString(text, "${n}ooo"))
// "Hellooo 世界!123 Gooo."

// 交换 Hello 和 Go
reg = regexp.MustCompile(`(Hello)(.*)(Go)`)
fmt.Printf("%q\n", reg.ReplaceAllString(text, "$3$2$1"))
// "Go 世界!123 Hello."

// 特殊字符的查找
reg = regexp.MustCompile("[\\f\\t\\n\\r\\v\\123\\x7F\\x{10FFFF}\\\\\\^\\$\\.\\*\\+\\?\\{\\}\\(\\)\\[\\]\\|]")
fmt.Printf("%q\n", reg.ReplaceAllString("\f\t\n\r\v\123\x7F\U0010FFFF\\^$.*+?{}()[]|", "-"))
// "----------------------"

1…4567

chimps

63 posts
32 tags
RSS
© 京ICP备19001740号-1 2020 chimps
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4