Go 中的核心包速览#私藏项目实操分享#

简介: Go 中的核心包速览#私藏项目实操分享#

大多数现实世界的编程都取决于我们与现有库的接口能力,而不是从头开始编写一切。本章将介绍一些最常用的软件包。

首先要提醒的是:尽管这些库中有些是相当明显的(或者在前面的章节中已经解释过了),但Go中包含的许多库需要专门的领域知识(例如:密码学)。解释这些底层技术超出了本书的范围.

Strings

Go 在 strings 包中提供了大量的函数来操作字符串:

package main
import (
    "fmt"
    "strings"
)
func main() {
    fmt.Println(
        // true
        strings.Contains("test", "es"),
        // 2
        strings.Count("test", "t"),
        // true
        strings.HasPrefix("test", "te"),
        // true
        strings.HasSuffix("test", "st"),
        // 1
        strings.Index("test", "e"),
        // "a-b"
        strings.Join([]string{"a", "b"}, "-"),
        // == "aaaaa"
        strings.Repeat("a", 5),
        // "bbaa"
        strings.Replace("aaaa", "a", "b", 2),
        // []string{"a","b","c","d","e"}
        strings.Split("a-b-c-d-e", "-"),
        // "test"
        strings.ToLower("TEST"),
        // "TEST"
        strings.ToUpper("test"),
    )
}

有时我们需要将字符串作为二进制数据来处理。要将一个字符串转换为一个字节片(反之亦然),请这样做:

arr := []byte("test")
str := string([]byte{'t', 'e', 's', 't'})

Input/Output

在我们想要查看文件的时候,需要使用到 Go 的 io 包,io 包由一些函数构成,但主要是其他包中使用的接口,主要的接口是 ReaderWriter

Reader支持读操作,通过 Read方法

Writer 支持写操作,通过 Write 方法

Go 中的许多函数都把 Reader 或者 Writer作为参数。例如,io包就有一个 Copy 函数从一个 Reader 复制到 Writer :

func Copy(dst Writer, src Reader) (written int 64, err error)

为了读或者写一个字符 []byte 或者 string ,你可以使用bytes 包中的 Buffer结构体:

var buf bytes.Buffer
buf.Write([]byte("test"))

一个 Buffer 不需要被初始化,而且同时支持 ReaderWriter 接口。你可以调用 buf.Bytes() 将字符串数据转换为一个 []byte.

如果你只需要从一个字符串中读取,你也可以使用 string.NewReader 函数,这比使用缓冲区更有效。

Files 和 Folder

要在 Go 中打开一个文件,请使用 os 包中的 Open 函数。下面是一个如何读取文件内容并在终端显示的例子:

package main
import (
    "fmt"
    "os"
)
func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        // handle the error here
        return
    }
    defer file.Close()
    // get the file size
    stat, err := file.Stat()
    if err != nil {
        return
    }
    // read the file
    bs := make([]byte, stat.Size())
    _, err = file.Read(bs)
    if err != nil {
        return
    }
    str := string(bs)
    fmt.Println(str)
}

我们在打开文件后立即使用 defer file.Close() 以确保在函数完成后立即关闭该文件。读取文件是很常见的,所以有一种更简洁的方法来做这件事:

package main
import (
    "fmt"
    "io/ioutil"
)
func main() {
    bs, err := ioutil.ReadFile("test.txt")
    if err != nil {
        return
    }
    str := string(bs)
    fmt.Println(str)
}

如下是我们如何创建一个文件:

package main
import (
    "os"
)
func main() {
    file, err := os.Create("test.txt")
    if err != nil {
        // handle the error here
        return
    }
    defer file.Close()
    file.WriteString("test")
}

要获得一个目录的内容,我们使用同样的 os.Open 函数,但给它一个目录路径而不是 一个文件名。然后我们调用 Readdir 方法:

package main
import (
    "fmt"
    "os"
)
func main() {
    dir, err := os.Open(".")
    if err != nil {
        return
    }
    defer dir.Close()
    fileInfos, err := dir.Readdir(-1)
    if err != nil {
        return
    }
    for _, fi := range fileInfos {
        fmt.Println(fi.Name())
    }
}

通常我们想递归地行走一个文件夹(读取文件夹的内容、所有的子文件夹、所有的子文件夹的子文件夹......)。为了使之更容易,在 path/filepath 包中有一个Walk函数:

package main
import (
    "fmt"
    "os"
    "path/filepath"
)
func main() {
    filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    fmt.Println(path)
    return nil
    })
}

你传递给Walk的函数会对根文件夹中的每个文件和文件夹进行调用。(本例中为 .)

Errors

Go 有一个内置的错误类型,我们已经看到了(错误类型)。我们可以通过以下方式创建我们自己的错误:使用 errors 包中的 New 函数

package main
import "errors"
func main() {
    err := error.New("error message")
}

Containers & Sort

除了列表和映射之外,Go 在容器包下面还有几个可用的集合。我们将以容器/列表包为例来看看:

List

container/list 包下实现了一个双向链表,链表的数据结构有点类似:

列表的每个节点都包含一个值(本例中为1、2或3 )和一个指向下一个节点的指针。由于这是一个 每个节点都有指向下一个节点的指针 前一个节点的指针。这个列表可以由这个程序创建:

package main
import (
    "container/list"
    "fmt"
)
func main() {
    var x list.List
    x.PushBack(1)
    x.PushBack(2)
    x.PushBack(3)
    for e := x.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value.(int))
    }
}

List的零值是一个空列表(*List也可以用list.New创建)。值被添加到列表中。我们对列表中的每一个项目进行循环,首先获得第一个元素,然后跟踪所有的链接,直到我们到达 nil。

Sort

排序包包含对任意数据进行排序的函数。有几个预定义的排序函数(针对 intsfloats 的切片)。如何对你自己的数据进行排序:

package main
import (
    "fmt"
    "sort"
)
type Person struct {
    Name string
    Age  int
}
type ByName []Person
func (this ByName) Len() int {
    return len(this)
}
func (this ByName) Less(i, j int) bool {
    return this[i].Name < this[j].Name
}
func (this ByName) Swap(i, j int) {
    this[i], this[j] = this[j], this[i]
}
func main() {
    kids := []Person{
        {"Jill", 9},
        {"Jack", 10},
    }
    sort.Sort(ByName(kids))
    fmt.Println(kids)
}

sort中的Sort函数接收一个sort.Interface并对其进行排序。sort.Interface需要3个方法。Len,  Less 和  Swap。为了定义我们自己的排序,我们创建了一个新的类型(ByName),并使其等同于我们要排序的内容的一个片断。然后我们定义这3个方法。

然后,对我们的人的名单进行排序,就像把名单投到我们的新类型中一样容易。我们还可以通过以下方式按年龄排序:

type ByAge []Person
func (this ByAge) Len() int {
    return len(this)
}
func (this ByAge) Less(i, j int) bool {
    return this[i].Age < this[j].Age
}
func (this ByAge) Swap(i, j int) {
    this[i], this[j] = this[j], this[i]
}

Hashes & Cryptography

散列函数将一组数据减少到一个较小的固定大小。散列函数在程序设计中经常被使用。在程序设计中经常使用,从查找数据到轻松检测变化。Go中的哈希函数被分成两类:加密的和非加密的。

非加密的哈希函数可以在哈希包下面找到,包括adler32。crc32,crc64和fnv。下面是一个使用crc32的例子:

package main
import (            
    "fmt"
    "hash/crc32"
)
func main() {
    h := crc32.NewIEEE()
    h.Write([]byte("test"))
    v := h.Sum32()
    fmt.Println(v)
}

crc32 哈希对象实现了 Writer 接口,所以我们可以像其他 Writer 一样向它写字节。一旦我们写完了我们想要的东西,我们就调用 Sum32() 来返回一个 uint32crc32 的一个常见用途是比较两个文件。如果两个文件的 Sum32 值相同,就很有可能(虽然不是100%确定)这两个文件是一样的。如果数值不同,那么这两个文件肯定是不一样的:

package main
import (
    "fmt"
    "hash/crc32"
    "io/ioutil"
)
func getHash(filename string) (uint32, error) {
    bs, err := ioutil.ReadFile("test1.txt")
    if err != nil {
        return 0, err
    }
    h := crc32.NewIEEE()
    h.Write(bs)
    return h.Sum32(), nil
}
func main() {
    h1, err := getHash("test1.txt")
    if err != nil {
        return
    }
    h2, err := getHash("test2.txt")
    if err != nil {
        return
    }
    fmt.Println(h1, h2, h1 == h2)
}

加密哈希函数与非加密对应函数类似,但它们有一个额外的特性,即难以逆转。鉴于一组数据的加密哈希值,要确定是什么产生的哈希值是非常困难的。这些哈希值经常被用于安全应用中

一个常见的密码学散列函数被称为 SHA-1。下面是它的使用方法:

package main
import (    
    "fmt"
    "crypto/sha1"
)
func main() {
    h := sha1.New()
    h.Write([]byte("test"))
    bs := h.Sum([]byte{})
    fmt.Println(bs)
}

这个例子与crc32的例子非常相似,因为crc32和sha1都实现了hash.Hash接口。主要区别在于,crc32计算的是32位的哈希值,而sha1计算的是160位的哈希值,没有本地类型来表示160位的数字。所以我们用一个20字节的片断来代替。


相关文章
|
4天前
|
搜索推荐 Go 开发者
Go模块与依赖管理:构建稳定、可维护的项目生态
【2月更文挑战第9天】Go模块是Go语言从1.11版本开始引入的一个新的依赖管理工具,它改变了以往通过GOPATH管理项目依赖的方式,为Go开发者带来了更加灵活、高效的依赖管理方式。本文将深入探讨Go模块与依赖管理的概念、使用方法和最佳实践,帮助读者更好地理解和应用Go模块,构建稳定、可维护的项目生态。
|
4天前
|
Go 开发者
Golang深入浅出之-Go语言项目构建工具:Makefile与go build
【4月更文挑战第27天】本文探讨了Go语言项目的构建方法,包括`go build`基本命令行工具和更灵活的`Makefile`自动化脚本。`go build`适合简单项目,能直接编译Go源码,但依赖管理可能混乱。通过设置`GOOS`和`GOARCH`可进行跨平台编译。`Makefile`适用于复杂构建流程,能定义多步骤任务,但编写较复杂。在选择构建方式时,应根据项目需求权衡,从`go build`起步,逐渐过渡到Makefile以实现更高效自动化。
29 2
|
4天前
|
安全 测试技术 Go
Golang深入浅出之-Go语言单元测试与基准测试:testing包详解
【4月更文挑战第27天】Go语言的`testing`包是单元测试和基准测试的核心,简化了测试流程并鼓励编写高质量测试代码。本文介绍了测试文件命名规范、常用断言方法,以及如何进行基准测试。同时,讨论了测试中常见的问题,如状态干扰、并发同步、依赖外部服务和测试覆盖率低,并提出了相应的避免策略,包括使用`t.Cleanup`、`t.Parallel()`、模拟对象和检查覆盖率。良好的测试实践能提升代码质量和项目稳定性。
18 1
|
4天前
|
安全 Go
Golang深入浅出之-Go语言标准库中的文件读写:io/ioutil包
【4月更文挑战第27天】Go语言的`io/ioutil`包提供简单文件读写,适合小文件操作。本文聚焦`ReadFile`和`WriteFile`函数,讨论错误处理、文件权限、大文件处理和编码问题。避免错误的关键在于检查错误、设置合适权限、采用流式读写及处理编码。遵循这些最佳实践能提升代码稳定性。
24 0
|
4天前
|
缓存 编译器 Go
【Go语言专栏】理解Go语言的包管理机制
【4月更文挑战第30天】Go语言包管理是构建可维护应用的关键,从基本概念如包导入、初始化到版本管理和依赖管理,再到Go Modules的引入,简化了过程。包的可见性规则和社区生态也至关重要。理解并掌握这些机制对于编写高质量Go代码具有决定性影响。随着Go语言的持续发展,包管理将更加强大易用。
|
4天前
|
安全 Unix Go
Golang深入浅出之-Go语言中的时间与日期处理:time包详解
【4月更文挑战第26天】Go语言的`time`包提供处理日期和时间的功能,包括`time.Time`类型、时间戳、格式化与解析。本文讨论了核心概念、常见问题(如时区处理、格式字符串混淆、超时控制和并发安全)及解决方法。推荐使用`time.LoadLocation`管理时区,熟悉时间格式规则,用`context`精确控制超时,并注意并发安全。文中通过代码示例展示了如何获取格式化时间、计算时间差以及创建定时任务。学习和应用这些知识可提高程序的健壮性和准确性。
30 2
|
4天前
|
XML JSON Go
Golang深入浅出之-XML处理在Go语言中的实现:encoding/xml包
【4月更文挑战第26天】Go语言的`encoding/xml`库提供XML处理,包括序列化和反序列化。本文讨论了XML处理的基础,如`xml.Marshal`和`xml.Unmarshal`函数,以及常见问题和易错点,如标签命名、结构体嵌套、omitempty标签和命名空间。建议遵循标签命名规则,正确处理嵌套和属性,谨慎使用omitempty,以及理解并有效利用命名空间。文中还给出了基础示例和处理XML属性的代码示例,帮助读者掌握XML处理技巧。
23 1
Golang深入浅出之-XML处理在Go语言中的实现:encoding/xml包
|
4天前
|
运维 关系型数据库 MySQL
Serverless 应用引擎产品使用之在阿里函数计算中,部署Go项目可以区分环境如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
19 0
|
4天前
|
数据管理 Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第25天】Go语言中的`context`包在并发、网络请求和长任务中至关重要,提供取消、截止时间和元数据管理。本文探讨`context`基础,如`Background()`、`TODO()`、`WithCancel()`、`WithDeadline()`和`WithTimeout()`。常见问题包括不当传递、过度使用`Background()`和`TODO()`以及忽略错误处理。通过取消和超时示例,强调正确传递上下文、处理取消错误和设置超时以提高应用健壮性和响应性。正确使用`context`是构建稳定高效Go应用的关键。
22 1
|
4天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
21 1