通过示例学习-Go-语言-2023-七-

通过示例学习 Go 语言 2023(七)

Go(Golang)中的内层/外层作用域常量

来源:golangbyexample.com/constant-scope-inner-outer-go/

目录

  • 概述

  • 示例

概述

在内层作用域中声明的常量,如果与外层作用域中声明的常量同名,将会遮蔽外层作用域中的常量。

示例

package main
import "fmt"
const a = 123
func main() {
    const a = 456
    fmt.Println(a)
}

输出

456

上述程序的输出为 456,因为内层声明的常量具有该值

Go (Golang) 中的常量映射

来源:golangbyexample.com/constant-map-go/

目录

  • 概述

  • 示例

概述

Go 仅支持四种类型的常量

  • 数值(int, int64, float, float64, complex128 等)

  • 字符串

  • 字符或符文

  • 布尔值

Go 不支持常量映射,因此以下程序会引发编译错误

示例

package main

func main() {
	const e = map[string]int{
		"a": 1,
	}
}

输出

const initializer map[string]int literal is not a constant

在 Go (Golang)中从后序和中序构建二叉树

来源:golangbyexample.com/binary-tree-postorder-inorder-golang/

目录

  • 概述

  • 程序

概述

给定两个数组,表示二叉树的后序和中序遍历。目标是从中构建二叉树

示例:

考虑下面的树

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/9a9347838908483552b24df3dc54cd38.png)

树的后序遍历将是

[1,2,4,3,5,6]

树的中序遍历将是

[4,2,1,5,3,6]

将提供后序和中序数组,我们必须再次从中序和后序构建树。以下将是策略

  • 我们将使用三个索引,即数组的开始、结束和当前索引

  • 后序中的最后一个索引将是根节点。

  • 我们将在中序数组中查找与后序数组最后一个索引的值匹配的索引。我们将此索引称为 rootIndex

  • 在中序数组中,rootIndex 左侧的所有值将位于左子树中

  • 在中序数组中,rootIndex 右侧的所有值将位于右子树中

  • 我们可以使用相同的策略递归处理右子树,然后处理左子树。

例如

  • 后序遍历中的最后一个索引是根节点,其值为 1

  • 1 在中序遍历中的 第 2 个索引。因此,rootIndex 是 2

  • 在中序遍历中,rootIndex 左侧的部分是 [4,2],它是左子树的一部分

  • 在中序遍历中,rootIndex 右侧是 [5,3,6],它是右子树的一部分

  • 我们可以递归处理右子树,然后处理左子树

程序

以下是相应的程序

package main

import (
	"fmt"
)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func buildTree(inorder []int, postorder []int) *TreeNode {
	lenTree := len(inorder)

	index := lenTree - 1
	return buildTreeUtil(inorder, postorder, &index, 0, lenTree-1)
}

func buildTreeUtil(inorder []int, postorder []int, index *int, low, high int) *TreeNode {
	if low > high {
		return nil
	}
	if low == high {
		currentIndexValue := postorder[*index]
		(*index)--
		return &TreeNode{Val: currentIndexValue}
	}

	currentIndexValue := postorder[*index]
	(*index)--

	root := &TreeNode{Val: currentIndexValue}

	mid := 0
	for i := low; i <= high; i++ {
		if inorder[i] == currentIndexValue {
			mid = i
		}
	}

	root.Right = buildTreeUtil(inorder, postorder, index, mid+1, high)
	root.Left = buildTreeUtil(inorder, postorder, index, low, mid-1)
	return root

}

func main() {
	inorder := []int{4, 2, 1, 5, 3, 6}
	postorder := []int{4, 2, 5, 6, 3, 1}

	root := buildTree(inorder, postorder)
	fmt.Printf("root: %d\n", root.Val)
	fmt.Printf("root.Left: %d\n", root.Left.Val)
	fmt.Printf("root.Left.Left: %d\n", root.Left.Left.Val)
	fmt.Printf("root.Right: %d\n", root.Right.Val)
	fmt.Printf("root.Right.Left: %d\n", root.Right.Left.Val)
	fmt.Printf("root.Right.Right: %d\n", root.Right.Right.Val)
} 

输出

root: 1
root.Left: 2
root.Left.Left: 4
root.Right: 3
root.Right.Left: 5
root.Right.Right: 6

注意: 查看我们的 Golang 高级教程。本系列的教程详细且我们已尝试用示例涵盖所有概念。这个教程适合那些希望获得专业知识和对 golang 有扎实理解的人 - Golang 高级教程

如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是的话,这篇文章适合你 - 所有设计模式 Golang*

从先序和中序在 Go (Golang)中构造二叉树

来源:golangbyexample.com/tree-preorder-inorder-golang/

目录

  • 概述

  • 程序

概述

给定两个数组,它们表示一个二叉树的先序遍历和中序遍历。目标是从中构造一个二叉树。

示例:

考虑下面的树

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/9a9347838908483552b24df3dc54cd38.png)

树的先序遍历将是

[1,2,4,3,5,6]

树的中序遍历将是

[4,2,1,5,3,6]

将给定先序和中序数组,我们必须再次从中序和先序构造树。以下是策略

  • 我们将使用三个索引,分别是数组的起始、结束和当前索引

  • 先序中的起始索引将是根。

  • 我们将在中序数组中找到一个索引,其值与先序数组的起始索引处的值匹配。我们将这个索引称为 rootIndex。

  • 中序数组中 rootIndex 左侧的所有值将位于左子树中

  • 中序数组中 rootIndex 右侧的所有值将位于右子树中

  • 然后我们可以用相同的策略递归左子树,再递归右子树。

例如

  • 先序遍历的第一个索引是根,其值为1

  • 1在中序遍历的第 2个索引上。因此 rootIndex 是2

  • 中序遍历中 rootIndex 左侧的部分是[4,2],这部分属于左子树

  • 中序遍历中 rootIndex 右侧的部分是[5,3,6],这部分属于右子树

  • 我们可以先递归左子树,然后递归右子树

程序

下面是相同程序的代码

package main

import (
	"fmt"
)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func buildTree(preorder []int, inorder []int) *TreeNode {

	lenOfTree := len(preorder)

	current := 0
	newRoot := buildTreeUtil(preorder, inorder, &current, 0, lenOfTree-1)

	return newRoot
}

func buildTreeUtil(preorder []int, inorder []int, current *int, low, high int) *TreeNode {
	if low > high {
		return nil
	}

	if low == high {
		rootNode := &TreeNode{Val: preorder[*current]}
		(*current)++
		return rootNode
	}

	rootNode := &TreeNode{Val: preorder[*current]}
	rootValue := preorder[*current]
	(*current)++

	var rootIndex int
	for i := low; i <= high; i++ {
		if inorder[i] == rootValue {
			rootIndex = i
		}
	}

	rootNode.Left = buildTreeUtil(preorder, inorder, current, low, rootIndex-1)
	rootNode.Right = buildTreeUtil(preorder, inorder, current, rootIndex+1, high)

	return rootNode
}

func main() {
	inorder := []int{4, 2, 1, 5, 3, 6}
	preorder := []int{1, 2, 4, 3, 5, 6}

	root := buildTree(preorder, inorder)
	fmt.Printf("root: %d\n", root.Val)
	fmt.Printf("root.Left: %d\n", root.Left.Val)
	fmt.Printf("root.Left.Left: %d\n", root.Left.Left.Val)
	fmt.Printf("root.Right: %d\n", root.Right.Val)
	fmt.Printf("root.Right.Left: %d\n", root.Right.Left.Val)
	fmt.Printf("root.Right.Right: %d\n", root.Right.Right.Val)
}

输出

root: 1
root.Left: 2
root.Left.Left: 4
root.Right: 3
root.Right.Left: 5
root.Right.Right: 6

注意: 查看我们的 Golang 高级教程。本系列的教程内容详尽,我们尽力涵盖所有概念和示例。本教程适合那些希望获得专业知识和深入理解 Golang 的读者 - Golang 高级教程

如果你有兴趣了解所有设计模式如何在 Golang 中实现。如果是的话,这篇文章就是为你准备的 - 所有设计模式 Golang

联系我们

来源:golangbyexample.com/contact-us/

嗨,你好,

我们期待收到你的消息。请随时通过下面的表单与我们联系,我们会尽快回复你。

Go(Golang)中的 float32 与 float64 之间的转换

来源:golangbyexample.com/conversion-float-golang/

目录

  • 概述

  • float32 转 float64

  • float64 转 float32

概述

Golang 需要显式转换才能在两种类型之间转换。float32 和 float64 数据类型之间的转换需要显式类型转换。下面是语法。

{destination_type}(some_value) 

这将 some_value 转换为 destination_type

float32 转 float64

var a float32 = 12
var b float64 = float64(a)

b := float64(a)

下面是相应的工作程序

package main
import "fmt"
func main() {
    var a float32 = 12.0
    var b float64 = float64(a)
    fmt.Printf("Underlying Type of b: %T\n", b)

    b2 := float64(a)
    fmt.Printf("Underlying Type of b2: %T\n", b2)
}

输出

Underlying Type of b: float64
Underlying Type of b2: float64

float64 转 float32

var a float64 = 12
var b float32 = float32(a)

b := float32(a)

下面是相应的工作程序。

package main

import "fmt"

func main() {
	var a float64 = 12.0
	var b float32 = float32(a)

	fmt.Printf("Underlying Type of b: %T\n", b)

	b2 := float32(a)
	fmt.Printf("Underlying Type of b2: %T\n", b2)
}

如果我们直接将 float64 值赋给 float32,或反之,且没有明确转换,将会引发编译错误。

cannot use a (type float64) as type float32 in assignment

同时,查看我们的 Golang 进阶教程系列 – Golang 进阶教程

在 Go (Golang)中,映射与 JSON 之间的转换。

来源:golangbyexample.com/map-json-golang/

目录

  • 概述

  • 映射到 JSON

  • JSON 到映射

概述

encoding/json包提供了用于转换到 JSON 和从 JSON 转换的工具。可以使用相同的工具将 Golang 映射转换为 JSON 字符串,反之亦然。但需要注意的是,映射允许键的整数值,而 JSON 不允许键的整数值。JSON 仅允许键的字符串值。因此,当将具有整数键值的映射转换为 JSON 时,键将具有字符串值。

映射到 JSON

让我们来看一个将映射转换为 JSON 的程序。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	a := make(map[int]string)

	a[1] = "John"

	j, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
	} else {
		fmt.Println(string(j))
	}
}

输出

{"1":"John"}

在上面的代码中,我们使用json.Marshal函数将映射转换为 JSON。该映射的键对应一个整数值。

a := make(map[int]string)

在转换后,结果 JSON 作为键的字符串值。

{"1":"John"}

让我们再看一个例子,在这个例子中,我们将映射转换为JSON,其中映射的值是一个结构体。以下是相关代码。

package main
import (
    "encoding/json"
    "fmt"
)
type employee struct {
    Name string
}
func main() {
    a := make(map[string]employee)
    a["1"] = employee{Name: "John"}
    j, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("Error: %s", err.Error())
    } else {
        fmt.Println(string(j))
    }
}

输出

{"1":{"Name":"John"}}

JSON 到映射

让我们尝试将 JSON 转换为映射。我们将把上面示例中得到的 JSON 结果转换回映射。

json.Unmarshal函数可用于将 JSON 转换回映射。

第一种情况:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	a := make(map[int]string)

	a[1] = "John"

	j, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
	} else {
		fmt.Println(string(j))
	}

	var b map[int]string
	json.Unmarshal(j, &b)

	fmt.Println(b)
}

输出

{"1":"John"}
map[1:John]

第二种情况

package main
import (
    "encoding/json"
    "fmt"
)
type employee struct {
    Name string
}
func main() {
    a := make(map[string]employee)
    a["1"] = employee{Name: "John"}
    j, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("Error: %s", err.Error())
    } else {
        fmt.Println(string(j))
    }
    var b map[int]employee
    json.Unmarshal(j, &b)
    fmt.Println(b)
}

输出

{"1":{"Name":"John"}}
map[1:{John}]

Go(Golang)中结构体与 JSON 之间的转换。

来源:golangbyexample.com/struct-json-golang/

目录

  • 概述

  • 结构体转 JSON

  • JSON 转结构体

概述

encoding/json 包提供了可以用于 JSON 转换的工具。相同的工具可以用于将 Go 结构体转换为 JSON 字符串及其反向转换。使用的两个函数是。

  • Marshal - 将结构体转换为 JSON 字符串。

  • Unmarshal - 将 JSON 字符串转换回结构体。

在我们查看如何将结构体转换为 JSON 及其反向转换之前,我们需要了解 Go 结构体的两件事:

  • 只有结构体的导出字段对外部库可见。因此,在转换后的 JSON 字符串中仅会存在结构体的导出字段。还要注意,在 Go 中,结构体的大写字段是导出的。

  • 结构体字段具有一个元部分,其中包含有关该字段的附加信息。这些元字段在将结构体转换为 JSON 及其反向转换时使用。此外,请注意这些结构体元字段是可选的。假设我们有以下结构体。

type employee struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

注意与每个字段关联的元标签,这些字段的名称为 ‘json’。在将结构体转换为 JSON 及其反向转换时使用这些元字段。

因此,上述结构体转换为 JSON 后将如下所示。

{
  "name" : "John",
  "age"  : 21
}

那么上述 JSON 字符串中的 name 键将映射到 employee 结构体的 Name 字段,而 JSON 字符串中的 age 键将映射到结构体的 Age 字段。此外,在将上述 JSON 字符串转换为结构体时,JSON 中 name 字段的值将传递给结构体的 Name 字段,而 JSON 中 age 字段的值将传递给结构体的 Age 字段。

作为另一个示例,假设我们有以下结构体。

type employee struct {
	Name string `json:"n"`
	Age  int    `json:"ag"`
}

然后在转换为 JSON 后,JSON 的 ‘n’ 键将映射到结构体的 Name 字段,而 JSON 的 ‘ag’ 键将映射到结构体的 Age 字段。因此,它将生成如下 JSON。

{
  "n" : "John",
  "age"  : 21
}

此外,在将上述 JSON 字符串转换为结构体时,JSON 字符串中的 ‘n’ 字段的值将传递给结构体的 Name 字段,而 JSON 中 ‘a’ 字段的值将传递给结构体的 Age 字段。

如果结构体不包含任何元标签,则结果 JSON 中的键名将与结构体字段的名称相同,反之亦然。例如,如果我们有以下结构体。

type employee struct {
	Name string 
	Age  int   
}

注意,字段没有 JSON 元数据标签。因此,转换为 JSON 后将如下所示。

{
  "Name" : "John",
  "Age"  : 21
}

结构体转 JSON

json.Marshal 函数可用于将结构体转换为 JSON。让我们来看一个从结构体转换为 JSON 的示例。为说明以上所有要点,我们创建了两个结构体。

  • employee1 结构体 - 它具有元标签。

  • employee2结构体 - 它没有元标签。

此外,两个结构体中的 salary 字段均为未导出。

package main
import (
    "encoding/json"
    "fmt"
    "log"
)
type employee1 struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    salary int    `json:"s"`
}
type employee2 struct {
    Name   string
    Age    int
    salary int
}
func main() {
    e1 := employee1{
        Name:   "John",
        Age:    21,
        salary: 1000,
    }
    j, err := json.Marshal(e1)
    if err != nil {
        log.Fatalf("Error occured during marshaling. Error: %s", err.Error())
    }
    fmt.Printf("employee1 JSON: %s\n", string(j))
    e2 := employee2{
        Name:   "John",
        Age:    21,
        salary: 1000,
    }
    j, err = json.Marshal(e2)
    if err != nil {
        log.Fatalf("Error occured during marshaling. Error: %s", err.Error())
    }
    fmt.Printf("\nemployee2 JSON: %s\n", string(j))
}

输出

employee1 JSON: {"n":"John","a":21}

employee2 JSON: {"Name":"John","Age":21}

请注意,我们使用json.Marshal函数将结构体转换为 JSON。

对于employee1结构体到 JSON 的转换,输出为

{"n":"John","a":21}

这是因为

  • salary字段在输出中不存在,因为它未导出,即该字段未大写。

  • 由于与employee1结构体关联的 JSON 标签,‘Name’字段映射到 JSON 的‘n’字段,而‘Age’字段映射到 JSON 的‘a’字段。

对于employee2结构体到 JSON 的转换,输出为

{"Name":"John","Age":21}

这是因为

  • salary字段在输出中不存在,因为它未导出,即该字段未大写。

  • 由于employee2结构体没有关联任何 JSON 标签,employee1结构体的‘Name’字段映射到 JSON 的‘Name’字段,而‘Age’字段映射到 JSON 的‘Age’字段。

JSON 到结构体

json.Unmarshal函数可用于将 JSON 转换为结构体。我们之前讨论的规则同样适用于从 JSON 转换为结构体。让我们来看一个例子。

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type employee1 struct {
	Name   string `json:"n"`
	Age    int    `json:"a"`
	salary int    `json:"s"`
}

type employee2 struct {
	Name   string
	Age    int
	salary int
}

func main() {
	e1Json := `{"n":"John","a":21}`

	var e1Converted employee1
	err := json.Unmarshal([]byte(e1Json), &e1Converted)
	if err != nil {
		log.Fatalf("Error occured during unmarshaling. Error: %s", err.Error())
	}
	fmt.Printf("employee1 Struct: %#v\n", e1Converted)

	e2Json := `{"Name":"John","Age":21}`
	var e2Converted employee2
	err = json.Unmarshal([]byte(e2Json), &e2Converted)
	if err != nil {
		log.Fatalf("Error occured during unmarshaling. Error: %s", err.Error())
	}
	fmt.Printf("\nemployee2 Struct: %#v\n", e2Converted)
}

输出

employee1 Struct: main.employee1{Name:"John", Age:21, salary:0}

employee2 Struct: main.employee2{Name:"John", Age:21, salary:0}

此示例使用了第一个示例的输出 JSON 字符串。在这里,我们使用json.Unmarshal函数将 JSON 字符串转换为结构体。首先需要注意的是,我们需要将结构体的地址传递给json.Unmarshal函数,如下所示。

err = json.Unmarshal(j, &e1Converted)

第一个参数是 JSON 字节,第二个是结构体的地址。

解组

{"n":"John","a":21}

输出到employee1结构体。

main.employee1{Name:"John", Age:21, salary:0}

解组

{"Name":"John","Age":21}

输出到employee2结构体。

main.employee2{Name:"John", Age:21, salary:0}

如果你尝试解组

{"n":"John","a":21}
 into employee2 struct then the output will be 
main.employee2{Name:"", Age:0, salary:0}

由于employee2结构体中没有元标签,且employee2结构体中的键名与 JSON 中的键名不同,因此不会进行解组。因此将创建一个空的employee2结构体,结构体中的每个字段都将初始化为其类型的默认零值。

如果 JSON 字符串包含salary字段,那么 JSON 字符串中的salary字段的值将不会反映在结构体的salary字段中,因为salary字段在结构体中未导出。请参见此示例。

package main
import (
    "encoding/json"
    "fmt"
    "log"
)
type employee1 struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    salary int    `json:"s"`
}
func main() {
    e1Json := `{"n":"John","a":21,"salary":1000}`
    var e1Converted employee1
    err := json.Unmarshal([]byte(e1Json), &e1Converted)
    if err != nil {
        log.Fatalf("Error occured during unmarshaling. Error: %s", err.Error())
    }
    fmt.Printf("employee1 Struct: %#v\n", e1Converted)
}

输出

employee1 Struct: main.employee1{Name:"John", Age:21, salary:0}

尽管 JSON 字符串中的salary字段值为 1000,但在转换为结构体后,结构体中的salary字段为 0。

在 Go (Golang) 中将 JSON 转换为 map。

来源:golangbyexample.com/json-to-map-golang/

目录

  • 概述**

  • 示例

概述

encoding/json 包提供的工具可以用于在 JSON 之间进行转换。相同的工具可以用于将 Golang map 转换为 JSON 字符串,反之亦然。

示例

json.Unmarshal 函数可以用于将 JSON 转换为 map。

可能有两种情况。

  • 如果你知道 JSON 的格式,那么请按相同格式初始化 map。例如,当我们知道 JSON 中的值部分为字符串类型时,我们可以按以下格式初始化 map。
map[string]string
  • 如果 JSON 的格式未知,则需要按照以下格式初始化相应的 map。值部分需要是一个空接口。
map[string]interface{}

让我们看看一些示例。在第一个示例中,我们有以下 JSON 字符串。

{"1":"John"}

假设 JSON 的格式是已知的。以下是程序。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	j := `{"1":"John"}`
	var b map[string]string
	json.Unmarshal([]byte(j), &b)

	fmt.Println(b)
}

输出

map[1:John]

在第二个示例中,我们有以下 JSON 字符串。

{"1":{"Name":"John"}}

我们正在将这个 JSON 字符串解析成下面的 map 类型。

map[int]employee

其中 employee 是一个结构体。

type employee struct {
    Name string
}

这是代码。

package main

import (
	"encoding/json"
	"fmt"
)

type employee struct {
	Name string
}

func main() {
	j := `{"1":{"Name":"John"}}`
	var b map[int]employee
	json.Unmarshal([]byte(j), &b)

	fmt.Println(b)
}

输出

map[1:{John}]

让我们再看看一种情况,即 JSON 的格式未知的例子。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	j := `{"1":"John"}`
	var b map[string]interface{}
	json.Unmarshal([]byte(j), &b)

	fmt.Println(b["1"])
}

输出

John

在 Go 中将映射转换为 JSON

来源:golangbyexample.com/map-to-json-golang/

目录

  • 概述

  • 示例

概述

encoding/json包提供了可以用于转换为 JSON 和从 JSON 转换的工具。相同的工具可以用来将 Golang 映射转换为 JSON 字符串,反之亦然。需要注意的一个重要点是,映射允许整数作为键,而 JSON 不允许整数作为键。JSON 只允许字符串作为键。因此,具有整数值作为键的映射在转换为 JSON 时,键将是字符串值。

示例

让我们看看将映射转换为 JSON 的程序

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	a := make(map[int]string)

	a[1] = "John"

	j, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
	} else {
		fmt.Println(string(j))
	}
}

输出

{"1":"John"}

在上面的代码中,我们使用json.Marshal函数将映射转换为 JSON。该映射的键是整数值。

a := make(map[int]string)

转换后,结果 JSON 的键为字符串值

{"1":"John"}

让我们再看一个示例,我们将一个映射转换为JSON,其中映射的值是一个结构体。以下是相应的代码

package main
import (
    "encoding/json"
    "fmt"
)
type employee struct {
    Name string
}
func main() {
    a := make(map[string]employee)
    a["1"] = employee{Name: "John"}
    j, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("Error: %s", err.Error())
    } else {
        fmt.Println(string(j))
    }
}

输出

{"1":{"Name":"John"}}

将已排序的链表转换为平衡的二叉搜索树

来源:golangbyexample.com/sorted-linked-list-to-balanced-bst/

目录

  • 概述

  • 程序

概述

目标是将已排序的链表转换为平衡的二叉搜索树。平衡的二叉搜索树是指每个节点的两个子树深度差不超过 1 的二叉搜索树。

假设我们有以下已排序的链表

-2->-1->0->1->2

然后它应该生成一个平衡的二叉搜索树。

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/24e11dabae8c15400646a8546c56358d.png)

程序

以下是相应程序

package main

import "fmt"

type ListNode struct {
	Val  int
	Next *ListNode
}

type SingleList struct {
	Len  int
	Head *ListNode
}

func (s *SingleList) AddFront(num int) {
	ele := &ListNode{
		Val: num,
	}
	if s.Head == nil {
		s.Head = ele
	} else {
		ele.Next = s.Head
		s.Head = ele
	}
	s.Len++
}

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func sortedListToBST(head *ListNode) *TreeNode {

	len := lenOfList(head)
	return sortedListToBSTUtil(&head, len)
}

func sortedListToBSTUtil(head **ListNode, n int) *TreeNode {

	if *head == nil {
		return nil
	}

	if n <= 0 {
		return nil
	}

	left := sortedListToBSTUtil(head, n/2)

	root := &TreeNode{Val: (*head).Val}
	*head = (*head).Next

	root.Left = left

	root.Right = sortedListToBSTUtil(head, n-n/2-1)
	return root

}

func lenOfList(head *ListNode) int {
	length := 0
	for head != nil {
		length++
		head = head.Next
	}
	return length
}

func main() {
	singleList := &SingleList{}
	fmt.Printf("AddFront: 2\n")
	singleList.AddFront(2)
	fmt.Printf("AddFront: 1\n")
	singleList.AddFront(1)
	fmt.Printf("AddFront: 0\n")
	singleList.AddFront(0)
	fmt.Printf("AddFront: -1\n")
	singleList.AddFront(-1)
	fmt.Printf("AddFront: -2\n")
	singleList.AddFront(-2)

	fmt.Println()
	root := sortedListToBST(singleList.Head)
	fmt.Printf("root: %d\n", root.Val)
	fmt.Printf("root.Left: %d\n", root.Left.Val)
	fmt.Printf("root.Left.Left: %d\n", root.Left.Left.Val)
	fmt.Printf("root.Right: %d\n", root.Right.Val)
	fmt.Printf("root.Right.Left: %d\n", root.Right.Left.Val)
}

输出

AddFront: 2
AddFront: 1
AddFront: 0
AddFront: -1
AddFront: -2

root: 0
root.Left: -1
root.Left.Left: -2
root.Right: 2
root.Right.Left: 1

注意: 查看我们的 Golang 高级教程。本系列教程详尽,我们试图用示例覆盖所有概念。本教程适合那些希望获得专业知识并深入理解 Golang 的人 - Golang 高级教程

如果你对如何在 Golang 中实现所有设计模式感兴趣,这篇文章就是为你准备的 - 所有设计模式 Golang

将整型数组或数字转换为 Go(Golang)中的字符串

来源:golangbyexample.com/array-int-string-golang/

目录

  • 概述

  • 程序

概述

在本教程中,我们将学习如何在 Go 中将整型或数字数组转换为字符串。

下面是相应的程序。

程序

这里是相应的程序。

package main

import (
	"fmt"
	"strconv"
)

func main() {
	sample := []int{2, 1, 3}

	output := ""
	for _, v := range sample {
		temp := strconv.Itoa(v)
		output = output + temp
	}
	fmt.Println(output)
}

输出

213

注意: 查看我们的 Golang 高级教程。本系列教程内容详尽,我们努力覆盖所有概念并提供示例。本教程适合希望获得专业知识和对 Golang 有深入理解的学习者 – Golang 高级教程

如果你有兴趣了解所有设计模式如何在 Golang 中实现,那么这篇文章适合你 –

所有设计模式 Golang

注意: 查看我们的系统设计教程系列 系统设计问题*

在 Go (Golang) 中将数组/切片转换为 JSON

来源:golangbyexample.com/array-slice-json-golang/

目录

  • 概述

  • 示例

概述

encoding/json 包提供了 Marshal 函数,可用于将 golang 数组或切片转换为 JSON 字符串,反之亦然。

让我们看看一个例子

示例

package main
import (
    "encoding/json"
    "fmt"
)
func main() {
    a := make([]string, 2)
    a[0] = "John"
    a[1] = "Sam"
    j, err := json.Marshal(a)
    if err != nil {
        fmt.Printf("Error: %s", err.Error())
    } else {
        fmt.Println(string(j))
    }
}

输出

["John","Sam"]
```*


<!--yml

分类:未分类

日期:2024-10-13 06:52:30

-->

# 将 IOTA 或枚举转换为 Go 语言中的字符串

> 来源:[`golangbyexample.com/convert-an-iota-or-enum-to-a-string-in-go-golang/`](https://golangbyexample.com/convert-an-iota-or-enum-to-a-string-in-go-golang/)

目录

+   概述

+   示例

# 概述

在 Golang 中,可以通过使用 IOTA 创建枚举。请参考这篇文章以了解更多关于 IOTA 的信息。

> [Go 语言中的 IOTA](https://golangbyexample.com/iota-in-golang/)

[`golangbyexample.com/iota-in-golang/embed/#?secret=0FEJ5OZNxf#?secret=4y3pVxudgT`](https://golangbyexample.com/iota-in-golang/embed/#?secret=0FEJ5OZNxf#?secret=4y3pVxudgT)

在这篇文章中,我们将看到如何将 IOTA 或枚举转换为字符串值。默认情况下,当打印 IOTA 或枚举值时,它将打印其整数部分。请看这个例子。稍后我们将看到如何定义自定义的**toString**方法以打印 IOTA 或枚举的字符串值。

# 示例

```go
package main

import "fmt"

type Size uint8

const (
	small Size = iota
	medium
	large
	extraLarge
)

func main() {
	fmt.Println(small)
	fmt.Println(medium)
	fmt.Println(large)
	fmt.Println(extraLarge)
}

输出

0
1
2
3

我们还可以在 Size 类型上定义一个toString方法,以打印枚举的确切值。请看下面的程序。

package main
import "fmt"
type Size int
const (
    small = Size(iota)
    medium
    large
    extraLarge
)
func main() {
    var m Size = 1
    m.toString()
}
func (s Size) toString() {
    switch s {
    case small:
        fmt.Println("Small")
    case medium:
        fmt.Println("Medium")
    case large:
        fmt.Println("Large")
    case extraLarge:
        fmt.Println("Extra Large")
    default:
        fmt.Println("Invalid Size entry")
    }
}

输出

medium

现在我们将为Size类型定义一个toString方法。它可以用来打印 Size 类型常量的字符串值。

在 Go(Golang)中将浮点数转换为整数

来源:golangbyexample.com/float-to-int-golang/

目录

  • 概述

  • float64 转 int

  • float32 转 int

概述

Golang 需要明确转换才能在不同类型之间进行转换。浮点数据类型可以通过显式类型转换直接转换为浮点数据类型。以下是其语法。

{destination_type}(some_value) 

这将some_value转换为目标类型。

float64 转 int

var a float64 = 12
var b int = int(a)

b := int(a)

以下是相应的程序

package main

import "fmt"

func main() {
    var a float64 = 12
    var b int = int(a)
    fmt.Printf("Underlying Type of b: %T\n", b)

    b2 := int(a)
    fmt.Printf("Underlying Type of b2: %T\n", b2)
}

输出

Underlying Type of b: int
Underlying Type of b2: int

float32 转 int

var a float32 = 12
var b int = int(a)

b := int(a)

以下是相应的工作程序。

package main

import "fmt"

func main() {
    var a float32 = 12
    var b int = int(a)
    fmt.Printf("Underlying Type of b: %T\n", b)

    b2 := int(a)
    fmt.Printf("Underlying Type of b2: %T\n", b2)
}

输出

Underlying Type of b: int
Underlying Type of b2: int

如果我们直接将浮点值赋给整数变量而没有明确转换,将会引发以下编译错误。

cannot use a (type float64) as type int in assignment

cannot use a (type float32) as type int in assignment

另外,请查看我们的 Golang 进阶教程系列 – Golang 进阶教程

在 Go 中将 int 转换为 float(Golang)

来源:golangbyexample.com/int-to-float-golang/

目录

  • 概述

  • int 到 float64

  • int 到 float32

概述

Golang 需要显式转换才能从一种类型转换为另一种类型。int 数据类型可以通过显式类型转换直接转换为 float 数据类型。以下是语法。

{destination_type}(some_value) 

这将some_value转换为destination_type

int 到 float64

var a int = 12
var b float64 = float64(a)

b := float64(a)

以下是相同的程序

package main
import "fmt"
func main() {
    var a int = 12
    var b float32 = float64(a)
    fmt.Printf("Underlying Type of b: %T\n", b)

    b2 := float64(a)
    fmt.Printf("Underlying Type of b2: %T\n", b2)
}

输出

Underlying Type of b: float64
Underlying Type of b2: float64

int 到 float32

var a int = 12
var b float32 = float32(a)

b := float32(a)

以下是相同的工作程序。

package main

import "fmt"

func main() {
	var a int = 12
	var b float32 = float32(a)

	fmt.Printf("Underlying Type of b: %T\n", b)

	b2 := float32(a)
	fmt.Printf("Underlying Type of b2: %T\n", b2)
}

输出

Underlying Type of b: float32
Underlying Type of b2: float32

如果我们直接将一个 int 赋值给 float 变量而不进行转换,将会引发编译错误。

cannot use a (type int) as type float64 in assignment

另外,查看我们的 Golang 高级教程系列 – Golang 高级教程

将查询参数字符串转换为 Go(Golang)中的查询参数哈希

来源:golangbyexample.com/query-param-map-golang/

目录

  • 概述

  • 程序

概述

假设我们有以下查询参数字符串

a=b&x=y

我们希望输出的格式如下图所示

map[a:b x:y]

程序

下面是相应的程序

package main

import (
	"fmt"
	"strings"
)

func main() {
	query_param_map := make(map[string]string)

	input := "a=b&x=y"

	input_split := strings.Split(input, "&")

	for _, v := range input_split {
		v_split := strings.Split(v, "=")
		query_param_map[v_split[0]] = v_split[1]

	}
	fmt.Println(query_param_map)

}

输出

map[a:b x:y]

使用 Go (Golang) 将单链表转换为循环链表

来源:golangbyexample.com/single-linked-list-circular-golang/

![](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/2bc3aa339263d40466019d041011a04e.png)

目录

  • 概述

  • 程序

概述

使用 Golang 将单链表转换为循环链表

输入单链表:

"A" -> "B" -> "C" -> "D"

输出应该是一个循环链表:

"A" -> "B" -> "C" -> "D"
 ^                               |
|____________________|

程序

本程序中有两个重要方法

  • ToCircular – 将单链表转换为循环链表

  • IsCircular – 检查一个链表是否为循环链表

package main

import "fmt"

type node struct {
	data string
	next *node
}

type singlyLinkedList struct {
	len  int
	head *node
}

func initList() *singlyLinkedList {
	return &singlyLinkedList{}
}

func (s *singlyLinkedList) AddFront(data string) {
	node := &node{
		data: data,
	}

	if s.head == nil {
		s.head = node
	} else {
		node.next = s.head
		s.head = node
	}
	s.len++
	return
}

func (s *singlyLinkedList) Traverse() error {
	if s.head == nil {
		return fmt.Errorf("TraverseError: List is empty")
	}
	current := s.head
	for current != nil {
		fmt.Println(current.data)
		current = current.next
	}
	return nil
}

//Function to convert singly linked list to circular linked list
func (s *singlyLinkedList) ToCircular() {
	current := s.head
	for current.next != nil {
		current = current.next
	}
	current.next = s.head
}

func (s *singlyLinkedList) IsCircular() bool {
	if s.head == nil {
		return true
	}
	current := s.head.next
	for current.next != nil && current != s.head {
		current = current.next
	}
	return current == s.head
}

func main() {
	singleList := initList()
	fmt.Printf("AddFront: D\n")
	singleList.AddFront("D")
	fmt.Printf("AddFront: C\n")
	singleList.AddFront("C")
	fmt.Printf("AddFront: B\n")
	singleList.AddFront("B")
	fmt.Printf("AddFront: A\n")
	singleList.AddFront("A")

	err := singleList.Traverse()
	if err != nil {
		fmt.Println(err.Error())
	}

	fmt.Printf("Size: %d\n", singleList.len)
	singleList.ToCircular()

	isCircular := singleList.IsCircular()
	fmt.Printf("Is Circular: %t\n", isCircular)
}

输出

AddFront: D
AddFront: C
AddFront: B
AddFront: A
A
B
C
D
Size: 4
Is Circular: true

使用 Go (Golang)将单链表转换为数组

来源:golangbyexample.com/linked-list-array-go/

![单链表转数组图](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/ccbe90c04631b28e895e760eba3b58b3.png)

目录

  • 概述

  • 程序

概述

使用 Golang 将单链表转换为数组

输入单链表:

 "A" -> "B" -> "C" -> "D"

输出数组:

["A", "B", "C", "D"]

程序

package main

import "fmt"

type node struct {
	data string
	next *node
}

type singlyLinkedList struct {
	len  int
	head *node
}

func initList() *singlyLinkedList {
	return &singlyLinkedList{}
}

func (s *singlyLinkedList) AddFront(data string) {
	node := &node{
		data: data,
	}

	if s.head == nil {
		s.head = node
	} else {
		node.next = s.head
		s.head = node
	}
	s.len++
	return
}

func (s *singlyLinkedList) Size() int {
	return s.len
}

func (s *singlyLinkedList) Traverse() error {
	if s.head == nil {
		return fmt.Errorf("TraverseError: List is empty")
	}
	current := s.head
	for current != nil {
		fmt.Println(current.data)
		current = current.next
	}
	return nil
}

//Function to convert singly linked list to an array
func (s *singlyLinkedList) ToArray() []string {
	var myarr []string
	current := s.head
	for current.next != nil {
		fmt.Printf("\nAdding Element to array: %s", current.data)
		myarr = append(myarr, current.data)
		current = current.next
	}
	fmt.Printf("\nAdding Element to array: %s", current.data)
	myarr = append(myarr, current.data)
	return myarr
}

func main() {
	singleList := initList()
	fmt.Printf("AddFront: D\n")
	singleList.AddFront("D")
	fmt.Printf("AddFront: C\n")
	singleList.AddFront("C")
	fmt.Printf("AddFront: B\n")
	singleList.AddFront("B")
	fmt.Printf("AddFront: A\n")
	singleList.AddFront("A")

	fmt.Printf("Size: %d\n", singleList.Size())

	err := singleList.Traverse()
	if err != nil {
		fmt.Println(err.Error())
	}

	myarr := singleList.ToArray()
	fmt.Println()
	fmt.Println(myarr)
}

输出:

AddFront: D
AddFront: C
AddFront: B
AddFront: A
Size: 4
A
B
C
D

Adding Element to array: A
Adding Element to array: B
Adding Element to array: C
Adding Element to array: D
[A B C D]

在 Go (Golang) 中将字符串转换为小写

来源:golangbyexample.com/string-lowercase-golang/

目录

  • 概述

  • 代码:

概述

在 GO 中,字符串是 UTF-8 编码的。GO 的 strings 包提供了一个 ToLower 方法,可以将所有 Unicode 字母转换为小写。该方法会返回字符串的副本,因为 GO 字符串是不可变的。

以下是该函数的签名

func ToLower(s string) string

我们来看看实际的代码

代码:

package main

import (
    "fmt"
    "strings"
)

func main() {
    res := strings.ToLower("ABC")
    fmt.Println(res)

    res = strings.ToLower("ABC12$a")
    fmt.Println(res)
}

输出:

abc
abc12$a
```*


<!--yml

类别:未分类

日期:2024-10-13 06:12:26

-->

# 在 Go(Golang)中将字符串转换为大写

> 来源:[`golangbyexample.com/golang-string-uppercase/`](https://golangbyexample.com/golang-string-uppercase/)

目录

+   概述

+   代码:

# **概述**

在 Go 语言中,字符串采用 UTF-8 编码。**strings** 包提供了一个 **ToUpper** 方法,可以将所有 Unicode 字母转换为大写。此方法将返回字符串的副本,因为 Go 中的字符串是不可变的。

以下是该函数的签名

让我们看看实际代码

# **代码:**

```go
package main

import (
    "fmt"
    "strings"
)

func main() {
    res := strings.ToUpper("abc")
    fmt.Println(res)

    res = strings.ToUpper("abc12$")
    fmt.Println(res)
}

输出:

[预览 1]*

在 Go 语言中转换不同时区的时间。

来源:golangbyexample.com/convert-time-timezones-go/

每个time.Time对象都有一个关联的location值。当你将任何time.Time对象的位置更改为其他位置时,该时间瞬间并不会改变。只有与该时间关联的location值会发生变化。与 time.Time 对象关联的location仅具有表示或显示逻辑。

In函数可用于更改与特定time.Time对象关联的location。每当在任何time.Time对象(例如 t)上调用In函数时,

  • 创建了一个表示相同时间瞬间的t副本。

  • t的位置设置为传递给In函数的显示位置。

  • t被返回。

让我们看看下面的代码,它可以用来更改与特定时间相关的地点值。

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    loc, _ := time.LoadLocation("UTC")
    fmt.Printf("UTC Time: %s\n", now.In(loc))

    loc, _ = time.LoadLocation("Europe/Berlin")
    fmt.Printf("Berlin Time: %s\n", now.In(loc))

    loc, _ = time.LoadLocation("America/New_York")
    fmt.Printf("New York Time: %s\n", now.In(loc))

    loc, _ = time.LoadLocation("Asia/Dubai")
    fmt.Printf("Dubai Time: %s\n", now.In(loc))
}

输出:

UTC Time: 2020-01-31 18:09:41.705858 +0000 UTC
Berlin Time: 2020-01-31 19:09:41.705858 +0100 CET
New York Time: 2020-01-31 13:09:41.705858 -0500 EST
Dubai Time: 2020-01-31 22:09:41.705858 +0400 +04

将 Unix 时间戳转换为 Go (Golang) 中的 time.Time

来源:golangbyexample.com/parse-unix-timestamp-time-go/

时间可以在 GO 中以 time.TimeUnix 时间戳 格式表示。Unix 时间戳,也称为纪元时间,是自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的秒数。此时间也称为 Unix 纪元。下面的代码显示了转换过程。

  • time.Time 转 Unix 时间戳

  • Unix 时间戳转 time.Time

package main

import (
    "fmt"
    "time"
)

func main() {
    tNow := time.Now()

    //time.Time to Unix Timestamp
    tUnix := tNow.Unix()
    fmt.Printf("timeUnix %d\n", tUnix)

    //Unix Timestamp to time.Time
    timeT := time.Unix(tUnix, 0)
    fmt.Printf("time.Time: %s\n", timeT)
}

输出:

timeUnix 1257894000
time.Time: 2009-11-10 23:00:00 +0000 UTC

Go 中的 CookieJar(Golang)

来源:golangbyexample.com/cookiejar-golang/

目录

  • 概述

  • 第一个示例

    • 服务器

    • 客户端

  • 第二个示例

    • 服务器

    • 客户端

概述

Golang 中的 HTTP 客户端允许您指定一个CookieJar,用于管理在进行外部 HTTP 请求时的 cookies 存储和发送。如其名所示,可以将其视为一个包含 cookies 的罐子。

golang.org/pkg/net/http/#Client

以下是 net/http Client结构的结构。它包含一个名为Jar的实例变量,其类型为CookieJar接口。

type Client struct {
    Transport RoundTripper

    CheckRedirect func(req *Request, via []*Request) error

    Jar CookieJar

    Timeout time.Duration
}

以下是CookieJar接口。

type CookieJar interface {
    SetCookies(u *url.URL, cookies []*Cookie)
    Cookies(u *url.URL) []*Cookie
}

net/http 提供了一个默认的 cookie jar 实现,符合上述CookieJar接口。我们在初始化 net/http 客户端时将使用它。

golang.org/pkg/net/http/cookiejar/#Jar

您还可以在初始化 net/http Client 结构时提供自定义 cookie jar,该结构实现了上述CookieJar接口。

HTTP 客户端以两种方式使用此 jar。

  • 向此 Jar 中添加 cookies。您可以显式地将 cookies 添加到此 jar 中。如果服务器在响应头中发送 Set-Cookies 头,则这些 cookies 也将被添加到 jar 中。Set-Cookie 头中指定的所有 cookies 都将被添加。

  • 在进行任何外部 HTTP 请求时,查询此 jar。它检查此 jar 以了解需要为特定域发送哪些 cookies。

让我们通过几个示例来说明 cookie jar。

在第一个示例中,客户端将在进行 HTTP 请求时添加一个 cookie。此 cookie 将在后续所有请求中发送到同一域。

在第二个示例中,我们将看到服务器发送Set-Cookie头,该 cookie 将在客户端设置。

第一个示例

在本示例中,我们将看到客户端如何在 cookie jar 中设置 cookie。首先,让我们创建一个服务器,以下是相关程序。

服务器

服务器监听 8080 端口,并有两个 API。

  • localhost:8080/doc

  • localhost:8080/doc/id

在这两个 API 中,我们打印接收到的请求头中的 cookies。

go.mod

module sample.com/server

go 1.16

server.go

package main

import (
	"fmt"
	"net/http"
)

func main() {
	docHandler := http.HandlerFunc(docHandler)
	http.Handle("/doc", docHandler)

	docGetID := http.HandlerFunc(docGetID)
	http.Handle("/doc/id", docGetID)

	http.ListenAndServe(":8080", nil)
}

func docHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Cookie in First API Call")
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	fmt.Println()
	w.WriteHeader(200)
	w.Write([]byte("Doc Get Successful"))
	return
}

func docGetID(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Cookie in Second API Call")
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	w.WriteHeader(200)
	w.Write([]byte("Doc Get ID Successful"))
	return
}

这是客户端代码。

客户端

go.mod

module sample.com/client
go 1.16

client.go

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
)

var client http.Client

func init() {
	jar, err := cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Got error while creating cookie jar %s", err.Error())
	}

	client = http.Client{
		Jar: jar,
	}
}

func main() {
	req, err := http.NewRequest("GET", "http://localhost:8080/doc", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}
	cookie := &http.Cookie{
		Name:   "token",
		Value:  "some_token",
		MaxAge: 300,
	}
	urlObj, _ := url.Parse("http://localhost:8080/")
	client.Jar.SetCookies(urlObj, []*http.Cookie{cookie})
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)

	req, err = http.NewRequest("GET", "http://localhost:8080/doc/id", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}

	resp, err = client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)
}

在上述客户端程序中,我们创建了一个带有 cookie jar 的 HTTP 客户端。

jar, err := cookiejar.New(nil)
if err != nil {
		log.Fatalf("Got error while creating cookie jar %s", err.Error())
}

client = http.Client{
		Jar: jar,
}

我们正在向 Cookie Jar 添加一个 cookie。

cookie := &http.Cookie{
		Name:   "token",
		Value:  "some_token",
		MaxAge: 300,
}
urlObj, _ := url.Parse("http://localhost:8080/")
client.Jar.SetCookies(urlObj, []*http.Cookie{cookie})

现在运行服务器。

go run server.go

和客户端。

go run client.go

注意服务器端的输出。

Cookie in First API Call
token=some_token

Cookie in Second API Call
token=some_token

相同的 cookie 在客户端对服务器的第一次和第二次调用中自动发送。它是如何开箱即用的?这是因为CookieJar参与了这个过程。golang HTTP 客户端在进行 HTTP 调用之前检查 Cookie Jar。然后发送这个 cookie。

第二个示例

在第二个示例中,我们将看到服务器在 Set-Cookie 头中发送的 cookie 如何被保存到CookieJar中。然后它将在后续调用中发送。为了说明这一点,让我们也创建一个将发送 Set-Cookie 头的服务器。以下是服务器代码。

服务器

我们将创建一个服务器,创建两个 API。

  • localhost:8080/doc – 在这个 API 中,服务器将在响应中设置Set-Cookie头。我们将从 golang 程序发起这个调用。golang http 客户端将在其端保存这个 cookie。然后客户端将为对 localhost:8080 的任何其他请求发送相同的 cookie 回到服务器。

  • localhost:8080/doc/id – 这是一个示例 API,用于演示 golang http 客户端如何实际使用CookieJar在请求中发送在 Set-Cookie 头中接收到的相同 cookie。注意在第二个 API 的代码中,我们接收到的所有 cookies。

go.mod

module sample.com/server

go 1.16

server.go

package main

import (
	"fmt"
	"net/http"
)

func main() {
	docHandler := http.HandlerFunc(docHandler)
	http.Handle("/doc", docHandler)

	docGetID := http.HandlerFunc(docGetID)
	http.Handle("/doc/id", docGetID)

	http.ListenAndServe(":8080", nil)
}

func docHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "id",
		Value:  "abcd",
		MaxAge: 300,
	}
	http.SetCookie(w, cookie)
	w.WriteHeader(200)
	w.Write([]byte("Doc Get Successful"))
	return
}

func docGetID(w http.ResponseWriter, r *http.Request) {
	for _, c := range r.Cookies() {
		fmt.Println(c)
	}
	w.WriteHeader(200)
	w.Write([]byte("Doc Get ID Successful"))
	return
}

我们在响应头的Set-Cookie中设置了以下 cookie。

cookie := &http.Cookie{
	Name:   "id",
	Value:  "abcd",
	MaxAge: 300,
}
http.SetCookie(w, cookie)

这是客户端代码。

客户端

go.mod

module sample.com/client
go 1.16

client.go

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/cookiejar"
)

var client http.Client

func init() {
	jar, err := cookiejar.New(nil)
	if err != nil {
		log.Fatalf("Got error while creating cookie jar %s", err.Error())
	}

	client = http.Client{
		Jar: jar,
	}
}

func main() {
	req, err := http.NewRequest("GET", "http://localhost:8080/doc", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)

	req, err = http.NewRequest("GET", "http://localhost:8080/doc/id", nil)
	if err != nil {
		log.Fatalf("Got error %s", err.Error())
	}

	resp, err = client.Do(req)
	if err != nil {
		log.Fatalf("Error occured. Error is: %s", err.Error())
	}
	defer resp.Body.Close()

	fmt.Printf("StatusCode: %d\n", resp.StatusCode)
}

在客户端,我们只是创建了一个指定了 CookieJar 的 HTTP 客户端。除此之外,我们只是进行了两个 API 调用。

现在首先运行服务器。它将在 8080 端口启动一个本地服务器。

go run server.go

现在运行客户端

go run client.go

注意服务器端第二个 API 的输出。可以看到,它是响应头中Set-Cookie返回的相同 cookie。

Printing the cookies in the Second API
id=abcd

这是它开箱即用的工作方式。

  • 在第一次 API 调用中,golang http 客户端将Set-Cookie响应头中存在的所有 cookies 保存到CookieJar中。

  • 在进行第二次 API 调用之前,它会检查CookieJar以确定需要发送给服务器的所有 cookies。然后,它会发送这些 cookies。

这就是关于 golang 中 cookie jar 的全部内容。希望你喜欢这个教程。另请查看我们的 Golang 高级教程系列 – Golang 高级教程

posted @ 2024-10-19 08:37  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报