#36 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 来初始化。

如果不指定容量,则切片的容量和长度将相等。

#35 Golang: 临时文件

2021-02-22
  1. 创建临时文件
  2. 使用编辑器编辑
  3. 获取内容
  4. 删除临时文件
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)
    }
}

#34 Golang strings

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

参考资料与拓展阅读

#33 Golang reflect

2021-02-17
func Copy(dst, src Value) int
func DeepEqual(x, y any) bool
func Swapper(slice any) func(i, j int)
type ChanDir
    func (d ChanDir) String() string
type Kind
    func (k Kind) String() string
type MapIter
    func (iter *MapIter) Key() Value
    func (iter *MapIter) Next() bool
    func (iter *MapIter) Reset(v Value)
    func (iter *MapIter) Value() Value
type Method
    func (m Method) IsExported() bool
type SelectCase
type SelectDir
type SliceHeader
type StringHeader
type StructField
    func VisibleFields(t Type) []StructField
    func (f StructField) IsExported() bool
type StructTag
    func (tag StructTag) Get(key string) string
    func (tag StructTag) Lookup(key string) (value string, ok bool)

Type

  • func ArrayOf(length int, elem Type) Type
  • func ChanOf(dir ChanDir, t Type) Type
  • func FuncOf(in, out []Type, variadic bool) Type
  • func MapOf(key, elem Type) Type
  • func PointerTo(t Type) Type
  • func PtrTo(t Type) Type
  • func SliceOf(t Type) Type
  • func StructOf(fields []StructField) Type
  • func TypeOf(i any) Type

Value

  • func Append(s Value, x ...Value) Value
  • func AppendSlice(s, t Value) Value
  • func Indirect(v Value) Value
  • func MakeChan(typ Type, buffer int) Value
  • func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
  • func MakeMap(typ Type) Value
  • func MakeMapWithSize(typ Type, n int) Value
  • func MakeSlice(typ Type, len, cap int) Value
  • func New(typ Type) Value
  • func NewAt(typ Type, p unsafe.Pointer) Value
  • func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
  • func ValueOf(i any) Value
  • func Zero(typ Type) Value
  • func (v Value) Addr() Value
  • func (v Value) Bool() bool
  • func (v Value) Bytes() []byte
  • func (v Value) Call(in []Value) []Value
  • func (v Value) CallSlice(in []Value) []Value
  • func (v Value) CanAddr() bool
  • func (v Value) CanComplex() bool
  • func (v Value) CanConvert(t Type) bool
  • func (v Value) CanFloat() bool
  • func (v Value) CanInt() bool
  • func (v Value) CanInterface() bool
  • func (v Value) CanSet() bool
  • func (v Value) CanUint() bool
  • func (v Value) Cap() int
  • func (v Value) Close()
  • func (v Value) Comparable() bool
  • func (v Value) Complex() complex128
  • func (v Value) Convert(t Type) Value
  • func (v Value) Elem() Value
  • func (v Value) Equal(u Value) bool
  • func (v Value) Field(i int) Value
  • func (v Value) FieldByIndex(index []int) Value
  • func (v Value) FieldByIndexErr(index []int) (Value, error)
  • func (v Value) FieldByName(name string) Value
  • func (v Value) FieldByNameFunc(match func(string) bool) Value
  • func (v Value) Float() float64
  • func (v Value) Grow(n int)
  • func (v Value) Index(i int) Value
  • func (v Value) Int() int64
  • func (v Value) Interface() (i any)
  • func (v Value) InterfaceData() [2]uintptrDEPRECATED
  • func (v Value) IsNil() bool
  • func (v Value) IsValid() bool
  • func (v Value) IsZero() bool
  • func (v Value) Kind() Kind
  • func (v Value) Len() int
  • func (v Value) MapIndex(key Value) Value
  • func (v Value) MapKeys() []Value
  • func (v Value) MapRange() *MapIter
  • func (v Value) Method(i int) Value
  • func (v Value) MethodByName(name string) Value
  • func (v Value) NumField() int
  • func (v Value) NumMethod() int
  • func (v Value) OverflowComplex(x complex128) bool
  • func (v Value) OverflowFloat(x float64) bool
  • func (v Value) OverflowInt(x int64) bool
  • func (v Value) OverflowUint(x uint64) bool
  • func (v Value) Pointer() uintptr
  • func (v Value) Recv() (x Value, ok bool)
  • func (v Value) Send(x Value)
  • func (v Value) Set(x Value)
  • func (v Value) SetBool(x bool)
  • func (v Value) SetBytes(x []byte)
  • func (v Value) SetCap(n int)
  • func (v Value) SetComplex(x complex128)
  • func (v Value) SetFloat(x float64)
  • func (v Value) SetInt(x int64)
  • func (v Value) SetIterKey(iter *MapIter)
  • func (v Value) SetIterValue(iter *MapIter)
  • func (v Value) SetLen(n int)
  • func (v Value) SetMapIndex(key, elem Value)
  • func (v Value) SetPointer(x unsafe.Pointer)
  • func (v Value) SetString(x string)
  • func (v Value) SetUint(x uint64)
  • func (v Value) SetZero()
  • func (v Value) Slice(i, j int) Value
  • func (v Value) Slice3(i, j, k int) Value
  • func (v Value) String() string
  • func (v Value) TryRecv() (x Value, ok bool)
  • func (v Value) TrySend(x Value) bool
  • func (v Value) Type() Type
  • func (v Value) Uint() uint64
  • func (v Value) UnsafeAddr() uintptr
  • func (v Value) UnsafePointer() unsafe.Pointer

#32 Golang io

2021-02-14

io, io/fs, io/util, os

find /usr/share/go-1.17/src/io/ -type f | grep -Fv _test.go
/usr/share/go-1.17/src/io/ioutil/testdata/hello
/usr/share/go-1.17/src/io/ioutil/tempfile.go
/usr/share/go-1.17/src/io/ioutil/ioutil.go
/usr/share/go-1.17/src/io/pipe.go
/usr/share/go-1.17/src/io/fs/fs.go
/usr/share/go-1.17/src/io/fs/readfile.go
/usr/share/go-1.17/src/io/fs/readdir.go
/usr/share/go-1.17/src/io/fs/sub.go
/usr/share/go-1.17/src/io/fs/stat.go
/usr/share/go-1.17/src/io/fs/walk.go
/usr/share/go-1.17/src/io/fs/glob.go
/usr/share/go-1.17/src/io/io.go
/usr/share/go-1.17/src/io/multi.go

方法

https://pkg.go.dev/io

相关方法

io.Reader / io.Writer

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

stringsbytesbufioos.File 都是 io.Reader 的实现。

小技巧:利用类型断言检查是否实现指定接口

https://stackoverflow.com/a/25677274

var _ io.Reader = (*os.File)(nil)
// nil 可以转换成任意类型的空指针
// 这里将 nil 转换成 os.File 指针,再赋值给 io.Reader 类型

io/fs

func Glob(fsys FS, pattern string) (matches []string, err error)
func ReadFile(fsys FS, name string) ([]byte, error)
func ValidPath(name string) bool
func WalkDir(fsys FS, root string, fn WalkDirFunc) error

io/util 包 (原 ioutil)

1.6 以及之后新版本,iouitl 包依然保留,但是只是对 io 包的封装。

Package ioutil implements some I/O utility functions.
As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details.

  1. func NopCloser(r io.Reader) io.ReadCloser
  2. func ReadAll(r io.Reader) ([]byte, error)
  3. func ReadDir(dirname string) ([]fs.FileInfo, error)
  4. func ReadFile(filename string) ([]byte, error)
  5. func TempDir(dir, pattern string) (name string, err error)
  6. func TempFile(dir, pattern string) (f *os.File, err error)
  7. func WriteFile(filename string, data []byte, perm fs.FileMode) error
// grep -REv "^//" /usr/share/go-1.17/src/io/ioutil/tempfile.go
package ioutil

import (
        "os"
)

func TempFile(dir, pattern string) (f *os.File, err error) {
        return os.CreateTemp(dir, pattern)
}

func TempDir(dir, pattern string) (name string, err error) {
        return os.MkdirTemp(dir, pattern)
}
// grep -REv "^//" /usr/share/go-1.17/src/io/ioutil/ioutil.go
package ioutil

import (
        "io"
        "io/fs"
        "os"
        "sort"
)

func ReadAll(r io.Reader) ([]byte, error) {
        return io.ReadAll(r)
}

func ReadFile(filename string) ([]byte, error) {
        return os.ReadFile(filename)
}

func WriteFile(filename string, data []byte, perm fs.FileMode) error {
        return os.WriteFile(filename, data, perm)
}

func ReadDir(dirname string) ([]fs.FileInfo, error) {
        f, err := os.Open(dirname)
        if err != nil {
                return nil, err
        }
        list, err := f.Readdir(-1)
        f.Close()
        if err != nil {
                return nil, err
        }
        sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
        return list, nil
}

func NopCloser(r io.Reader) io.ReadCloser {
        return io.NopCloser(r)
}

var Discard io.Writer = io.Discard

引申

io 包是基础,提供了 Reader 和 Writer 接口。其他包则是实现这两个接口。

string 包:字符串 IO

相当于 Python io.StringIO

  • string.Reader

bytes 包:字节 IO

通过 []byte 模拟 io,相当于 Python io.BytesIO

  • bytes.Buffer (Reader + Writer)
  • bytes.Reader

buffio 包:缓冲 IO

就是加入了一个缓冲的功能,提升 IO 效率,减少直接系统调用。
也方便实现一些网络协议中的分包,比如 SMTP 协议中按换行切割。

  • bufio.Reader
  • bufio.Writer
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("无法打开文件:", err)
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            // 文件结束或发生错误
            break
        }
        fmt.Print(line)
    }
}

os 包:系统 IO

  • os.File 文件类型
func Open(name string) (*File, error)
func OpenFile(name string, flag int, perm FileMode) (*File, error)

var Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
var Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
var Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")

net 包:网络 IO

网络编程的部分,这里就不继续展开了。

  • net.Conn 网络连接
func Pipe() (Conn, Conn)

func Dial(network, address string) (Conn, error)
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
func FileConn(f *os.File) (c Conn, err error)

func (d *Dialer) Dial(network, address string) (Conn, error)
func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error)

func FilePacketConn(f *os.File) (c PacketConn, err error)
func ListenPacket(network, address string) (PacketConn, error)

func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error)
func ListenIP(network string, laddr *IPAddr) (*IPConn, error)

func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
func (l *TCPListener) Accept() (Conn, error)
func (l *TCPListener) AcceptTCP() (*TCPConn, error)

func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error)
func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)

func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error)
func ListenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error)
func (l *UnixListener) Accept() (Conn, error)
func (l *UnixListener) AcceptUnix() (*UnixConn, error)

#31 Golang SHA 函数

2021-02-12

哈希算法(hash)是一种将任意长度数据映射成固定长度数据的方法。有时也叫摘要算法。

有非常多不同的哈希算法,其中最常见的是 md5 和 sha (sha1/sha256/sha512)两种。Golang md5 在 2021/01/14, Go MD5 中已经写过了。这里就记录一下 Golang sha 的使用方法。

// crypto.sha1
func New() hash.Hash
func Sum(data []byte) [Size]byte {
    if boringEnabled {
        return boringSHA1(data)
    }
    var d digest
    d.Reset()
    d.Write(data)
    return d.checkSum()
}
func (d *digest) MarshalBinary() ([]byte, error)
func (d *digest) UnmarshalBinary(b []byte) error
func (d *digest) Reset()
func (d *digest) Size() int
func (d *digest) BlockSize() int
func (d *digest) Write(p []byte) (nn int, err error)
func (d *digest) Sum(in []byte) []byte
func (d *digest) checkSum() [Size]byte
func (d *digest) ConstantTimeSum(in []byte) []byte
func (d *digest) constSum() [Size]byte

// crypto.sha256
func New() hash.Hash
func New224() hash.Hash // sha224
func Sum224(data []byte) [Size224]byte
func Sum256(data []byte) [Size]byte

// crypto.sha512
func New() hash.Hash
func New384() hash.Hash
func New512_224() hash.Hash
func New512_256() hash.Hash
func Sum384(data []byte) [Size384]byte
func Sum512(data []byte) [Size]byte
func Sum512_224(data []byte) [Size224]byte
func Sum512_256(data []byte) [Size256]byte

用法都一样:

h := sha1.New()  // hash.Hash
io.WriteString(h, "His money is twice tainted:")
io.WriteString(h, " 'taint yours and 'taint mine.")
fmt.Printf("% x", h.Sum(nil))
h := sha1.New()
if _, err := io.Copy(h, f); err != nil {
    log.Fatal(err)
}
fmt.Printf("% x", h.Sum(nil))

hash.Hash 接口

type Hash interface {
    io.Writer
    Sum(b []byte) []byte
    Reset()
    Size() int
    BlockSize() int
}

函数

  • sha1.New() -> hash.Hash
  • sha1.Sum(data []byte) -> [Size]byte

  • sha256.New() -> hash.Hash

  • sha256.Sum256(data []byte) -> [Size]byte
  • sha256.New224() -> hash.Hash
  • sha256.Sum224(data []byte) -> [Size224]byte

  • sha512.New() -> hash.Hash

  • sha512.Sum512(data []byte) -> [Size]byte
  • sha512.New384() -> hash.Hash
  • sha512.Sum384(data []byte) -> [Size384]byte
  • sha512.New512_224() -> hash.Hash
  • sha512.Sum512_224(data []byte) -> [Size224]byte
  • sha512.New512_256() -> hash.Hash
  • sha512.Sum512_256(data []byte) -> [Size256]byte
package main

import (
    "crypto/md5"
    "crypto/sha1"
    "crypto/sha256"
    "crypto/sha512"
    "fmt"
    "hash/fnv"
    "io"
    "log"
    "os"
)

func main() {
    filepath := "go.mod"
    f, err := os.Open(filepath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    fi, err := f.Stat()
    fmt.Printf("%#v err:%#v\n", fi, err)
    data := make([]byte, fi.Size())
    n, err := f.Read(data)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d, %#v, err:%#v\n", n, data, err)

    {
        hash := fnv.New32a()
        hash.Write(data)
        result := hash.Sum32()
        fmt.Printf("FNV-1a 32-bit hash: %d\n\n", result)
    }

    {
        fmt.Printf("MD5    : %x\n", md5.Sum(data))
        fmt.Printf("Sha1   : %x\n", sha1.Sum(data))
        fmt.Printf("Sha256 : %x\n", sha256.Sum256(data))
        fmt.Printf("Sha512 : %x\n", sha512.Sum512(data))
    }

    {
        f.Seek(0, 0)
        hasher := md5.New()
        _, err = io.Copy(hasher, f)
        if err != nil {
            log.Fatal(err)
        }
        sum := hasher.Sum(nil)
        fmt.Printf("MD5 2  : %x\n", sum)
    }
}

#30 Golang 基础

2021-02-09

工作这么多年之后,学习一门新的语言,对于语法方面的东西应该是只用看看就行了(重点在其生态的学习)。
所以,这只是对 Go 语法做一个简单的梳理。

#28 Golang container

2021-02-09

堆 Heap (container/heap)

  • 最小堆
  • 完全二叉树
  • 父节点的值小于其子节点的值
type Interface interface {
    sort.Interface
    Push(x any) // add x as element Len()
    Pop() any   // remove and return element Len() - 1.
}

func Fix(h Interface, i int)
func Init(h Interface)
func Pop(h Interface) any
func Push(h Interface, x any)
func Remove(h Interface, i int) any
  1. 接口:
  2. heap.Interface:定义了操作堆的基本方法,包括获取堆中元素数量、比较元素优先级、交换元素位置、添加元素到堆中、从堆中取出元素等。
  3. 函数:
  4. heap.Init(h Interface):对一个实现了 heap.Interface 的堆进行初始化操作。
  5. heap.Push(h Interface, x interface{}):将元素 x 添加到堆 h 中。
  6. heap.Pop(h Interface) interface{}:从堆 h 中取出具有最高优先级的元素并返回。
  7. heap.Remove(h Interface, i int) interface{}:从堆 h 中移除指定索引 i 处的元素并返回。

使用方法:

  1. 定义一个用户自定义的类型,表示要存储在堆中的元素,并为该类型实现 heap.Interface 接口的方法。
  2. 使用 heap.Init() 对堆进行初始化,或者使用 make() 创建一个空的堆。
  3. 使用 heap.Push() 将元素添加到堆中。
  4. 使用 heap.Pop() 从堆中取出具有最高优先级的元素。
  5. 可选地,使用 heap.Remove() 移除堆中的特定元素。

container/heap 包提供了一种方便而高效的方式来管理具有优先级的元素。它在各种场景中都有应用,例如任务调度、事件处理、优先级队列等。通过实现自定义的 heap.Interface 接口方法,可以根据具体需求定义元素的优先级比较方式,使堆适应不同的应用场景。

List (container/list)

type Element
    func (e *Element) Next() *Element
    func (e *Element) Prev() *Element
type List
    func New() *List
    func (l *List) Back() *Element
    func (l *List) Front() *Element
    func (l *List) Init() *List
    func (l *List) InsertAfter(v any, mark *Element) *Element
    func (l *List) InsertBefore(v any, mark *Element) *Element
    func (l *List) Len() int
    func (l *List) MoveAfter(e, mark *Element)
    func (l *List) MoveBefore(e, mark *Element)
    func (l *List) MoveToBack(e *Element)
    func (l *List) MoveToFront(e *Element)
    func (l *List) PushBack(v any) *Element
    func (l *List) PushBackList(other *List)
    func (l *List) PushFront(v any) *Element
    func (l *List) PushFrontList(other *List)
    func (l *List) Remove(e *Element) any

container/list 包是 Go 语言标准库中的一个包,提供了双向链表(doubly linked list)的实现。双向链表是一种数据结构,其中每个节点都包含对前一个节点和后一个节点的引用。

container/list 包提供了 List 类型,该类型代表了一个双向链表。List 类型的对象可以用来存储和操作任意类型的元素。它支持在链表的前部和后部添加和删除元素,以及在链表中搜索、遍历和修改元素。

container/list 包的主要功能和方法包括:

  1. List 类型的方法:
  2. PushFront(v interface{}) *Element:在链表的前部插入一个值为 v 的元素,并返回该元素的指针。
  3. PushBack(v interface{}) *Element:在链表的后部插入一个值为 v 的元素,并返回该元素的指针。
  4. Remove(e *Element) interface{}:从链表中移除指定元素 e,并返回该元素的值。
  5. Front() *Element:返回链表的第一个元素。
  6. Back() *Element:返回链表的最后一个元素。
  7. Len() int:返回链表中元素的数量。
  8. Element 类型的方法:
  9. Next() *Element:返回当前元素的下一个元素。
  10. Prev() *Element:返回当前元素的前一个元素。
  11. Value() interface{}:返回当前元素的值。

使用 container/list 包的步骤通常是:

  1. 导入 container/list 包。
  2. 创建一个新的链表对象:myList := list.New()
  3. 使用 PushFront()PushBack() 方法向链表中添加元素。
  4. 使用 Front()Back() 方法获取链表的第一个或最后一个元素。
  5. 使用 Next()Prev() 方法遍历链表中的元素。
  6. 使用 Remove() 方法从链表中移除特定的元素。

container/list 包提供了一种简单而灵活的方式来管理和操作链表。它适用于需要频繁进行元素插入、删除和遍历的场景。然而,由于链表是一种指针结构,因此在随机访问和索引元素方面的性能较差。如果需要更高效的随机访问和索引操作,可以考虑使用切片(slice)或其他数据结构。

Ring (container/ring)

type Ring
    func New(n int) *Ring
    func (r *Ring) Do(f func(any))
    func (r *Ring) Len() int
    func (r *Ring) Link(s *Ring) *Ring
    func (r *Ring) Move(n int) *Ring
    func (r *Ring) Next() *Ring
    func (r *Ring) Prev() *Ring
    func (r *Ring) Unlink(n int) *Ring

container/ring 包是 Go 语言标准库中的一个包,提供了环形链表(circular list)的实现。环形链表是一种特殊的链表,其中最后一个节点的下一个节点是第一个节点,形成一个环状结构。

container/ring 包提供了 Ring 类型,该类型代表了一个环形链表。Ring 类型的对象可以用来存储和操作任意类型的元素。它支持在环形链表中向前和向后遍历、插入和删除元素,以及计算环形链表的长度。

container/ring 包的主要功能和方法包括:

  1. Ring 类型的方法:
  2. New(n int) *Ring:创建一个具有 n 个节点的新环形链表,并返回指向第一个节点的指针。
  3. Next() *Ring:返回下一个节点的指针。
  4. Prev() *Ring:返回上一个节点的指针。
  5. Move(n int) *Ring:将当前指针向前(负值)或向后(正值)移动 n 步,并返回新的指针。
  6. Link(s *Ring) *Ring:将当前环形链表连接到另一个环形链表 s 的前面,并返回连接后的环形链表。
  7. Unlink(n int) *Ring:从当前环形链表中移除下一个 n 个节点,并返回剩余的环形链表。
  8. Ring 类型的属性:
  9. Len() int:返回环形链表中节点的数量。
  10. Value() interface{}:返回当前节点的值。

使用 container/ring 包的步骤通常是:

  1. 导入 container/ring 包。
  2. 使用 New() 方法创建一个新的环形链表,并指定节点的数量。
  3. 使用 Next()Prev() 方法在环形链表中遍历节点。
  4. 使用 Value() 方法获取当前节点的值。
  5. 使用 Move() 方法向前或向后移动指针。
  6. 使用 Link() 方法将两个环形链表连接起来,或使用 Unlink() 方法移除节点。

container/ring 包提供了一种简单而灵活的方式来处理环形链表。它适用于需要在环形结构中进行元素遍历和操作的场景。可以使用环形链表来实现循环队列、轮转算法等应用。但是,与普通链表相比,环形链表的插入和删除操作相对较慢,因此如果需要频繁进行插入和删除操作,可能有更适合的数据结构选择。