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

举报
宇宙之一粟 发表于 2022/01/15 01:03:50 2022/01/15
【摘要】 大多数现实世界的编程都取决于我们与现有库的接口能力,而不是从头开始编写一切。本章将介绍一些最常用的软件包。 首先要提醒的是:尽管这些库中有些是相当明显的(或者在前面的章节中已经解释过了),但Go中包含的许多库需要专门的领域知识(例如:密码学)。解释这些底层技术超出了本书的范围. Strin...

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

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

Strings

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

package mainimport (    "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​​ 包由一些函数构成,但主要是其他包中使用的接口,主要的接口是 ​​Reader​​ 和 ​​Writer​

​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.Bufferbuf.Write([]byte("test"))
        

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

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

Files 和 Folder

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

package mainimport (    "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 mainimport (    "fmt"    "io/ioutil")func main() {    bs, err := ioutil.ReadFile("test.txt")    if err != nil {        return    }    str := string(bs)    fmt.Println(str)}
        

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

package mainimport (    "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 mainimport (    "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 mainimport (    "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 mainimport "errors"func main() {    err := error.New("error message")}
        

Containers & Sort

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

List

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

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

package mainimport (    "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

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

package mainimport (    "fmt"    "sort")type Person struct {    Name string    Age  int}type ByName []Personfunc (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 []Personfunc (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 mainimport (                "fmt"    "hash/crc32")func main() {    h := crc32.NewIEEE()    h.Write([]byte("test"))    v := h.Sum32()    fmt.Println(v)}
        

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

package mainimport (    "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 mainimport (        "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字节的片断来代替。

文章来源: blog.csdn.net,作者:宇宙之一粟,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/yuzhou_1shu/article/details/122476704

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。