通过示例学习-Go-语言-2023-三十五-

通过示例学习 Go 语言 2023(三十五)

理解 Go 中的如果-否则语句(Golang)

来源:golangbyexample.com/understand-if-else-statement-golang/

这是 golang 综合教程系列的第十二章。有关该系列其他章节,请参考此链接——Golang 综合教程系列

下一个教程——开关

上一个教程——范围循环

现在让我们查看当前教程。以下是当前教程的目录。

概述

Go 有类似于其他编程语言的 if-else 语句,以执行基本的条件逻辑。以下是 golang 中 if-else 语句的格式。

if condition {
   //Do something
} else if condition {
   //Do something
} else {
   //Do something

在我们进一步讨论之前,先谈谈条件。条件中只允许语句或结果为布尔值的语句组合。在 Go 中,false 布尔值在条件中被视为假,true 布尔值被视为真。如上所述,条件可以由多个语句通过运算符组合而成,例如&&、||、>、<、>=、<=、!等。

现在让我们详细看看 if-else 语句,以理解其中的小细节。Go 支持以下格式的 if-else 语句。

  • 仅仅 if

  • 如果-否则

  • 如果-否则阶梯

  • 嵌套 if-else

  • 如果与简短语句

如果语句

if 语句单独具有以下格式。

if condition {
   //Do something
}

如果条件为真,则执行括号内的语句。关于 if 语句需要注意的一些要点。

  • 可以省略条件周围的括号。

  • 条件后的开闭括号是强制的。

让我们看一个工作示例。以下程序检查一个数字是否大于 5。

package main

import "fmt"

func main() {
    a := 6
    if a > 5 {
        fmt.Println("a is greater than 5")
    }
}

输出

a is greater than 5

让我们看另一个 if 条件中的多个语句的示例。以下是一个程序,用于检查一个数字是否在特定范围内。注意条件中的多个语句由&&运算符连接。

package main

import "fmt"

func main() {
    a := 4
    if a > 3 && a < 6 {
        fmt.Println("a is within range")
    }
}

输出

a is within range

如果-否则语句

如果-否则语句具有以下格式。

if condition {
   //Do something
} else {
   //Do something
}

如果条件为真,则执行 if 块中的语句,否则执行 else 块中的语句。关于 if-else 语句需要注意一些要点。

  • else 关键字应该与其闭合括号在同一行。如果不在同一行,将会出现以下编译器错误。
syntax error: unexpected else, expecting }

让我们看一个小的 if-else 语句示例。在下面的程序中,我们使用 if-else 语句找出两个数字中的最大值。

package main

import "fmt"

func main() {
    a := 1
    b := 2

    if a > b {
        fmt.Println("a is greater than b")
    } else {
        fmt.Println("b is greater than a")
    }
}

输出

b is greater than a

如果-否则阶梯

如果-否则阶梯具有以下格式。

if condition1 {
   //Do something
} else if condition2 {
   //Do something
} else {
  //Do something
}

关于这个 if-else 阶梯需要注意的一些要点。

  • 可以在中间添加任意数量的 else if 语句。

  • else if 应位于前一个闭合括号的同一行。

以下是一个可工作的代码示例。该代码根据年龄使用 if-else 阶梯来判断一个人是“孩子”、“年轻”还是“老年”。

package main

import "fmt"

func main() {
    age := 29
    if age < 18 {
        fmt.Println("Kid")
    } else if age >= 18 && age < 40 {
        fmt.Println("Young")
    } else {
        fmt.Println("Old")
    }
}

输出:

Young

嵌套如果否则

下面是嵌套 if else 的一些可能格式。

仅嵌套 if

if condition {
  //Do something
  if condition2 { 
    //Do something
  }
  //Do something
}

嵌套 if else

if condition1 {
   //....
   if condition2 {
      //...
   } else {
      //...
   }
  //...
}

下面的组合也可以用于嵌套的 if else。

if condition1 {
   //...
} else {
   //...
   if condition2 {
      //...
   } else {
     //....
   }
   //....
}

让我们看一个嵌套 if else 的工作示例。在下面的程序中,我们使用嵌套 if else 打印三个数字中的最大值。

package main

import "fmt"

func main() {
    a := 1
    b := 2
    c := 3
    if a > b {
        if a > c {
            fmt.Println("Biggest is a")
        } else if b > c {
            fmt.Println("Biggest is b")
        }
    } else if b > c {
        fmt.Println("Biggest is b")
    } else {
        fmt.Println("Biggest is c")
    }
}

输出:

Biggest is c

带短语句的 if

If 语句也支持在条件前添加语句。这个语句将在条件之前执行。语句中还可以有新的初始化变量。下面是该格式。

if statement; condition {
   //Do something
}

如果语句中存在初始化,它将是一个短声明。注意,在语句中不支持 var 关键字。让我们看一个工作示例。

package main

import "fmt"

func main() {
    if a := 6; a > 5 {
        fmt.Println("a is greater than 5")
    }
}

输出

a is greater than 5

在 if 语句中初始化的变量在所有分支中都可用。如下面示例中,变量 a 在 else 块中也可用。

package main

import "fmt"

func main() {
    if a := 1; a > 5 {
        fmt.Println("a is greater than 5")
    } else {
        fmt.Println("a is less than 5")
    }
}

输出:

a is less than 5

if 条件

我们在开头提到过,if 条件中只允许布尔值或结果为布尔值的语句。让我们看看使用非布尔值时出现的错误的工作代码。

package main

import "fmt"

func main() {
    if 1 {
        fmt.Println("a is greater than 5")
    }
}

输出: 下面的编译器错误被抛出

non-bool 1 (type int) used as if condition

三元运算符

Go 中没有三元运算符,因此你需要使用 if else 语句来替代它。

结论

这就是 Go 语言中 if else 语句的全部内容。希望你喜欢这篇文章。请在评论中分享反馈/改进/错误。

下一个教程Switch

上一个教程范围循环

理解 Go (Golang) 中的 := 符号或短变量声明。

来源:golangbyexample.com/short-variable-declaration-go/

Go 提供了另一种声明变量的方法,即使用 := 操作符。当使用 := 操作符时,可以省略 var 关键字和类型信息。以下是这种声明的格式。

{var_name} := {value}

类型推断将根据值的类型进行。同时,请参阅此文章以获取有关类型推断的详细信息。让我们看看一个工作示例。在下面的代码中。

  • m2 的类型被正确推断为 int,因为赋给它的值是 123,而 123 是 int 类型。

  • 同样,n2 的类型也被正确推断为字符串,因为赋给它的值是 “circle”,它是一个 string,依此类推。

  • 另外,请注意变量 t2 的类型被正确推断为结构体 main.sample

  • u2的类型被正确推断为[]string,因为这是get()函数调用返回的类型。

package main

import "fmt"

func main() {
    m2 := 123                   //Type Inferred will be int
    n2 := "circle"              //Type Inferred will be string
    o2 := 5.6                   //Type Inferred will be float64
    p2 := true                  //Type Inferred will be bool
    q2 := 'a'                   //Type Inferred will be rune
    r2 := 3 + 5i                //Type Inferred will be complex128
    s2 := &sample{name: "test"} //Type Inferred will be pointer to main.sample
    t2 := sample{name: "test"}  //Type Inferred will be main.sample
    u2 := get()                 //Type inferred will []string as that is the return value of function get()

    fmt.Println("")
    fmt.Printf("m2: Type: %T Value: %v\n", m2, m2)
    fmt.Printf("n2: Type: %T Value: %v\n", n2, n2)
    fmt.Printf("o2: Type: %T Value: %v\n", o2, o2)
    fmt.Printf("p2: Type: %T Value: %v\n", p2, p2)
    fmt.Printf("q2: Type: %T Value: %v\n", q2, q2)
    fmt.Printf("r2: Type: %T Value: %v\n", r2, r2)
    fmt.Printf("s2: Type: %T Value: %v\n", s2, s2)
    fmt.Printf("t2: Type: %T Value: %v\n", t2, t2)
    fmt.Printf("u2: Type: %T Value: %v\n", u2, u2)
}

type sample struct {
    name string
}

func get() []string {
    return []string{"a", "b"}
}

输出

m2: Type: int Value: 123
n2: Type: string Value: circle
o2: Type: float64 Value: 5.6
p2: Type: bool Value: true
q2: Type: int32 Value: 97
r2: Type: complex128 Value: (3+5i)
s2: Type: *main.sample Value: &{test}
t2: Type: main.sample Value: {test}
u2: Type: []string Value: [a b]

关于 := 操作符需要注意的一些要点。

  • := 操作符仅在函数内部可用,函数外部不允许使用。

  • 使用 := 声明的变量一旦被声明,就不能再次使用 := 操作符进行重新声明。因此,下面的语句将引发编译器错误“左侧没有新变量在 :=”。

a := 8
a := 16
  • := 操作符还可以用于在一行中声明多个变量。请参见下面的示例。
a,b := 1, 2
  • 在多个声明的情况下,如果左侧至少有一个变量是新的,则可以再次使用 := 来声明特定变量。请参见下面的示例。注意,b 再次使用 := 被声明。这只有在至少有一个变量是新的(这里是变量 c)的情况下才可能。在这种情况下,它作为变量 b 的赋值。
package main

import "fmt"

func main() {
    a, b := 1, 2
    b, c := 3, 4
    fmt.Println(a, b, c)
}

输出:

1, 3, 4

理解 Go 中的数组(Golang)– 完整指南

来源:golangbyexample.com/understanding-array-golang-complete-guide/

这是 golang 综合教程系列的第十七章。有关该系列其他章节,请参考此链接 – Golang 综合教程系列

下一个教程切片

前一个教程结构体

现在让我们查看当前的教程。以下是当前教程的目录。

目录

概述

+   **定义**
  • 数组声明

  • 访问数组元素

  • 数组在 go 中是值

  • 迭代数组的不同方法

  • 多维数组

  • 结论 # 概述

类似于其他编程语言,golang 也有数组数据结构。但在 go 中,数组的行为与其他语言略有不同,我们还有一种称为切片的东西,它类似于数组的引用。在本文中,我们将仅研究数组。

定义

数组是同一类型元素的连续集合。它是存储在内存中连续的元素有序序列。

这是数组声明的格式。

sample := [size_of_array]{type}{a1, a2... an}
  • size_of_array – 数组中的元素数量

  • 是数组中每个元素的类型。

  • a1, a2 … an 是实际元素。

在 golang 中,数组的大小是其类型的一部分。这意味着两个具有不同元素数量的数组是两种不同的类型,不能相互赋值。如果尝试赋值两个不同长度的数组,将会引发下面的错误。

cannot use sample1 (type [1]int) as type [2]int in assignment

代码如下:

sample1 := [1]int{1}
sample2 := [2]int{1,2}

sample2 = sample1

由于同样的原因,数组的长度在创建时是固定的,不能在后续更改。

数组声明

数组声明中元素的数量和实际元素都是可选的。

在下面的例子中,我们看到声明数组的 4 种方式。

  • 同时指定数组的长度和实际元素。例如:
[2]int{1, 2}
  • 仅长度 – 在这种情况下,所有实际元素都填充为该类型的默认值零。例如:
[2]int{}
  • 仅实际元素 – 在这种情况下,数组的长度将等于实际元素的数量。当不指定长度时,符号‘…’需要在方括号内使用,格式为[…]。该符号是对编译器的指令,以计算长度。
[...]int{2, 3}
  • 如果没有长度和实际元素——在这种情况下,将创建一个空数组。与上面类似,符号 ‘…’ 在这种情况下也需要使用。
[...]int{}

让我们看一个代码示例来说明上述要点。还请记住,内置函数 len() 可以用来计算数组的长度。在下面的程序中,我们使用 len() 函数来计算数组的长度。

package main

import "fmt"

func main() {
    //Both number of elements and actual elements
    sample1 := [2]int{1, 2}
    fmt.Printf("Sample1: Len: %d, %v\n", len(sample1), sample1)

    //Only actual elements
    sample2 := [...]int{2, 3}
    fmt.Printf("Sample2: Len: %d, %v\n", len(sample2), sample2)

    //Only number of elements
    sample3 := [2]int{}
    fmt.Printf("Sample3: Len: %d, %v\n", len(sample3), sample3)

    //Without both number of elements and actual elements
    sample4 := [...]int{}
    fmt.Printf("Sample4: Len: %d, %v\n", len(sample4), sample4)
}

输出

Sample1: Len: 2, [1 2]
Sample2: Len: 2, [2 3]
Sample3: Len: 2, [0 0]
Sample4: Len: 0, []

注意在上面的示例中,对于 sample3 变量,实际元素填充了 int 的默认值,即 0。

如果实际指定的元素少于数组的长度,也是可以的。其余元素将用指定类型的默认值填充。请参见下面的示例。指定的数组长度为 4,而只声明了 2 个实际元素。因此,其余两个元素被赋值为 0,这是 int 的默认零值。

package main

import "fmt"

func main() {
    sample := [4]int{5, 8}
    fmt.Printf("Sample: Len: %d, %v\n", len(sample), sample)
}

输出

Sample: Len: 4, [5 8 0 0]

访问数组元素

由于数组元素是以连续方式存储的,我们可以使用索引访问数组元素。同样,单个数组元素也可以使用索引赋值。访问越界索引将导致编译错误。请参见下面的示例以说明这些要点。第一个索引位置将是 ,最后一个索引将是 (length_of_array-1)

package main

import "fmt"

func main() {
    sample := [2]string{"aa", "bb"}

    fmt.Println(sample[0])
    fmt.Println(sample[1])

    sample[0] = "xx"
    fmt.Println(sample)
    //sample[3] = "yy"
}

输出

aa
bb
[xx bb]

取消注释下面的行

sample[3] = "yy"

, 这将导致编译错误

invalid array index 3 (out of bounds for 2-element array)

数组在 Go 中是值

在 Go 中,数组是值类型。因此,数组变量名称并不是指向第一个元素的指针,而是表示整个数组。当

  • 一个数组变量被赋值给另一个数组变量。

  • 一个数组变量作为参数传递给一个函数。

让我们用另一个示例来看上述要点

package main

import "fmt"

func main() {
    sample1 := [2]string{"a", "b"}
    fmt.Printf("Sample1 Before: %v\n", sample1)
    sample2 := sample1
    sample2[0] = "c"
    fmt.Printf("Sample1 After assignment: %v\n", sample1)
    fmt.Printf("Sample2: %v\n", sample2)
    test(sample1)
    fmt.Printf("Sample1 After Test Function Call: %v\n", sample1)
}
func test(sample [2]string) {
    sample[0] = "d"
    fmt.Printf("Sample in Test function: %v\n", sample)
}

输出

Sample1 Before: [a b]
Sample1 After assignment: [a b]
Sample2: 
Sample in Test function: [d b]
Sample1 After Test Function Call: [a b]

在上述示例中,

  • 我们将 sample1 赋值给 sample2,然后将 sample2 的 0 索引位置更改为不同的值。之后,当我们打印 sample1 时,会发现它没有改变。这是因为当我们将 sample1 赋值给 sample2 时,创建了一个副本,改变 sample2sample1 没有任何影响。

  • 我们将 sample1 传递给测试函数,然后在测试函数中将其在 0 索引位置的值更改。之后,当我们打印 sample1 时,会发现它没有改变。原因是,当 sample1 作为参数传递给测试函数时,会创建 sample1 的一个副本。

迭代数组的不同方法

数组可以使用以下方式迭代:

  • 使用 for 循环

  • 使用 for-range 循环

让我们看一个两者的代码示例

package main

import "fmt"

func main() {
    letters := [3]string{"a", "b", "c"}
    //Using for loop
    fmt.Println("Using for loop")
    len := len(letters)
    for i := 0; i < len; i++ {
        fmt.Println(letters[i])
    }
    //Using for-range operator
    fmt.Println("\nUsing for-range loop")
    for i, letter := range letters {
        fmt.Printf("%d %s\n", i, letter)
    }
}

输出

Using for loop
a
b
c

Using for-range loop
0 a
1 b
2 c

多维数组

下面是声明二维数组的格式

sample := [x][y]{type}{{a11, a12 .. a1y},
                       {a21, a22 .. a2y},
                       {.. },
                       {ax1, ax2 .. axy}} 

其中

  • x 表示行数

  • y 表示列数

  • aij 表示位于第 i 行和第 j 列的元素

同样的思路可以扩展到三维、四维等更多维度。我们上面讨论的所有规则也适用于多维数组。

让我们看一个代码示例

package main

import "fmt"

func main() {
    sample := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    fmt.Println("First Run")
    for _, row := range sample {
        for _, val := range row {
            fmt.Println(val)
        }
    }

    sample[0][0] = 6
    sample[1][2] = 1
    fmt.Println("\nSecond Run")
    for _, row := range sample {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

输出

First Run
1
2
3
4
5
6

Second Run
6
2
3
4
5
1

在上面的示例中,我们使用索引访问二维数组的元素,适用于第一维和第二维。

sample[0][0] = 6

还要注意我们是如何遍历二维数组的。我们需要使用嵌套的 range。第一个 range 遍历数组中的数组,第二个 range 在此之后遍历单个数组。

结论

这篇文章全部讲的是 Golang 中的数组。希望你喜欢这篇文章。请在评论中分享反馈/改进/错误。

下一篇教程 – 切片

上一篇教程 – 结构体

理解 Go(Golang)中的时间包中的持续时间

来源:golangbyexample.com/understanding-duration-go/

duration 是两个时刻之间经过的时间。它表示为 int64 纳秒 计数。因此,持续时间在 Go 中只是一个表示纳秒的数字。如果持续时间值等于 1000000000,则表示 1 秒1000 毫秒10000000000 纳秒。由于持续时间是 int64, 因此可以表示的最长持续时间为 290 年。因此,持续时间用于捕获两个 time.Time 对象之间的纳秒数。例如,两个时间值相隔 1 小时的持续时间将是以下值,相当于 1 小时中的纳秒数。

1 *60*60*1000*1000*1000

time 包中表示如下。

type Duration int64

以下是 time 包中定义的一些常见持续时间

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

一些在 time.Time 对象上定义的返回 Duration 的函数是

  • func (t Time) Sub(u Time) Duration – 它返回持续时间 t-u。

  • func Since(t Time) Duration – 它返回自 t 以来经过的持续时间。

  • func Until(t Time) Duration – 它返回到 t 的持续时间。

类型 Duration 还定义了几个实用函数,可用于将持续时间值转换为-

  • 小时

  • 分钟

持续时间的打印值表示为“10h8m0.5s”的形式,其中 h 代表 小时m 代表 分钟s 代表

让我们看看到目前为止我们所学内容的示例代码。

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    oldTime := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
    diff := now.Sub(oldTime)
    //diff is of type Duration now

    //In hours
    fmt.Printf("Hours: %f\n", diff.Hours())

    //In minutes
    fmt.Printf("Minutes: %f\n", diff.Minutes())

    //In seconds
    fmt.Printf("Seconds: %f\n", diff.Seconds())

    //time Since
    fmt.Println(time.Since(now.Add(-time.Hour * 1)))

    //time until
    fmt.Println(time.Until(now.Add(time.Hour * 1)))
}

输出:

Hours: 714.565222
Minutes: 42873.913306
Seconds: 2572434.798350
1h0m0.000093629s
59m59.999888401s

理解 Go 中的 Errorf 函数

来源:golangbyexample.com/errorf-function-golang/

目录

  • 概述

    • 格式化字符串变量

    • 格式化一个整数

    • 格式化结构体

  • 错误的封装

概述

Errorf函数在fmt包中定义,用于根据提供的格式说明符创建带有格式化消息的自定义错误。

golang.org/pkg/fmt/#Errorf

  • 它的主要用途是用格式化消息在 Golang 中创建自定义错误。

  • 它还用于创建可以封装另一个提供给它的错误的自定义错误。在 Golang 中,一个错误可以封装另一个错误。错误的封装是什么意思?这意味着创建一个错误的层次结构,其中特定的错误实例封装另一个错误,而该特定实例本身也可以被封装在另一个错误中。我们将在本教程后面详细讨论。

以下是Errorf的函数原型

func Errorf(format string, a ...interface{}) error

如你所见,它返回一个错误的实例。

Errorf也是一个可变参数函数,这意味着它可以有多个参数。关于其参数列表有两个重要点

  • 请注意第一个参数是一个格式模板字符串。

  • 下一个是可变数量的参数。此列表中的每个参数可以是字符串、整数、结构体或其他类型。这就是它是一个空接口的原因。

Errrof使用自定义说明符格式化字符串。第一个参数是格式模板字符串,包含需要格式化的实际字符串以及一些格式化动词。这些格式化动词告诉后续参数在最终字符串中如何格式化。因此,基本上格式字符串参数包含某些符号,这些符号由后续参数替换。

例如

格式化字符串变量

  • %s符号被使用

  • 示例

name := "J"
fmt.Errorf("Name has less then 3 character. Name: %s\n", name)

格式化一个整数

  • %d符号被使用

  • 示例

age := 0
fmt.Errorf("Age is 0: Age:%d\n", age)

格式化结构体

例如,有三种格式说明符用于打印结构体。

  • %v – 只打印值,字段名不会被打印。这是使用 Println 打印结构体的默认方式

  • **%+v – **它将打印字段和对应的值。

  • **%#v – **它将打印结构体,同时显示字段名和值

这就是为什么

fmt.Errorf("Employee not found. Details: %v\n", e)
fmt.Errorf("Employee not found. Details: %+v\n", e)
fmt.Errorf("Employee not found. Details: %#v\n", e)

返回的错误信息如下格式化消息

这是相同功能的工作程序

package main

import (
	"fmt"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	sampleErr := "database connection issue"

	err := fmt.Errorf("Err is: %s", sampleErr)
	fmt.Println(err)

	port := 8080

	err = fmt.Errorf("Err is: %s to port %d", sampleErr, port)
	fmt.Println(err)

	e := employee{
		Name: "John",
	}

	err = fmt.Errorf("Employee not found. Details %v", e)
	fmt.Println(err)
	err = fmt.Errorf("Employee not found. Details %+v", e)
	fmt.Println(err)
	err = fmt.Errorf("Employee not found. Details %#v", e)
	fmt.Println(err)
}

输出

Err is: database connection issue
Err is: database connection issue to port 8080
Employee not found. Details {John 0}
Employee not found. Details {Name:John Age:0}
Employee not found. Details main.employee{Name:"John", Age:0}

请注意下面的Errorf

err = fmt.Errorf("Err is: %s to port %d", sampleErr, port)
  • %ssampleErr替换。

  • %dport替换。

所以基本上格式字符串参数中的符号或动词按顺序被后续参数替换

如果格式字符串中的格式说明符数量与后续的变量参数数量不匹配,则格式说明符将原样打印。例如,在下面的代码中,我们有两个格式说明符。

  • %s

  • %d

而下一个变量参数的数量仅为一个。因此,当我们格式化它时,它返回带有第二个格式说明符的格式化错误,原样显示 MISSING 作为警告。

package main

import "fmt"

func main() {
	name := "John"

	err := fmt.Errorf("Employee not found with name: %s and age %d", name)
	fmt.Println(err)
}

输出

Employee not found with name: John and age %!d(MISSING)

错误的封装

以下是封装错误的语法。

e := fmt.Errorf("... %w ...", ..., err, ...)

%w指令用于封装错误。fmt.Errorf应该仅使用一个%w 指令调用。让我们看一个例子。

package main

import (
	"fmt"
)

type errorOne struct{}

func (e errorOne) Error() string {
	return "Error One happened"
}

func main() {

	e1 := errorOne{}

	e2 := fmt.Errorf("E2: %w", e1)

	e3 := fmt.Errorf("E3: %w", e2)

	fmt.Println(e2)

	fmt.Println(e3)

}

输出

E2: Error One happened
E3: E2: Error One happened

在上面的程序中,我们创建了一个名为errorOne的结构体,它具有一个Error方法,因此它实现了error接口。然后我们创建了一个名为e1errorOne结构体实例。接着我们将该实例e1封装到另一个错误e2中,如下所示。

e2 := fmt.Errorf("E2: %w", e1)

然后我们将e2封装到e3中,如下所示。

e3 := fmt.Errorf("E3: %w", e2)

因此我们创建了一个错误层次结构,其中e3封装了e2,而e2封装了e1。因此,e3也以传递方式封装了e1。当我们打印e2时,它也会打印来自e1的错误并给出输出。

E2: Error One happened

当我们打印e3时,它打印来自e2以及e1的错误并给出输出。

E3: E2: Error One happened

你可以在这里详细了解错误的封装和解封装。

golangbyexample.com/wrapping-and-unwrapping-error-golang/

这就是关于 Errorf 函数的所有内容。希望你喜欢这篇文章。请在评论中分享反馈。同时,查看我们的 Golang 高级教程系列 – Golang 高级教程

理解 Go 中的 Fprint 函数(Golang)

来源:golangbyexample.com/fprint-golang/

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

目录

  • 概述

  • 程序

概述

Fprintfmt包中定义,用于使用默认格式说明符格式化字符串,并将其写入传递给它的io.Writer实例。

golang.org/pkg/fmt/#Fprint

下面是Fprint的函数原型

func Fprint(w io.Writer, a ...interface{}) (n int, err error)

Fprint也是一个可变参数函数,这意味着它可以有多个参数。以下是有关其参数的详细信息

  • 第一个参数是写入的io.Writer实例

  • 接下来的参数是可变数量的参数。此列表中的每个参数可以是字符串、整数、结构体或其他任何东西。这就是为什么它是一个空接口。每个参数都使用默认说明符进行格式化

Fprint使用默认格式说明符格式化字符串,但在字符串后不添加新行。Fprint在第一个参数后接受可变数量的参数,其中每个参数都是一个空接口。由于参数类型是空接口,我们可以将任何数据类型传递给它。我们可以传递字符串、整数、浮点数、结构体或任何其他数据类型。传递给Fprint函数的每个参数都根据该参数类型的默认格式说明符进行格式化。例如,结构体将根据以下说明符进行格式化

%v

这个格式说明符只打印结构中的值部分。fmt包中还有一个函数可以添加新行 – Fprintln

程序

让我们来看一个例子

package main

import (
	"fmt"
	"log"
	"os"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21

	fmt.Fprint(os.Stdout, "Name is:", name, "\n")
	fmt.Fprint(os.Stdout, "Age is:", age, "\n")

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Fprint(os.Stdout, e, "\n")

	fmt.Fprint(os.Stdout, "a", 12, "b", 12.0, "\n")

	fmt.Fprint(os.Stdout, 12, 12.0, "\n")

	bytesPrinted, err := fmt.Fprint(os.Stdout, "Name is: ", name, "\n")
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Println(bytesPrinted)
}

输出

Name is:John
Age is:21
{John 21}
a12b12
12 12
Name is: John
14

关于Fprint函数的一些重要点

  • 在上面的所有Fprint函数中,我们将os.Stdout的实例传递给它,该实例实现了io.Writer接口。基本上,使用os.StdoutFprint写入标准输出。这就是os.Stdout的定义。
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
  • 它不会在末尾添加新行。这就是为什么需要使用“\n”来添加新行的原因。

  • 只有当两个参数都是非字符串时,它才会在这两个参数之间添加空格。这就是为什么

fmt.Fprint(os.Stdout, 12, 12.0, "\n")

打印

12 12

while

fmt.Fprint(os.Stdout, "a", 12, "b", 12.0, "\n")

打印

a12b12
  • 它还返回打印的字符数以及发生的任何错误
bytesPrinted, err := fmt.Fprint(os.Stdout, "Name is: ", name, "\n")
if err != nil {
    log.Fatalln("Error occured", err)
}
fmt.Fprint(bytesPrinted)

将输出以下内容

Name is: John
14

bytesPrinted的数量为 14,因为输出了 14 个字符

Fprint也可以用于写入文件。由于在 golang 中的文件实例实现了io.Writer,这不是问题。下面是相应的程序

package main
import (
    "fmt"
    "log"
    "os"
)
type employee struct {
    Name string
    Age  int
}
func main() {
    file, err := os.Create("./temp.txt")
    if err != nil {
        log.Fatal(err)
    }
    name := "John"
    age := 21
    fmt.Fprint(file, "Name is:", name, "\n")
    fmt.Fprint(file, "Age is:", age, "\n")
    e := employee{
        Name: name,
        Age:  age,
    }
    fmt.Fprint(file, e, "\n")
    fmt.Fprint(file, "a", 12, "b", 12.0, "\n")
    fmt.Fprint(file, 12, 12.0, "\n")
}

输出

它将在当前目录中创建文件名 temp.txt,内容如下。在这个程序中,我们用创建的文件替换了os.Stdout

Name is:John
Age is:21
{John 21}
a12b12
12 12

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

理解 Go(Golang)中的 Fprintf 函数

来源:golangbyexample.com/fprintf-golang/

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

目录

  • 概述

    • 格式化字符串变量

    • 格式化整数

    • 格式化结构体

  • 程序

概述

Fprintffmt包中定义,用于格式化字符串并将格式化后的字符串写入传递给它的io.Writer实例。

golang.org/pkg/fmt/#Fprint

Golang 提供了另一个类似的函数PrintfFprintfPrintf之间唯一的区别是,Fprintf将数据写入传递给它的io.Writer实例,而Printf函数则写入标准输出。

以下是Fprintf的函数原型

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

Fprintf也是一个可变参数函数,意味着它可以有多个参数。以下是关于其参数的详细信息

  • 第一个参数是要写入的io.Writer实例

  • 第二个参数是一个formattemplate字符串。该字符串包含自定义说明符,如%s用于string%d用于int等。

  • 接下来是一个可变数量的参数。这个列表中的每个参数可以是字符串、整数、结构体或其他任何类型。这就是为什么它是一个空接口。

Fprintf使用自定义说明符格式化字符串。formattemplate字符串包含需要格式化的实际字符串及一些格式动词。这些格式动词指示如何在最终字符串中格式化后续参数。因此,格式字符串参数包含某些符号,这些符号将被后续参数替换。

还需要注意的是Fprintf函数不会添加新行。我们需要使用新行标识符来添加新行“\n”

示例

格式化字符串变量

  • %s符号被使用

  • 示例

name := "John"
fmt.Fprintf(os.Stdout, "Name is: %s\n", name)

输出将如下所示。它将写入os.Stdout,即标准输出。请注意,我们将os.Stdout作为第一个参数传递。

Name is John

格式化整数

  • %d符号被使用

  • 示例

age := 21
fmt.Fprintf(os.Stdout, "Age is: %d\n", age)

输出将如下所示。它将写入os.Stdout,即标准输出。

格式化结构体

例如,打印结构体时有三种格式说明符。

  • %v – 它只会打印值。字段名称不会被打印。这是使用 Println 打印结构体时的默认方式。

  • **%+v – **它将打印字段和值。

  • **%#v – **它将打印结构体,包括字段名称和值

这就是为什么

fmt.Fprintf(os.Stdout, "Employee is %v\n", e)
fmt.Fprintf(os.Stdout, "Employee is %+v\n", e)
fmt.Fprintf(os.Stdout,"Employee is %#v\n", e)

分别写入以下内容到os.Stdout实例

Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}

这与上述解释相符。此外,请注意,此函数返回打印的字符数和任何发生的错误。它不会添加新行。你需要明确地添加“\n”

程序

这是相应的工作程序。

package main

import (
	"fmt"
	"log"
	"os"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21

	fmt.Fprintf(os.Stdout, "Name is: %s\n", name)
	fmt.Fprintf(os.Stdout, "Age is: %d\n", age)

	fmt.Fprintf(os.Stdout, "Name: %s Age: %d\n", name, age)

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Fprintf(os.Stdout, "Employee is %v\n", e)
	fmt.Fprintf(os.Stdout, "Employee is %+v\n", e)
	fmt.Fprintf(os.Stdout, "Employee is %#v\n", e)

	bytesPrinted, err := fmt.Fprintf(os.Stdout, "Name is: %s\n", name)
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Println(bytesPrinted)
}

输出

Name is: John
Age is: 21
Name: John Age: 21
Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}
Name is: John
14

在上面的所有Fprintf函数中,我们将os.Stdout的实例传递给它,该实例实现了io.Writer接口。基本上,使用os.Stdout时,Fprintf写入标准输出。这就是os.Stdout的定义。

Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")

注意下面的Fprintf

fmt.Fprintf(os.Stdout, "Name: %s Age: %d\n", name, age)
  • %s被名字替换。

  • %d被年龄替换。

所以基本上,格式字符串参数中的符号或动词按照顺序被后续参数替换。

Fprintf也可以用于写入文件。由于文件实例在 golang 中实现了io.Writer,因此这不是问题。下面是相应的程序。

package main
import (
    "fmt"
    "log"
    "os"
)
type employee struct {
    Name string
    Age  int
}
func main() {
    name := "John"
    age := 21
    file, err := os.Create("./temp.txt")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Fprintf(file, "Name is: %s\n", name)
    fmt.Fprintf(file, "Age is: %d\n", age)
    fmt.Fprintf(file, "Name: %s Age: %d\n", name, age)
    e := employee{
        Name: name,
        Age:  age,
    }
    fmt.Fprintf(file, "Employee is %v\n", e)
    fmt.Fprintf(file, "Employee is %+v\n", e)
    fmt.Fprintf(file, "Employee is %#v\n", e)
}

输出

它将在当前目录中创建文件名temp.txt,并包含以下内容。在此程序中,我们将os.Stdout替换为创建的文件。

Name is: John
Age is: 21
Name: John Age: 21
Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}

如果格式字符串中的格式说明符数量与下一个可变参数的数量不匹配,则格式说明符将原样打印。例如,在下面的代码中,我们有两个格式说明符。

  • %d

  • %s

而下一个可变参数只有一个。因此,当我们调用它时,它将把第二个格式说明符原样写出,并发出 MISSING 警告。

package main

import (
	"fmt"
	"os"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"

	fmt.Fprintf(os.Stdout, "Name is: %s %d\n", name)
}

输出

Name is: John %!d(MISSING)

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

理解 Go(Golang)中的 Fprintln 函数

来源:golangbyexample.com/fprintln-golang/

![fprintln 图像](https://gitee.com/OpenDocCN/geekdoc-golang-zh/raw/master/docs/go-exam-2023/img/3cd9912b7a0733f8200683fac3715644.png)

目录

  • 概述

  • 程序

概述

Fprintln定义在fmt包中,用于使用默认格式说明符格式化字符串,并将其写入传递给它的io.Writer实例。它还添加了一个新行。

golang.org/pkg/fmt/#Fprintln

下面是Fprintln的函数原型

func Fprintln(w io.Writer, a ...interface{}) (n int, err error)

Fprintln也是一个可变参数函数,这意味着它可以有多个参数。这里是关于其参数的详细信息。

  • 第一个参数是要写入的io.Writer实例

  • 接下来是可变数量的参数。这个列表中的每个参数可以是字符串、整数、结构体或其他任何东西。这就是它是一个空接口的原因。每个参数都使用默认说明符进行格式化。

Fprintln使用默认格式说明符格式化字符串,并在字符串后添加新行。Fprintln接受可变数量的参数,每个参数都是一个空接口。它返回打印的字符数量和发生的任何错误。由于参数类型是空接口,我们可以传递任何数据类型。我们可以传递字符串、整数、浮点数、结构体或任何其他数据类型。每个传递给Fprintln函数的参数都根据该参数类型的默认格式说明符进行格式化。例如,结构体将根据以下说明符进行格式化。

fmt包中还有另一个函数Fprint,与Fprintln相同。唯一的区别是

  • Fprintln在末尾添加新行,而Fprint则不添加新行。
%v

该格式说明符仅打印结构体中的值部分。让我们看一个例子。

程序

package main

import (
	"fmt"
	"log"
	"os"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21
	fmt.Fprintln(os.Stdout, "Name is: ", name)
	fmt.Fprintln(os.Stdout, "Age is: ", age)
	e := employee{
		Name: name,
		Age:  age,
	}
	fmt.Fprintln(os.Stdout, e)
	fmt.Fprintln(os.Stdout, "a", 12, "b", 12.0)

	bytesPrinted, err := fmt.Fprintln(os.Stdout, "Name is: ", name)
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Println(bytesPrinted)
}

输出

Name is:  John
Age is:  21
{John 21}
a 12 b 12
Name is:  John
15

关于FPrintln函数的一些重要事项

  • 在上述所有Fprintln函数中,我们将os.Stdout的实例传递给它,该实例实现了io.Writer接口。基本上,使用os.StdoutFprintln写入标准输出。这就是os.Stdout的定义。
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
  • 它在末尾添加一个新行。这就是为什么每个输出在不同的行上

  • 输出中每个参数将以空格分隔。这就是为什么

fmt.Fprintln(os.Stdout,"Name is: ", name)

打印

Name is: John

在两个参数之间自动引入空格。

  • 它返回打印的字符数量或发生的任何错误
bytesPrinted, err := fmt.Fprintln(os.Stdout, "Name is: ", name)
if err != nil {
    log.Fatalln("Error occured", err)
}
fmt.Println(bytesPrinted)

将输出如下

Name is: John
14

bytesPrinted的数量是 14,因为输出了 14 个字符

Fprintln还可以用于写入文件。由于文件实例在 Golang 中实现了io.Writer,这不是问题。以下是相应的程序

package main

import (
	"fmt"
	"log"
	"os"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	file, err := os.Create("./temp.txt")
	if err != nil {
		log.Fatal(err)
	}

	name := "John"
	age := 21
	fmt.Fprintln(file, "Name is: ", name)
	fmt.Fprintln(file, "Age is: ", age)
	e := employee{
		Name: name,
		Age:  age,
	}
	fmt.Fprintln(file, e)
	fmt.Fprintln(file, "a", 12, "b", 12.0)
}

输出

它将在当前目录中创建名为 temp.txt 的文件,内容如下。在这个程序中,我们将os.Stdout 替换为创建的文件。

Name is:  John
Age is:  21
{John 21}
a 12 b 12

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

理解 Go(Golang)中的 go.sum 和 go.mod 文件。

来源:golangbyexample.com/go-mod-sum-module/

注意: 如果你有兴趣学习 Golang,那么我们有一个全面的 golang 教程系列。请查看一下 – Golang 综合教程系列。现在让我们看看当前的教程。下面是内容目录。

目录

  • 概述

  • 示例

  • go.mod 文件中的间接依赖示例 * # 概述

模块是 go 对依赖管理的支持。根据定义,模块是一个相关包的集合,其根目录下有 go.mod 文件。 go.mod 文件定义了该模块的

  • 模块导入路径。

  • 创建模块时所使用的 go 版本。

  • 模块成功构建的依赖要求。它定义了项目的依赖要求,并将其锁定到正确的版本。

go.sum

该文件列出了所需的直接和间接依赖的校验和及其版本。值得一提的是,go.mod 文件对于成功构建是足够的。那么为什么需要 go.sum 文件呢? go.sum 文件中的校验和用于验证每个直接和间接依赖的校验和,以确认它们没有被修改。

我们上面提到过,go.mod 文件列出了模块的依赖要求。现在,一个模块的依赖可以分为两种类型。

  • 直接 - 直接依赖是模块直接导入的依赖。

  • 间接 – 这是模块的直接依赖所导入的依赖。此外,任何在 go.mod 文件中提到但未在模块的任何源文件中导入的依赖也被视为间接依赖。

go.mod 文件仅记录直接依赖。然而,在以下情况下,它可能记录间接依赖。

  • 任何不在你的直接依赖的 go.mod 文件中列出的间接依赖,或者如果直接依赖没有 go.mod 文件,那么该依赖将以 //indirect 作为后缀添加到 go.mod 文件中。我们将在文章后面看到一个示例以便更好地理解这一点。

还请注意,go.modgo.sum 文件都应当被检查到版本控制系统(VCS)中,例如 git。

示例

让我们看一个例子来理解我们刚才谈到的 go.modgo.sum 文件。为此,让我们先创建一个模块。

go mod init learn

此命令将在同一目录下创建一个 go.mod 文件。让我们检查一下这个文件的内容。执行 cat go.mod

module learn

go 1.14

当模块使用 init 命令首次创建时,go.mod 文件将只包含两个内容。

  • 模块的导入路径在顶部。
module learn
  • 创建模块时使用的 go 版本
go 1.14

由于这是一个空模块,因此尚未指定任何直接依赖项。我们在同一目录中创建一个名为uuid.go的文件,内容如下:

uuid.go

package main

import (
	"fmt"
	"strings"

	"github.com/pborman/uuid"
)

func main() {
	uuidWithHyphen := uuid.NewRandom()
	uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)
	fmt.Println(uuid)
}

请注意,我们在 uuid.go 中也导入了依赖项。

"github.com/pborman/uuid"

让我们运行以下命令:

go mod tidy

此命令将下载源文件中所需的所有依赖项,并用该依赖项更新go.mod文件。运行此命令后,让我们再次检查go.mod文件的内容。执行cat go.mod

module learn

go 1.14

require github.com/pborman/uuid v1.2.1

它列出了在 uuid 文件中指定的直接依赖项及其确切版本。现在让我们检查一下go.sum文件。

执行cat go.sum

github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=

go.sum文件列出了模块所需的直接和间接依赖项的校验和。github.com/google/uuidgithub.com/pborman/uuid内部使用。它是该模块的间接依赖项,因此记录在go.sum文件中。

我们现在也可以运行这个文件,它将给出正确的输出。

go run uuid.go

输出

e594dc4d9a754bcb83b56e89b18b4b46

go.mod 文件中的间接依赖示例

我们在上面提到,go.mod文件在以下情况下可能包含间接依赖项。

  • 任何未在直接依赖项的go.mod文件中列出的间接依赖项,或者如果直接依赖项没有 go.mod 文件,那么该依赖项将以//indirect作为后缀添加到go.mod文件中。我们将在本文后面看到一个示例以更好地了解这一点。

让我们通过一个示例来理解。为此,我们先再次创建一个模块。

git mod init learn

现在创建一个文件learn.go

package main

import (
	"github.com/gocolly/colly"
)

func main() {
	_ = colly.NewCollector()
}

请注意,我们在learn.go中指定的依赖项如下:

github.com/gocolly/colly

因此,github.com/gocolly/colly是 learn 模块的直接依赖项,因为它在模块中被直接导入。现在让我们运行以下命令:

go mod tidy

运行此命令后,让我们再次检查go.mod文件的内容。由于 colly 版本 v1.2.0 没有 go.mod 文件,colly 所需的所有依赖项将以//indirect 作为后缀添加到go.mod文件中。

执行cat go.mod

module learn

go 1.14

require (
	github.com/PuerkitoBio/goquery v1.6.0 // indirect
	github.com/antchfx/htmlquery v1.2.3 // indirect
	github.com/antchfx/xmlquery v1.3.3 // indirect
	github.com/gobwas/glob v0.2.3 // indirect
	github.com/gocolly/colly v1.2.0
	github.com/kennygrant/sanitize v1.2.4 // indirect
	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
	github.com/temoto/robotstxt v1.1.1 // indirect
	golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1 // indirect
	google.golang.org/appengine v1.6.7 // indirect
)

所有其他依赖项都以//indirect结尾。所有直接和间接依赖项的校验和将记录在 go.sum 文件中。

理解 Go (Golang) 中的模块名称或模块导入路径

来源:golangbyexample.com/module-import-path-golang/

目录

  • 概述

  • 该模块是一个实用模块,你计划将其发布到像 git 这样的版本控制系统(VCS)")

  • 该模块是一个实用模块,你不打算发布你的模块

  • 该模块是一个可执行模块

概述

模块是 Go 支持的依赖管理。模块的定义是包含相关包的集合,其根目录中有 go.modgo.mod 文件定义了

  • 模块导入路径。

  • 模块成功构建所需的依赖要求。它定义了项目的依赖要求,并将其锁定到正确的版本。

现在让我们更详细地讨论模块导入路径。这是用于导入该模块所有包的前缀路径。让我们看看一个例子来更好地理解模块导入路径。为此,让我们创建一个模块

go mod init sample.com/learn

上述命令将创建一个导入路径为 sample.com/learn 的模块。它还会在同一目录中创建一个 go.mod 文件。让我们检查一下该文件的内容。执行 cat go.mod

module sample.com/learn

go 1.14

go.mod 文件的第一行将包含模块导入路径,正如你在上面的 go.mod 文件中看到的那样。现在,这个模块导入路径有什么用。模块的导入路径用于从任何其他模块导入该模块的任何包。这个其他模块将使用模块导入路径作为前缀路径,加上包的文件夹路径。

module_import_path + folder_path_of_package

因此,如果 sample.com/learn 模块包含位于目录 math 中的 math 包,那么任何其他模块都可以使用以下路径导入 math

sample.com/learn/math

现在让我们看看决定模块导入路径命名的因素。可以有三种情况决定可以与模块一起使用的导入路径。

  • 该模块是一个实用模块,你计划将其发布到 VCS 例如 git

  • 该模块是一个实用模块,你不打算发布你的模块

  • 该模块是一个可执行模块

该模块是一个实用模块,你计划将其发布到像 git 这样的版本控制系统(VCS)

如果你计划发布你的模块,则模块名称应与托管该模块的仓库的 URL 匹配。Go 尝试使用模块的导入路径从 VCS 下载依赖项。

例如,注意该模块的导入路径 github.com/pborman/uuid。go.mod 文件如下所示

module github.com/pborman/uuid

...

请注意,模块的 URL 与模块导入路径相同。

该模块是一个工具模块,您并不打算发布该模块。

当您仅打算在本地使用工具模块时,就是这种情况。在这种情况下,导入路径可以是任何东西。参考这个示例,我们展示了如何导入本地模块。在这个示例中,我们为模块导入路径使用了一个不代表版本控制系统仓库的名称。

golangbyexample.com/import-local-module-golang/

该模块是一个可执行模块。

在这种情况下,模块导入路径可以是任何东西。即使您计划将模块提交到版本控制系统,该导入路径也可以是非 URL,因为它不会被其他模块使用。

然而,在创建模块时使用有意义的导入路径是一个好习惯。

理解 Go(Golang)中的 Print 函数

来源:golangbyexample.com/print-function-golang/

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

目录

  • 概述

  • 程序

概述

Printfmt包中定义,用于格式化字符串并写入标准输出

https://golang.org/pkg/fmt/#Print

以下是Print的函数原型

func Print(a ...interface{}) (n int, err error)

Print使用默认格式说明符格式化字符串,但在字符串后不添加换行。Print接受可变数量的参数,每个参数都是一个空接口。它返回打印的字符数和发生的任何错误。由于参数类型是空接口,我们可以传递任何数据类型。我们可以传递字符串、整数、浮点数、结构体或任何其他数据类型。Print函数的每个参数根据该参数类型的默认格式说明符进行格式化。例如,结构体将根据以下说明符进行格式化

%v

此格式说明符仅打印结构体中的值部分。fmt包还提供了一个附加换行的函数——PrintlnPrint函数与Println函数完全相同,除了两个区别

  • 它不会在末尾添加换行。我们需要使用换行标识符来添加换行“\n”。

  • 仅在操作数都不是字符串时,才会在参数之间添加空格

让我们看一个例子

程序

让我们看一个相同的例子

package main

import "fmt"

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21
	fmt.Print("Name is:", name, "\n")
	fmt.Print("Age is:", age, "\n")

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Print(e, "\n")

	fmt.Print("a", 12, "b", 12.0, "\n")

	fmt.Print(12, 12.0, "\n")

        bytesPrinted, err := fmt.Print("Name is: ", name, "\n")
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Print(bytesPrinted)
}

输出

Name is:John
Age is:21
{John 21}
a12b12
12 12
Name is: John
14

关于Print函数的一些重要注意事项

  • 它不会在末尾添加换行。这就是为什么需要使用“\n”来添加换行。

  • 只有当两个参数都不是字符串时,它才会在两个参数之间添加空格。这就是原因

fmt.Print(12, 12.0, "\n")

打印

12 12

fmt.Print("a", 12, "b", 12.0, "\n")

打印

a12b12
  • 它还返回打印的字符数和发生的任何错误
bytesPrinted, err := fmt.Print("Name is: ", name, "\n")
if err != nil {
    log.Fatalln("Error occured", err)
}
fmt.Print(bytesPrinted)

将输出以下内容

Name is: John
14

bytesPrinted的数量为 14,因为输出了 14 个字符

此外,请查看我们的 Golang 高级教程系列——Golang 高级教程*

理解 Go(Golang)中的 Printf 函数。

来源:golangbyexample.com/printf-golang/

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

目录

  • 概述

  • 程序

概述

Printf定义在fmt包中,用于格式化字符串并写入标准输出。

golang.org/pkg/fmt/#Printf

下面是Printf的函数原型。

func Printf(format string, a ...interface{}) (n int, err error)

Printf使用自定义说明符格式化字符串。它也不会添加新行。Printf也是一个可变参数函数,意味着它可以有多个参数。关于其参数列表有两个重要点。

  • 请注意,第一个参数是格式模板字符串。

  • 下一个是可变数量的参数。此列表中的每个参数可以是字符串、整数、结构体或其他任何内容。因此,它是一个空接口。

格式模板字符串包含需要格式化的实际字符串和一些格式动词。这些格式动词说明尾随参数在最终字符串中的格式化方式。因此,格式字符串参数包含某些符号,这些符号会被尾随参数替换。

示例

打印字符串变量

  • %s符号被使用

  • 示例

name := "John"
fmt.Printf("Name is: %s\n", name)

打印整数

  • %d符号被使用。

  • 示例

age := 21
fmt.Printf("Age is: %d\n", age)

打印结构体

例如,有三个格式说明符用于打印结构体。

  • %v – 它只会打印值。字段名称不会被打印。这是在使用 Println 时打印结构体的默认方式。

  • %+v –它将打印字段和值。

  • %#v –它将打印结构体,包括字段名称和值。

这就是原因。

fmt.Printf("Employee is %v\n", e)
fmt.Printf("Employee is %+v\n", e)
fmt.Printf("Employee is %#v\n", e)

分别打印如下。

Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}

这与上面的解释一致。

另外,请注意此函数返回打印的字符数以及发生的任何错误。与Println不同,它不会自动添加新行。你需要显式添加“\n”

程序

这里是相应的工作程序。

package main

import (
	"fmt"
	"log"
)

type employee struct {
	Name string
	Age  int
}

func main() {
	name := "John"
	age := 21

	fmt.Printf("Name is: %s\n", name)
	fmt.Printf("Age is: %d\n", age)

	fmt.Printf("Name: %s Age: %d\n", name, age)

	e := employee{
		Name: name,
		Age:  age,
	}

	fmt.Printf("Employee is %v\n", e)
	fmt.Printf("Employee is %+v\n", e)
	fmt.Printf("Employee is %#v\n", e)

	bytesPrinted, err := fmt.Printf("Name is: %s\n", name)
	if err != nil {
		log.Fatalln("Error occured", err)
	}
	fmt.Println(bytesPrinted)
}

输出

Name is: John
Age is: 21
Name: John Age: 21
Employee is {John 21}
Employee is {Name:John Age:21}
Employee is main.employee{Name:"John", Age:21}
Name is: John
14

请注意下面的Printf

fmt.Printf("Name: %s Age: %d\n", name, age)
  • %s被名称替换。

  • %d被年龄替换。

因此,格式字符串参数中的符号或动词按顺序被尾随参数替换。

如果格式字符串中的格式说明符数量与下一个可变参数的数量不匹配,则格式说明符将按原样打印。例如,在下面的代码中,我们有两个格式说明符。

  • %d

  • %s

而下一个可变数量的参数仅有一个。因此,当我们打印它时,它会如实打印第二个格式说明符,并显示“MISSING”作为警告。

package main
import "fmt"
type employee struct {
    Name string
    Age  int
}
func main() {
    name := "John"
    fmt.Printf("Name is: %s %d\n", name)
}

输出

Name is: John %!d(MISSING)

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

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