Go-技巧积累

使用系统粘贴板

利用 github.com/atotto/clipboard 模块, 如:

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

import (
"fmt"
"github.com/atotto/clipboard"
)

func main() {
text := "Hello, World!"

err := clipboard.WriteAll(text)
if err != nil {
fmt.Println("Error copying to clipboard:", err)
return
}

fmt.Println("Text copied to clipboard successfully!")
}

` 和 “

前者 (反引号) 用于包裹原始字面值, 后者 (双引号) 用于包裹解释字面值.

正则表达式捕获

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"
"regexp"
)

func main() {
// 正则表达式模式使用括号来捕获子字符串
re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)

// 待匹配的字符串
str := "今天是2022-01-31,明天是2022-02-01。"

// 查找匹配的子字符串
match := re.FindStringSubmatch(str)

// 输出捕获的子字符串
if len(match) > 0 {
fmt.Println("完整匹配:", match[0])
fmt.Println("年份:", match[1])
fmt.Println("月份:", match[2])
fmt.Println("日期:", match[3])
} else {
fmt.Println("未找到匹配的日期")
}
}

查找字符串中子字符串的起始和结束索引

判断一个变量的类型

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 Person struct {
Name string
Age int
}

func main() {
p := Person{
Name: "John",
Age: 30,
}

var v interface{} = p

if isPerson(v) {
fmt.Println("It's Person")
} else {
fmt.Println("It's not Person")
}
}

func isPerson(v interface{}) bool {
personType := reflect.TypeOf(Person{})
valueType := reflect.TypeOf(v)

if valueType.Kind() == reflect.Ptr {
valueType = valueType.Elem()
}

return valueType == personType
}

判断一个成员是否被结构体定义

reflect 包中的 FieldByName 函数:

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
package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string
Age int
Email string
}

func main() {
p := Person{
Name: "John",
Age: 30,
}

_, exists := reflect.TypeOf(p).FieldByName("hello")
if exists {
fmt.Println("hello is defined")
} else {
fmt.Println("hello does not define")
}
}

判断一个文件是否为可执行文件

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

import (
"fmt"
"os"
)

func main() {
filename := "example.exe"

fileInfo, err := os.Stat(filename)
if err != nil {
fmt.Println("Error:", err)
return
}

mode := fileInfo.Mode()
if mode.IsRegular() && mode.Perm()&0100 != 0 {
fmt.Println("The file is executable.")
} else {
fmt.Println("The file is not executable.")
}
}

启动一个独立于源程序的进程

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
package main

import (
"log"
"os"
"os/exec"
"syscall"
)

func main() {
// 创建一个新的命令
cmd := exec.Command("/path/to/your/program", "arg1", "arg2")

// 设置命令的输出和错误输出
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// 设置命令的进程组ID,以便它在父进程退出后继续运行
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}

// 启动命令
err := cmd.Start()
if err != nil {
log.Fatalf("启动进程失败:%v", err)
}

// 等待命令完成
err = cmd.Wait()
if err != nil {
log.Printf("命令执行失败:%v", err)
}
}

简短声明的注意事项

:= 运算符只能用于函数内部, 在函数外部声明需要用完整的 var 声明语法:

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

import "fmt"

var Protocol = []string{} // 在函数外部声明并初始化切片变量

func main() {
// 在 main 函数中使用 Protocol 切片
Protocol = append(Protocol, "HTTP", "HTTPS", "FTP")

// 打印切片
fmt.Println(Protocol)
}

strings 包中的 Builder, Reader, Replacer 类型

type Builder

空结构体, 用于标记类型:

1
2
3
type Builder struct {
// contains filtered or unexported fields
}

该类型用于高效构建大型字符串, 提供有创建, 拼接字符串的方法.

示例:

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

import (
"fmt"
"strings"
)

func main() {
var builder strings.Builder

builder.WriteString("Hello, ")
builder.WriteString("World!")

result := builder.String()
fmt.Println(result) // 输出: Hello, World!
}

type Reader

1
2
3
type Reader struct {
// contains filtered or unexported fields
}

该类型实现了 io.Reader 接口, 用于从字符串中读取数据.

示例:

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

import (
"fmt"
"strings"
)

func main() {
reader := strings.NewReader("Hello, World!")

buffer := make([]byte, 5)
n, err := reader.Read(buffer)
if err != nil {
fmt.Println(err)
return
}

fmt.Println(string(buffer[:n])) // 输出: Hello
}

type Replacer

1
2
3
type Replacer struct {
// contains filtered or unexported fields
}

提供方便的字符串替换操作.

示例:

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

import (
"fmt"
"strings"
)

func main() {
replacer := strings.NewReplacer("Hello", "Hi", "World", "Gopher")

result := replacer.Replace("Hello, World!")
fmt.Println(result) // 输出: Hi, Gopher!
}

展开一个变量的所有内容

用 “github.com/davecgh/go-spew/spew” 包:

1
2
3
4
5
import "github.com/davecgh/go-spew/spew"

spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)

语法展开切片语法

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

import "fmt"

func printNames(names ...string) {
for _, name := range names {
fmt.Println(name)
}
}

func main() {
nameSlice := []string{"Alice", "Bob", "Carol"}

printNames(nameSlice...) // 使用...语法展开切片作为参数

// 或者可以直接传递元素列表
printNames("David", "Eve", "Frank")
}

这里函数接受的是不确定数量的 string 数量, 因此直接传入切片不行, 需要将切片展开.

对数组的不同索引做处理

利用 switch 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var t textinput.Model
for i := range m.inputs {
t = textinput.New()
t.Cursor.Style = cursorStyle
t.CharLimit = 32

switch i {
case 0:
t.Placeholder = "Nickname"
t.Focus()
t.PromptStyle = focusedStyle
t.TextStyle = focusedStyle
case 1:
t.Placeholder = "Email"
t.CharLimit = 64
case 2:
t.Placeholder = "Password"
t.EchoMode = textinput.EchoPassword
t.EchoCharacter = '•'
}

m.inputs[i] = t
}

Go 语言字符串排序

利用 sort 包:

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

import (
"fmt"
"sort"
)

func main() {
names := []string{"Alice", "Bob", "Eve", "David", "Carol"}

sort.Strings(names)

fmt.Println(names)
}

输出为:

1
[Alice Bob Carol David Eve]

switch true

switch 语句没有表达式, 是一种特殊形式的 switch 语句, 用于替代一系列的 if-else 语句. 其匹配执行第一个满足条件的 case 分支:

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

import "fmt"

func main() {
day := 3

switch {
case day == 1:
fmt.Println("day one")
case day == 2:
fmt.Println("day two")
case day == 3:
fmt.Println("day three")
default:
fmt.Println("other")
}
}

strconv 库的使用

用于类型转换, 如将数字转换为字符串:

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

import (
"fmt"
"strconv"
)

func main() {
str := "Hello"
num := 42

result := str + " " + strconv.Itoa(num)
fmt.Println(result)

result2 := fmt.Sprintf("%s %d", str, num)
fmt.Println(result2)
}

用 iota 生成连续递增值

iota 名称来源于希腊字母表中的第九个字母, 表示第九个常量, 其在 Go 中用于在常量声明中生成连续递增值, 如:

1
2
3
4
5
const (
todo status = iota // 0
inProgress // 1
done // 2
)

os/exec package 的用法

用于运行外部命令, 不同于 C 语言的 system 调用, 其不调用系统 shell, 也无管道和通配符等.

示例:

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

import (
"bytes"
"fmt"
"os/exec"
)

func main() {
cmd := exec.Command("grep", "world")
input := []byte("Hello, world!\nGoodbye, world!")
cmd.Stdin = bytes.NewBuffer(input)

output, err := cmd.Output()
if err != nil {
fmt.Println("Fail to run ", err)
return
}

fmt.Println(string(output))
}

cmd.Output() 会运行并返回值.

类型断言语法

用于在运行时, 判断一个 “接口类型” 的值的 “实际类型”, 如:

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

import "fmt"

func processMessage(msg interface{}) {
switch msg := msg.(type) {
case string:
fmt.Println("接收到字符串消息:", msg)
case int:
fmt.Println("接收到整数消息:", msg)
default:
fmt.Println("接收到未知类型的消息")
}
}

func main() {
processMessage("Hello")
processMessage(42)
processMessage(true)
}

(意思就是其返回值是 “类型”)

也可以转换为对应类型.

返回 error 类型的函数

引入 errors 包, 用 errors.New() 创建 error 类型的值, 示例:

1
2
3
4
import "errors"
func Test() error {
return errors.New("Test")
}

获取当前时间并格式化输出

使用 time 标准库, 用 time.Now() 获取当前时间, 用 time.Format() 格式化, 示例:

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

import (
"fmt"
"time"
)

func main() {
currentTime := time.Now()
formattedTime := currentTime.Format("20060102-15-04-05")
fmt.Println(formattedTime)
}

复制文件

io.Copy(dest, src), 示例:

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
package main

import (
"fmt"
"io"
"os"
)

func main() {
sourceFile := "source.txt"
destinationFile := "destination.txt"

err := copyFile(sourceFile, destinationFile)
if err != nil {
fmt.Println("Failed to copy file:", err)
return
}

fmt.Println("File copied successfully.")
}

func copyFile(sourceFile, destinationFile string) error {
src, err := os.Open(sourceFile)
if err != nil {
return err
}
defer src.Close()

dest, err := os.Create(destinationFile)
if err != nil {
return err
}
defer dest.Close()

_, err = io.Copy(dest, src)
if err != nil {
return err
}

return nil
}

判断文件/目录是否存在

os.Stat(), 其返回一个 os.FileInfo 类型的对象, 包含文件/目录的信息. 示例:

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

import (
"fmt"
"os"
)

func main() {
dirname := "mydir"

_, err := os.Stat(dirname)

if os.IsNotExist(err) {
fmt.Println("Directory does not exist.")
} else if err != nil {
fmt.Println("Failed to get directory information:", err)
} else {
fmt.Println("Directory exists.")
}
}

创建目录

os.Mkdir()os.MkdirAll() (后者会递归创建), 示例:

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

import (
"fmt"
"os"
)

func main() {
err := os.Mkdir("mydir", 0755)
if err != nil {
fmt.Println("Failed to create directory:", err)
return
}

fmt.Println("Directory created successfully.")

err = os.MkdirAll("mydir/nested/subdirectory", 0755)
if err != nil {
fmt.Println("Failed to create nested directory:", err)
return
}

fmt.Println("Nested directory created successfully.")
}

删除文件

os.Remove(), 示例:

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

import (
"fmt"
"os"
)

func main() {
err := os.Remove("example.txt")
if err != nil {
fmt.Println("Failed to delete file:", err)
return
}

fmt.Println("File deleted successfully.")
}

创建文件并写入文本

os.Create() 创建文件. 用 handle.WriteString() 写入, 示例:

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
package main

import (
"fmt"
"os"
)

func main() {
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("Failed to create file:", err)
return
}
defer file.Close()

content := "This is the content that will be written to the file."

_, err = file.WriteString(content)
if err != nil {
fmt.Println("Failed to write to file:", err)
return
}

fmt.Println("File created and written successfully.")
}

正则替换

regexp 中的 ReplaceAllString()ReplaceAllStringFunc(), 示例:

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"
"regexp"
)

func main() {
str := "Hello, World!"
re := regexp.MustCompile(`World`)
result := re.ReplaceAllString(str, "Gopher")
fmt.Println(result)
}```


# 正则匹配
使用标准库中的 `regexp`, 需要先编译正则表达式, 然后再使用.

编译用 `regexp.Compile()` 或 `regexp.MustCompile()`, 后者在编译失败时会引发 panic.

```go
pattern := regexp.MustCompile(`\d+`)

匹配用 pattern.MatchString(str)FindString(), FindAllString() :

1
matchedString := pattern.FindString(str)

示例如:

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

import (
"fmt"
"regexp"
)

func main() {
str := "Hello, 123 World!"
pattern := regexp.MustCompile(`\d+`)

matched := pattern.MatchString(str)
fmt.Println("Matched:", matched)

matchedString := pattern.FindString(str)
fmt.Println("Matched String:", matchedString)
}

忽略变量中的特殊字符

regexp.QuoteMeta() 进行转义, 如:

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

import (
"fmt"
"regexp"
)

func main() {
str := "Hello (World)!"
pattern := regexp.QuoteMeta("Hello (World)!")

reg := regexp.MustCompile(pattern)
matched := reg.MatchString(str)

fmt.Println("Matched:", matched)
}

遍历目录

filepath.Walk() 函数, 其函数签名为:

1
func Walk(root string, walkFn WalkFunc) error

root 指要遍历的目录.

WalkFunc 会对每个访问到的文件/目录做处理, 其定义需为:

1
type WalkFunc func(path string, info os.FileInfo, err error) error
  • path, 为文件/目录的绝对路径
  • info, 提供文件元信息, 常见的有 Name(), Size(), Mode(), ModTime(), IsDir(), Sys()
  • err, 包含错误信息

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
```


# 结构体的标签
注意: 在标签中, 冒号 ":" 后面应该没有空格

如:
```go
type Main struct {
Sort string `yaml:"sort"`
Help struct {
Hidden bool `yaml:"hidden"`
} `yaml:"help"`
} `yaml:"main"`

文件路径拼接

path/filepath 标准包.

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

import (
"fmt"
"path/filepath"
)

func main() {
dir := "/path/to/dir"
filename := "file.txt"

absolutePath := filepath.Join(dir, filename)

fmt.Println("Absolute Path:", absolutePath)
}

log 标准包

log.Fatal() 会默认将错误输出到 stderr, 然后调用 os.Exit(1) 终止程序.

安装包

如:

1
go get -u github.com/charmbracelet/glamour

.(type)

一种特殊语法, 只能用于 switch 或者 select 语句中, 其可以先获取接口的实际类型, 在选择了一个分支后, 将变量的接口类型转换为对应的实际类型:

1
2
3
4
5
6
7
8
switch msg := msg.(type) {
case string:
fmt.Println("这是一个字符串:", msg)
case int:
fmt.Println("这是一个整数:", msg)
default:
fmt.Println("未知类型")
}

读取/写入文件

读取

1
data, err := os.ReadFile("filename")

写入

1
err = os.WriteFile("filename", data, 0644)

(0644 中的 “0” 表示其为 8 进制数, “644” 为权限)


Go-技巧积累
http://example.com/2023/10/04/Go-技巧积累/
作者
Jie
发布于
2023年10月4日
许可协议