golang中判断两个slice是否相等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"bytes"
"fmt"
)
func main() {
a := []byte{0, 1, 3, 2}
b := []byte{0, 1, 3, 2}
c := []byte{1, 1, 3, 2}
fmt.Println(bytes.Equal(a, b))
fmt.Println(bytes.Equal(a, c))
}
|
计算中文长度
1
2
3
|
title := "中国人a"
fmt.Println(len([]rune(title))) // 第一种方案:输出 4
fmt.Println(utf8.RuneCountInString(title)) // 第二种方案:输出 4
|
数据库初始化
什么是 DSN 信息?
DSN 全称为 Data Source Name,表示 数据源信息,用于定义如何连接数据库。不同数据库的 DSN 格式是不同的,这取决于数据库驱动的实现,下面是 go-sql-driver/sql 的 DSN 格式,如下所示:
1
2
|
// [用户名[:密码]@][协议(数据库服务器地址)]]/数据库名称?参数列表
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
|
如何配置 sql. DB 的 SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime
如何配置 sql. DB 的 SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime
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
|
import (
"github.com/go-sql-driver/mysql"
_ "github.com/go-sql-driver/mysql" // 引入 mysql 驱动
)
// 初始化单个数据库
func initDB() {
var err error
// 设置数据库连接信息
config := mysql.Config{
User: "root",
Passwd: "123456",
Net: "tcp",
Addr: "127.0.0.1:3305",
DBName: "goblog",
AllowNativePasswords: true,
}
// 准备数据库连接池
db, err = sql.Open("mysql", config.FormatDSN())
checkError(err)
// 设置最大连接数,不能超过数据库本身设置的最大连接数 `show variables like 'max_connections';`
db.SetMaxOpenConns(200) // yim 设置了 512
// 设置最大空闲连接数
db.SetMaxIdleConns(100) // yim 设置了 256
// 设置每个连接的过期时间,这里的推荐,比较保守的做法是设置五分钟
db.SetConnMaxLifetime(5 * time.Minute)
err = db.Ping()
checkError(err)
}
func checkError(err error) {
if err != nil {
log.Fatal(err)
}
}
|
根据结构体返回字段名切片
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
|
func main() {
// 输出:[ `name` `password` `gender` `create_time` `update_time` `id` `number` ]
fmt.Println(RawFieldNames(&User{}))
}
const dbTag = "db" // 根据哪个标签获取字段名称
type User struct {
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Gender string `db:"gender"` // 男|女|未公开
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
Id int64 `db:"id"`
Number string `db:"number"` // 学号
}
func RawFieldNames(in interface{}) []string {
out := make([]string, 0)
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// we only accept structs
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
}
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
// gets us a StructField
fi := typ.Field(i)
if tagv := fi.Tag.Get(dbTag); tagv != "" {
out = append(out, fmt.Sprintf(" `%s` ", tagv))
} else {
out = append(out, fmt.Sprintf( `"%s"` , fi.Name))
}
}
return out
}
|
移除数组元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func Remove(strings []string, strs ...string) []string {
out := append([]string(nil), strings...)
for _, str := range strs {
var n int
for _, v := range out {
if v != str {
out[n] = v
n++
}
}
out = out[:n]
}
return out
}
|
超时处理 & 定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 超时处理
select {
case <-ch:
case <-time.After(time.Second * 1): // 1秒后超时触发
fmt.Println("timeout")
}
// 定时器
count := 0
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
<-ticker.C
fmt.Println(count)
count++
if count >= 5 {
break
}
}
|
获取真实IP
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
|
func GetRemoteAddr(r *http.Request) string {
v := r.Header.Get("X-Forward-For")
if len(v) > 0 {
return v
}
return r.RemoteAddr
}
// 获取本机ip
var ip string
func GetLocalIP() string {
if ip != "" {
return ip
}
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
ip = ipnet.IP.String()
return ip
}
}
}
return ""
}
|
md5 && hmac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func Md5(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func HmacSha256(data string, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
// 简单的密码加密方案
func makePassword(pwd string) string {
return helper.Md5(helper.HmacSha256("盐", pwd))
}
|
利用反射,结构体转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
61
62
|
// 结构体转map方法
func Struct2Map(obj interface{}) map[string]interface{} {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
var data = make(map[string]interface{})
if t.Kind() != reflect.Struct {
return data
}
for i := 0; i < t.NumField(); i++ {
data[t.Field(i).Name] = v.Field(i).Interface()
}
return data
}
// 结构体转map方法,key直接取结构体上变量名的 json 名
func Struct2MapJson(obj interface{}) map[string]interface{} {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
var data = make(map[string]interface{})
if t.Kind() != reflect.Struct {
return data
}
for i := 0; i < t.NumField(); i++ {
data[t.Field(i).Tag.Get("json")] = v.Field(i).Interface()
}
return data
}
// 结构体转map方法,处理嵌套结构体
func StructToMapByJson(obj interface{}) map[string]interface{} {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
var data = make(map[string]interface{})
if t.Kind() != reflect.Struct {
return data
}
for i := 0; i < t.NumField(); i++ {
value := v.Field(i)
// 处理嵌套结构体
t2 := reflect.TypeOf(value.Interface())
if t2.Kind() == reflect.Struct {
for j := 0; j < t2.NumField(); j++ {
temp := t2.Field(j)
_ = temp
data[t2.Field(j).Tag.Get("json")] = value.Field(j).Interface()
}
continue
}
data[t.Field(i).Tag.Get("json")] = value.Interface()
}
return data
}
|
同步队列+并发消费
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
|
package main
import (
"context"
"fmt"
"sync"
"time"
)
// 同步队列+并发消费案例
type (
Task struct {
StartTime time.Time
}
TaskManager struct {
Tasks chan *Task
Wg *sync.WaitGroup
Ctx context.Context
}
)
func main() {
timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) // 10分钟超时
defer cancel()
var manager = &TaskManager{
Tasks: make(chan *Task, 10), // 等待队列10,并发5 执行任务
Wg: &sync.WaitGroup{},
Ctx: timeoutCtx,
}
runDaemon(manager)
// 生产者:负责生产任务
var tasks []*Task
tasks = append(tasks, &Task{})
for i := 0; i < 20; i++ {
manager.Wg.Add(1)
manager.Tasks <- &Task{StartTime: time.Now()}
}
manager.Wg.Wait()
}
func runDaemon(manager *TaskManager) {
// 并发 5 个协程消费队列
for i := 0; i < 5; i++ {
go func() {
for {
select {
case task := <-manager.Tasks:
// todo 执行相关业务逻辑
fmt.Println("消费任务:start_time:", task.StartTime.String())
manager.Wg.Done()
case <-manager.Ctx.Done():
return
}
}
}()
}
}
|
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
// Strval 获取变量的字符串值
// 浮点型 3.0将会转换成字符串3, "3"
// 非数值或字符类型的变量将会被转换成JSON格式字符串
func Strval(value interface{}) string {
// interface 转 string
var key string
if value == nil {
return key
}
switch value.(type) {
case float64:
ft := value.(float64)
key = strconv.FormatFloat(ft, 'f', -1, 64)
case float32:
ft := value.(float32)
key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
case int:
it := value.(int)
key = strconv.Itoa(it)
case uint:
it := value.(uint)
key = strconv.Itoa(int(it))
case int8:
it := value.(int8)
key = strconv.Itoa(int(it))
case uint8:
it := value.(uint8)
key = strconv.Itoa(int(it))
case int16:
it := value.(int16)
key = strconv.Itoa(int(it))
case uint16:
it := value.(uint16)
key = strconv.Itoa(int(it))
case int32:
it := value.(int32)
key = strconv.Itoa(int(it))
case uint32:
it := value.(uint32)
key = strconv.Itoa(int(it))
case int64:
it := value.(int64)
key = strconv.FormatInt(it, 10)
case uint64:
it := value.(uint64)
key = strconv.FormatUint(it, 10)
case string:
key = value.(string)
case []byte:
key = string(value.([]byte))
default:
newValue, _ := json.Marshal(value)
key = string(newValue)
}
return key
}
|
map实现集合方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 第一种
m := make(map[string]bool)
m["a"] = true
if m["a"] {
fmt.Println("a exist")
}
if m["b"] {
fmt.Println("b exist")
} else {
fmt.Println("b no exist")
}
// 第二种
m2 := make(map[string]struct{})
m2["c"] = struct{}{}
if _, ok := m2["c"]; ok {
fmt.Println("c exist")
}
if _, ok := m2["d"]; ok {
fmt.Println("d exist")
} else {
fmt.Println("d no exist")
}
|
浮点数四舍五入
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
|
// Round 四舍五入
// 返回将 val 根据指定精度 precision(十进制小数点后数字的数目)进行四舍五入的结果,precision可为0
func Round(val float64, precision int) float64 {
p := math.Pow10(precision)
return math.Floor(val*p+0.5) / p
}
// 元转分
func Yuan2Fen(f float64) int64 {
if f == 0 {
return 0
}
decimalValue := decimal.NewFromFloat(f)
decimalValue = decimalValue.Mul(decimal.NewFromInt(100))
return decimalValue.BigInt().Int64()
}
// 分转元
func FenToYuan(i int64) string {
if i == 0 {
return "0.00"
}
decimalValue := decimal.NewFromInt(i)
decimalValue = decimalValue.Div(decimal.NewFromInt(100))
return decimalValue.String()
}
|
url编码特殊处理
1
2
3
4
5
6
7
8
9
10
|
// url.QueryEscape 不会编译“~”字符,但php那边会编译成"%7E",所以需要手动转换
func urlEncode(str string) string {
specialChar := map[string]string{
"~": "%7E",
}
for k, v := range specialChar {
str = strings.Replace(url.QueryEscape(str), k, v, -1)
}
return str
}
|
下载网络文件到本地
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 下载文件到本地
// url 网络文件链接
// filepath 本地文件存放路径,如/Users/xxx/Downloads/test.wav
func DownLoad(url, filepath string) error {
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Create output file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// copy stream
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}
|
获取链接后缀
1
2
3
4
5
|
func main() {
url := "https://example.com/files/document.pdf"
fmt.Println(path.Base(url)) // 输出 document.pdf
fmt.Println(path.Ext(url)) // 输出 .pdf
}
|
用正则表达式处理html代码
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
|
// 去掉html代码中的标签,只保留纯文本
func TrimHtml(src string) string {
//将HTML标签全转换成小写
re, _ := regexp.Compile(`<[\S\s]+?>`)
src = re.ReplaceAllStringFunc(src, strings.ToLower)
//去除STYLE
re, _ = regexp.Compile(`<style[\S\s]+?</style>`)
src = re.ReplaceAllString(src, "")
//去除SCRIPT
re, _ = regexp.Compile(`<script[\S\s]+?</script>`)
src = re.ReplaceAllString(src, "")
//去除所有尖括号内的HTML代码
re, _ = regexp.Compile(`<[\S\s]+?>`)
src = re.ReplaceAllString(src, "")
//去除连续的换行符
re, _ = regexp.Compile(`\s{2,}`)
src = re.ReplaceAllString(src, "\n\n")
return strings.TrimSpace(src)
}
// 获取html代码中的图片链接
func GetHtmlImages(htmls string) []string {
var imgRE = regexp.MustCompile(`<img[^>]+\bsrc=["']([^"']+)["']`)
imgs := imgRE.FindAllStringSubmatch(htmls, -1)
out := make([]string, len(imgs))
for i := range out {
out[i] = imgs[i][1]
}
return out
}
|
对称加密算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// sha1加密
func Sha1(s string) string {
h := sha1.New()
h.Write([]byte(s))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// HMACSHA1是从 SHA1 哈希函数构造的一种键控哈希算法
func HMACSHA1(secret, value string) string {
key := []byte(secret)
mac := hmac.New(sha1.New, key)
mac.Write([]byte(value))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}
|
获取文件链接URL的后缀
1
2
3
4
5
6
7
8
9
|
// 获取文件链接的后缀
func GetUrlFileExt(requestUrl string) string {
parsedURL, err := url.Parse(requestUrl)
if err != nil {
return ""
}
fileName := filepath.Base(parsedURL.Path)
return filepath.Ext(fileName)
}
|