Golang
2021-04-26
type ExpiringMap struct {
m sync.Map
expiry time.Duration
}
func (e *ExpiringMap) set(key, value interface{}) {
e.m.Store(key, value)
go func() {
time.Sleep(e.expiry)
e.m.Delete(key)
}()
}
改进:定时清理
package main
import (
"sync"
"time"
)
type Item struct {
value interface{}
expiration int64
}
type ExpiringMap struct {
m sync.Mutex
items map[string]Item
timeout time.Duration
}
func NewExpiringMap(timeout time.Duration) *ExpiringMap {
em := &ExpiringMap{
items: make(map[string]Item),
timeout: timeout,
}
go em.cleanup()
return em
}
func (em *ExpiringMap) Set(key string, value interface{}) {
em.m.Lock()
defer em.m.Unlock()
em.items[key] = Item{
value: value,
expiration: time.Now().Add(em.timeout).UnixNano(),
}
}
func (em *ExpiringMap) Get(key string) (interface{}, bool) {
em.m.Lock()
defer em.m.Unlock()
item, found := em.items[key]
if !found {
return nil, false
}
if time.Now().UnixNano() > item.expiration {
return nil, false
}
return item.value, true
}
func (em *ExpiringMap) Delete(key string) {
em.m.Lock()
defer em.m.Unlock()
delete(em.items, key)
}
func (em *ExpiringMap) deleteExpiredKeys() {
em.m.Lock()
defer em.m.Unlock()
now := time.Now().UnixNano()
for key, item := range em.items {
if now < item.expiration {
break
}
delete(em.items, key)
}
}
func (em *ExpiringMap) cleanup() {
for {
time.Sleep(em.timeout) // 阻塞协程
em.deleteExpiredKeys()
}
}
func main() {
em := NewExpiringMap(time.Second * 10)
time.Sleep(time.Second * 15)
_, ok := em.Get("key1")
if !ok {
println("key1 is deleted")
}
_, ok = em.Get("key2")
if !ok {
println("key2 is deleted")
}
}
Golang fasthttp
2021-04-26
https://github.com/valyala/fasthttp
fasthttp
package main
import (
"flag"
"fmt"
"log"
"github.com/valyala/fasthttp"
)
var (
addr = flag.String("addr", ":8080", "TCP address to listen to")
compress = flag.Bool("compress", false, "Whether to enable transparent response compression")
)
func main() {
flag.Parse()
h := requestHandler
if *compress {
h = fasthttp.CompressHandler(h)
}
if err := fasthttp.ListenAndServe(*addr, h); err != nil {
log.Fatalf("Error in ListenAndServe: %v", err)
}
}
func requestHandler(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "Hello, world!\n\n")
fmt.Fprintf(ctx, "Request method is %q\n", ctx.Method())
fmt.Fprintf(ctx, "RequestURI is %q\n", ctx.RequestURI())
fmt.Fprintf(ctx, "Requested path is %q\n", ctx.Path())
fmt.Fprintf(ctx, "Host is %q\n", ctx.Host())
fmt.Fprintf(ctx, "Query string is %q\n", ctx.QueryArgs())
fmt.Fprintf(ctx, "User-Agent is %q\n", ctx.UserAgent())
fmt.Fprintf(ctx, "Connection has been established at %s\n", ctx.ConnTime())
fmt.Fprintf(ctx, "Request has been started at %s\n", ctx.Time())
fmt.Fprintf(ctx, "Serial request number for the current connection is %d\n", ctx.ConnRequestNum())
fmt.Fprintf(ctx, "Your ip is %q\n\n", ctx.RemoteIP())
fmt.Fprintf(ctx, "Raw request is:\n---CUT---\n%s\n---CUT---", &ctx.Request)
ctx.SetContentType("text/plain; charset=utf8")
// Set arbitrary headers
ctx.Response.Header.Set("X-My-Header", "my-header-value")
// Set cookies
var c fasthttp.Cookie
c.SetKey("cookie-name")
c.SetValue("cookie-value")
ctx.Response.Header.SetCookie(&c)
}
r.Body
-> ctx.PostBody()
r.URL.Path
-> ctx.Path()
r.URL
-> ctx.URI()
r.Method
-> ctx.Method()
r.Header
-> ctx.Request.Header
r.Header.Get()
-> ctx.Request.Header.Peek()
r.Host
-> ctx.Host()
r.Form
-> ctx.QueryArgs()
+ ctx.PostArgs()
r.PostForm
-> ctx.PostArgs()
r.FormValue()
-> ctx.FormValue()
r.FormFile()
-> ctx.FormFile()
r.MultipartForm
-> ctx.MultipartForm()
r.RemoteAddr
-> ctx.RemoteAddr()
r.RequestURI
-> ctx.RequestURI()
r.TLS
-> ctx.IsTLS()
r.Cookie()
-> ctx.Request.Header.Cookie()
r.Referer()
-> ctx.Referer()
r.UserAgent()
-> ctx.UserAgent()
w.Header()
-> ctx.Response.Header
w.Header().Set()
-> ctx.Response.Header.Set()
w.Header().Set("Content-Type")
-> ctx.SetContentType()
w.Header().Set("Set-Cookie")
-> ctx.Response.Header.SetCookie()
w.Write()
-> ctx.Write()
, ctx.SetBody()
, ctx.SetBodyStream()
, ctx.SetBodyStreamWriter()
w.WriteHeader()
-> ctx.SetStatusCode()
w.(http.Hijacker).Hijack()
-> ctx.Hijack()
http.Error()
-> ctx.Error()
http.FileServer()
-> fasthttp.FSHandler()
, fasthttp.FS
http.ServeFile()
-> fasthttp.ServeFile()
http.Redirect()
-> ctx.Redirect()
http.NotFound()
-> ctx.NotFound()
http.StripPrefix()
-> fasthttp.PathRewriteFunc
fasthttp/router
/user/{user}
named
/user/{user}_admin
/user/{user?}
optional
/user/{name:[a-zA-Z]{5}}
regex
/user/{name?:[a-zA-Z]{5}}
/user/{name:*}
cache all
fasthttp/session
package main
import (
"log"
"time"
"github.com/fasthttp/session/v2"
"github.com/fasthttp/session/v2/providers/redis"
)
var serverSession *session.Session
func init() {
var provider session.Provider
var err error
encoder := session.MSGPEncode
decoder := session.MSGPDecode
provider, err = redis.New(redis.Config{
KeyPrefix: "session",
Addr: "127.0.0.1:6379",
PoolSize: 8,
IdleTimeout: 30 * time.Second,
})
if err != nil {
log.Fatal(err)
}
cfg := session.NewDefaultConfig()
cfg.EncodeFunc = encoder
cfg.DecodeFunc = decoder
serverSession = session.New(cfg)
if err = serverSession.SetProvider(provider); err != nil {
log.Fatal(err)
}
}
// 获取 SessionStore
store, err := serverSession.Get(ctx)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
defer func() {
if err := serverSession.Save(ctx, store); err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
}()
// Set
store.Set("foo", "bar")
// Get
val := store.Get("foo")
if val == nil {
// ...
}
store.Delete(key)
store.Set(bytesKey)
val := store.GetBytes(bytesKey)
store.DeleteBytes(bytesKey)
data := store.GetAll() // Dict
data := store.Ptr() // *Dict
store.Flush()
store.Reset()
// Session ID,默认 sessionid
id := store.GetSessionID()
store.SetSessionID(bytesID)
// Session 有效期
changed := store.HasExpirationChanged()
duration := store.GetExpiration()
err := store.SetExpiration(duration)
savsgio/atreugo
构建与 fasthttp 和 fasthttp/router 之上的 HTTP 框架,做了一些简单的封装工作。
提供了几个示例。
基础用法:
package main
import (
"github.com/savsgio/atreugo/v11"
)
func main() {
config := atreugo.Config{
Addr: "0.0.0.0:8000",
}
server := atreugo.New(config)
server.GET("/", func(ctx *atreugo.RequestCtx) error {
return ctx.TextResponse("Hello World")
})
server.GET("/echo/{path:*}", func(ctx *atreugo.RequestCtx) error {
return ctx.TextResponse("Echo message: " + ctx.UserValue("path").(string))
})
v1 := server.NewGroupPath("/v1")
v1.GET("/", func(ctx *atreugo.RequestCtx) error {
return ctx.TextResponse("Hello V1 Group")
})
if err := server.ListenAndServe(); err != nil {
panic(err)
}
}
Golang
2021-04-01
类似代码:
for _, f := range files {
fp, err := os.Open(f)
if err != nil {
panic(err)
}
defer fp.Close()
// do something
}
Goland 提示:possible resource leak,'defer' is called in the 'for' loop
defer
是在最后函数退出时执行,但是变量已经经过循环覆盖,可能会导致内存泄漏。
似乎 Golang 的 defer 还是不够聪明。
换种写法:
for _, f := range files {
func () {
fp, err := os.Open(f)
if err != nil {
panic(err)
}
defer fp.Close()
// do something
}()
}
Golang
2021-03-30
- GORM
- ent
- Xorm 转到Gitea
- Bun
- Reform
- GoRose
GitHub 搜索结果
- go-gorm/gorm

The fantastic ORM library for Golang, aims to be developer friendly
- ent/ent

An entity framework for Go
- geektutu/7days-golang

7 days golang programs from scratch (web framework Gee, distributed cache GeeCache, object relational mapping ORM framework GeeORM, rpc framework GeeRPC etc) 7 天用 Go 动手写/从零实现系列
- gogf/gf

GoFrame is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
- sqlc-dev/sqlc

Generate type-safe code from SQL
- go-xorm/xorm

Simple and Powerful ORM for Go, support mysql,postgres,tidb,sqlite3,mssql,oracle, Moved to https://gitea.com/xorm/xorm
- volatiletech/sqlboiler

Generate a Go ORM tailored to your database schema.
- go-pg/pg

Golang ORM with focus on PostgreSQL features and performance
- go-gorp/gorp

Go Relational Persistence - an ORM-ish library for Go
- xo/xo

Command line tool to generate idiomatic Go code for SQL databases supporting PostgreSQL, MySQL, SQLite, Oracle, and Microsoft SQL Server
- upper/db

Data access layer for PostgreSQL, CockroachDB, MySQL, SQLite and MongoDB with ORM-like features.
- uptrace/bun

SQL-first Golang ORM
- xxjwxc/gormt

database to golang struct
- steebchen/prisma-client-go

Prisma Client Go is an auto-generated and fully type-safe database client
- xormplus/xorm

xorm 是一个简单而强大的 Go 语言 ORM 库,通过它可以使数据库操作非常简便。本库是基于原版 xorm 的定制增强版本,为 xorm 提供类似 ibatis 的配置文件及动态 SQL 支持,支持 AcitveRecord 操作
- go-reform/reform

A better ORM for Go, based on non-empty interfaces and code generation.
- gobuffalo/pop

A Tasty Treat For All Your Database Needs
- bobohume/gonet

go 分布式服务器,基于内存 mmo
- unionj-cloud/go-doudou

go-doudou(doudou pronounce /dəudəu/)is OpenAPI 3.0 (for REST) spec and Protobuf v3 (for grpc) based lightweight microservice framework. It supports monolith service application as well.
- gohouse/gorose

GoRose(go orm), a mini database ORM for golang, which inspired by the famous php framwork laravle's eloquent. It will be friendly for php developer and python or ruby developer. Currently provides six major database drivers: mysql,sqlite3,postgres,oracle,mssql, Clickhouse.
- huandu/go-sqlbuilder

A flexible and powerful SQL string builder library plus a zero-config ORM.
Golang
2021-03-29
httpbin 是我练手的一个非常简单的小项目,功能就是:
1. HTTP POST 请求 (POST /
) 提交一个字符串,服务器返回一个 ID。
1. HTTP GET 请求 (GET /xxxx
),返回 ID 对应的字符串。
Golang GoStdLib
2021-03-01
- archive: 打包 (tar, zip)
参考:《Golang archive
包》
- compress: 压缩 (bzip2, flate, gzip, lzw, zlib)
压缩 & 解压缩
import (
"bytes"
"compress/gzip"
"io"
)
func compressData(data string) ([]byte, error) {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
_, err := gz.Write([]byte(data))
if err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func decompressData(compressedData []byte) (string, error) {
r, err := gzip.NewReader(bytes.NewReader(compressedData))
if err != nil {
return "", err
}
defer r.Close()
decompressedData, err := io.ReadAll(r)
if err != nil {
return "", err
}
return string(decompressedData), nil
}
压缩 & 解压缩 & 列出压缩包中的内容
import (
"archive/zip"
)
// 压缩文件
func compressFile(zipFilename string, filenames ...string) error {
zipFile, err := os.Create(zipFilename)
if err != nil {
return err
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
for _, filename := range filenames {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
fi, err := file.Stat()
if err != nil {
return err
}
fileHeader, err := zip.FileInfoHeader(fi)
if err != nil {
return err
}
// 设置文件名(压缩后的文件名)
fileHeader.Name = filename
// 写入文件头部
writer, err := zipWriter.CreateHeader(fileHeader)
if err != nil {
return err
}
// 复制文件内容到压缩包中
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
}
// 解压缩文件
func decompressFile(zipFilename, targetDir string) error {
zipReader, err := zip.OpenReader(zipFilename)
if err != nil {
return err
}
defer zipReader.Close()
for _, file := range zipReader.File {
// 打开压缩包中的文件
fileReader, err := file.Open()
if err != nil {
return err
}
defer fileReader.Close()
// 创建目标文件
extractedFilePath := fmt.Sprintf("%s/%s", targetDir, file.Name)
extractedFile, err := os.Create(extractedFilePath)
if err != nil {
return err
}
defer extractedFile.Close()
// 复制文件内容到目标文件
_, err = io.Copy(extractedFile, fileReader)
if err != nil {
return err
}
}
return nil
}
func listFilesInZip(zipFilename string) ([]string, error) {
files := []string{}
zipReader, err := zip.OpenReader(zipFilename)
if err != nil {
return files, err
}
defer zipReader.Close()
for _, file := range zipReader.File {
files = append(files, file.Name)
}
return files, nil
}
Golang GoStdLib
2021-03-01
标准库中的 archive
支持 tar
,zip
两种打包格式。
https://en.wikipedia.org/wiki/Tar_(computing)
最常用的 tar 包:
func createTarArchive(sourceDir, targetFile string) error {
file, err := os.Create(targetFile)
if err != nil {
return err
}
defer file.Close()
tarWriter := tar.NewWriter(file)
defer tarWriter.Close()
return filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
if err := tarWriter.WriteHeader(header); err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tarWriter, file)
if err != nil {
return err
}
}
return nil
})
}
func extractTarArchive(sourceFile, targetDir string) error {
file, err := os.Open(sourceFile)
if err != nil {
return err
}
defer file.Close()
tarReader := tar.NewReader(file)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
targetPath := filepath.Join(targetDir, header.Name)
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
return err
}
if header.Typeflag == tar.TypeReg {
file, err := os.Create(targetPath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
}
}
return nil
}
Golang
2021-02-24
数组声明时可以使用 ...
当作长度,表示自动判断。
数组声明时,可以只初始化其中的部分值。
a := [...]int{2: 1, 4: 2} // [5]int{0, 0, 1, 0, 4}
b := [5]int{1, 2, 3} // [5]int{1, 2, 3, 0, 0}
基本操作
a := [...]int{1, 2, 3}
var b []int
// 新增
b = append(b, 1)
// 删除元素
s = append(s[:index], s[index+1:]...)
// 遍历
for i := range a {
fmt.Println(a[i])
}
for index, value := range a {
fmt.Printf("%v: %v", index, value)
}
for _, value := range a {
fmt.Printf("%v", value)
}
for i := 0; i < len(array); i++ {
fmt.Printf("value: %d\n", array[i])
}
// 判断元素是否存在/获取元素序号
func Index(target int, array []int) int {
for i, v := range array {
if target == v {
return i
}
}
return -1
}
示例
package main
import (
"fmt"
"reflect"
)
func main() {
a := [5]int{1, 3, 5, 7, 9}
fmt.Printf("%s\t%#v\n", reflect.TypeOf(a), a)
// [5]int [5]int{1, 3, 5, 7, 9}
// array => slice
b := a[:] // a[0:len(a)]
fmt.Printf("%s\t%#v\n", reflect.TypeOf(b), b)
// []int []int{1, 3, 5, 7, 9}
// slice => array
// c := ([5]int)(b) // cannot convert b (type []int) to type [5]int
c := (*[5]int)(b) // 切片只能转换成数组指针
fmt.Printf("%s\t%#v\n", reflect.TypeOf(c), c)
// *[5]int &[5]int{1, 3, 5, 7, 9}
// 用类型别名试试:
type NumArr [5]int
c2 := (*NumArr)(b)
fmt.Printf("%s\t%#v\n", reflect.TypeOf(c2), c2)
// *main.NumArr &main.NumArr{1, 3, 5, 7, 9}
// 只能遍历赋值
d := [5]int{}
for index, v := range b {
d[index] = v
}
fmt.Printf("%s\t%#v\n", reflect.TypeOf(d), d)
// 通过 copy 的方法实现 slice2array
e := [5]int{}
copy(e[:], b) // return length of b
fmt.Printf("%s\t%#v\n", reflect.TypeOf(e), e)
}
去重
func Unique(arr []int) []int {
arrLen := len(arr) - 1
for arrLen > 0 {
for i := arrLen - 1; i >= 0; i-- {
if arr[arrLen] == arr[i] {
arr = append(arr[:i], arr[i+1:]...)
break
}
}
arrLen--
}
return arr
}
func UniqueOptimized(arr []int) []int {
uniqueArr := make([]int, 0, len(arr))
uniqueMap := make(map[int]struct{})
for _, num := range arr {
if _, ok := uniqueMap[num]; !ok {
uniqueArr = append(uniqueArr, num)
uniqueMap[num] = struct{}{}
}
}
return uniqueArr
}
// BenchmarkUniqueOriginal-20 135 8658964 ns/op 0 B/op 0 allocs/op
// BenchmarkUniqueOptimized-20 3501 347027 ns/op 285402 B/op 208 allocs/op
可以看到,一个性能好一些(只用 4% 的时间),资源使用多一些。
使用反射
https://blog.csdn.net/youngwhz1/article/details/83026263
func SliceRemoveDuplicate(a interface{}) (ret []interface{}) {
if reflect.TypeOf(a).Kind() != reflect.Slice {
fmt.Printf("<SliceRemoveDuplicate> <a> is not slice but %T\n", a)
return ret
}
va := reflect.ValueOf(a)
for i := 0; i < va.Len(); i++ {
if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {
continue
}
ret = append(ret, va.Index(i).Interface())
}
return ret
}
func SliceInsert(s []interface{}, index int, value interface{}) []interface{} {
rear := append([]interface{}{}, s[index:]...)
return append(append(s[:index], value), rear...)
}
func SliceInsert2(s *[]interface{}, index int, value interface{}) {
rear := append([]interface{}{}, (*s)[index:]...)
*s = append(append((*s)[:index], value), rear...)
}
func SliceInsert3(s interface{}, index int, value interface{}) bool {
if ps, ok := s.(*[]string); ok {
if val, ok := value.(string); ok {
rear := append([]string{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
return true
}
} else if ps, ok := s.(*[]int); ok {
if val, ok := value.(int); ok {
rear := append([]int{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
}
} else if ps, ok := s.(*[]float64); ok {
if val, ok := value.(float64); ok {
rear := append([]float64{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
}
} else {
fmt.Printf("<SliceInsert3> Unsupported type: %T\n", s)
}
return false
}
func SliceRemove(s []interface{}, index int) []interface{} {
return append(s[:index], s[index+1:]...)
}
func SliceRemove2(s *[]interface{}, index int) {
*s = append((*s)[:index], (*s)[index+1:]...)
}
func SliceRemove3(s interface{}, index int) bool {
if ps, ok := s.(*[]string); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else if ps, ok := s.(*[]int); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else if ps, ok := s.(*[]float64); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else {
fmt.Printf("<SliceRemove3> Unsupported type: %T\n", s)
return false
}
return true
}
func SliceClear(s *[]interface{}) {
*s = append([]interface{}{})
}
func SliceClear2(s *[]interface{}) {
*s = (*s)[0:0]
}
func SliceClear3(s interface{}) bool {
if ps, ok := s.(*[]string); ok {
*ps = (*ps)[0:0]
//*ps = append([]string{})
} else if ps, ok := s.(*[]int); ok {
*ps = (*ps)[0:0]
//*ps = append([]int{})
} else if ps, ok := s.(*[]float64); ok {
*ps = (*ps)[0:0]
//*ps = append([]float64{})
} else {
fmt.Printf("<SliceClear3> Unsupported type: %T\n", s)
return false
}
return true
}
泛型实现
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
tot := len(s) + len(v)
if tot <= cap(s) {
s2 := s[:tot]
copy(s2[i+len(v):], s[i:])
copy(s2[i:], v)
return s2
}
s2 := make(S, tot)
copy(s2, s[:i])
copy(s2[i:], v)
copy(s2[i+len(v):], s[i:])
return s2
}
内置方法 make
make(Type, Len, Cap)
可以用来为 slice
, map
, channel
三种类型初始化(类似:C 语言 (int *)malloc(5)
)。
这里就看 Slice 的情况。
s1 := make([]int, 5)
s2 := make([]int, 5, 10)
// s2[8] = 1 // panic: runtime error: index out of range [8] with length 5
s2 = append(s2, 1)
如果有预留空间,append
的时候可以不用重新分配内存并遍历赋值。
如果切片有扩容的需要,就最好采用 make
来初始化。
如果不指定容量,则切片的容量和长度将相等。
Golang
2021-02-22
- 创建临时文件
- 使用编辑器编辑
- 获取内容
- 删除临时文件
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
)
func main() {
// create a tmp file
// ioutil.TempFile creates a temp file and opens the file for reading and writing
// and returns the resulting *os.File (file descriptor).
tmpFile, err := ioutil.TempFile(os.TempDir(), "todo-tmp-")
if err != nil {
log.Fatal("Cannot create temporary file", err)
}
// defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
fmt.Println("Created File: " + tmpFile.Name())
// choose editor by env var
editor := os.Getenv("EDITOR")
args := []string{}
if editor == "" {
editor = "vim"
args = append(args, "--clean")
fmt.Println("No EDITOR enviroment variable set, use " + editor)
} else {
fmt.Println("Using EDITOR enviroment variable: " + editor)
}
args = append(args, tmpFile.Name())
fmt.Println("Opening file with " + editor + " with args: " + fmt.Sprint(args))
// check the editor command is available
// _, err = exec.LookPath(editor)
// if err != nil {
// log.Fatal("Cannot find editor: " + editor)
// }
// call the editor
cmd := exec.Command(editor, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal("Cannot run editor: " + editor)
}
// read the file
// data, err := ioutil.ReadFile(tmpFile.Name())
// if err != nil {
// log.Fatal("Cannot read file: " + tmpFile.Name())
// }
tmpFile.Seek(0, 0)
s := bufio.NewScanner(tmpFile)
for s.Scan() {
fmt.Println("Content:\n\n" + s.Text())
}
if err = s.Err(); err != nil {
log.Fatal("error reading temp file", err)
}
}
Golang GoStdLib
2021-02-21
func Clone(s string) string //
func Compare(a, b string) int // 比较
func Contains(s, substr string) bool // 包含子字符串
func ContainsAny(s, chars string) bool // 包含任意Char
func ContainsRune(s string, r rune) bool // 包含Rune
func Count(s, substr string) int // 计数
func Cut(s, sep string) (before, after string, found bool)
func EqualFold(s, t string) bool //
func Fields(s string) []string // 切割
func FieldsFunc(s string, f func(rune) bool) []string
func HasPrefix(s, prefix string) bool // 前缀判断 startswith
func HasSuffix(s, suffix string) bool // 后缀判断 endswith
func Index(s, substr string) int // 查找
func IndexAny(s, chars string) int // 查找Any
func IndexByte(s string, c byte) int // 查找Byte
func IndexFunc(s string, f func(rune) bool) int // 查找Func
func IndexRune(s string, r rune) int // 查找Rune
func Join(elems []string, sep string) string // 连接
func LastIndex(s, substr string) int // 右查找
func LastIndexAny(s, chars string) int // 右查找Any
func LastIndexByte(s string, c byte) int // 右查找Byte
func LastIndexFunc(s string, f func(rune) bool) int // 右查找Func
func Map(mapping func(rune) rune, s string) string //
func Repeat(s string, count int) string // 重复
func Replace(s, old, new string, n int) string // 替换
func ReplaceAll(s, old, new string) string // 替换 = Replace -1
func Split(s, sep string) []string // 切割
func SplitAfter(s, sep string) []string //
func SplitAfterN(s, sep string, n int) []string //
func SplitN(s, sep string, n int) []string //
func Title(s string) string //
func ToLower(s string) string // 转小写
func ToLowerSpecial(c unicode.SpecialCase, s string) string
func ToTitle(s string) string // Title
func ToTitleSpecial(c unicode.SpecialCase, s string) string
func ToUpper(s string) string
func ToUpperSpecial(c unicode.SpecialCase, s string) string
func ToValidUTF8(s, replacement string) string
func Trim(s, cutset string) string
func TrimFunc(s string, f func(rune) bool) string
func TrimLeft(s, cutset string) string
func TrimLeftFunc(s string, f func(rune) bool) string
func TrimPrefix(s, prefix string) string
func TrimRight(s, cutset string) string
func TrimRightFunc(s string, f func(rune) bool) string
func TrimSpace(s string) string
func TrimSuffix(s, suffix string) string
type Builder
func (b *Builder) Cap() int
func (b *Builder) Grow(n int)
func (b *Builder) Len() int
func (b *Builder) Reset()
func (b *Builder) String() string
func (b *Builder) Write(p []byte) (int, error)
func (b *Builder) WriteByte(c byte) error
func (b *Builder) WriteRune(r rune) (int, error)
func (b *Builder) WriteString(s string) (int, error)
type Reader
func NewReader(s string) *Reader
func (r *Reader) Len() int
func (r *Reader) Read(b []byte) (n int, err error)
func (r *Reader) ReadAt(b []byte, off int64) (n int, err error)
func (r *Reader) ReadByte() (byte, error)
func (r *Reader) ReadRune() (ch rune, size int, err error)
func (r *Reader) Reset(s string)
func (r *Reader) Seek(offset int64, whence int) (int64, error)
func (r *Reader) Size() int64
func (r *Reader) UnreadByte() error
func (r *Reader) UnreadRune() error
func (r *Reader) WriteTo(w io.Writer) (n int64, err error)
type Replacer
func NewReplacer(oldnew ...string) *Replacer
func (r *Replacer) Replace(s string) string
func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error)
Builder
Reader
Replacer
参考资料与拓展阅读