通过示例学习-Go-语言-2023-三十四-
通过示例学习 Go 语言 2023(三十四)
Go(Golang)中的时间转换
目录
-
概述
-
转换
-
time.Time 到其他
-
时间 Unix
-
时间 Unix 毫秒
-
时间 Unix 微秒
-
时间 Unix 纳秒
-
完整工作代码
-
-
时间 Unix 到其他
-
time.Time
-
时间 Unix 毫秒
-
时间 Unix 微秒
-
时间 Unix 纳秒
-
完整工作代码
-
-
时间 Unix 毫秒到其他
-
time.Time
-
时间 Unix
-
时间 Unix 微秒
-
时间 Unix 纳秒
-
完整工作代码:
-
-
时间 Unix 微秒到其他
-
time.Time
-
时间 Unix
-
时间 Unix 毫秒
-
时间 Unix 纳秒
-
完整工作代码:
-
-
时间 Unix 纳秒到其他
-
time.Time
-
时间 Unix
-
时间 Unix 毫秒
-
时间 Unix 微秒
-
完整工作代码
-
-
概述
时间可以在 GO 中以以下 5 种格式表示:
-
time.Time 对象
-
Unix 时间(也称为纪元时间) – 自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的秒数。此时间也称为 Unix 纪元
-
Unix 纳秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的纳秒数
-
Unix 毫秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的毫秒数
-
Unix 微秒 – 自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的微秒数
转换
time.Time 到其他
设:
timeT := time.Now()
时间 Unix
tUnix := timeT.Unix()
时间 Unix 毫秒
tUnixMilli := int64(time.Nanosecond) * timeT.UnixNano() / int64(time.Millisecond)
时间 Unix 微秒
tUnixMicro := int64(time.Nanosecond) * timeT.UnixNano() / int64(time.Microsecond)
时间 Unix 纳秒
tUnixNano := timeT.UnixNano()
完整工作代码
package main
import (
"fmt"
"time"
)
func main() {
timeT := time.Now() //It will return time.Time object with current timestamp
fmt.Printf("time.Time %s\n", timeT)
//Converstion to Time Unix also known as epoch time
tUnix := timeT.Unix()
fmt.Printf("timeUnix: %d\n", tUnix)
//Conversion to Time Unix Millisecond
tUnixMilli := int64(time.Nanosecond) * timeT.UnixNano() / int64(time.Millisecond)
fmt.Printf("timeUnixMilli: %d\n", tUnixMilli)
//Conversion to Time Unix Microsecond
tUnixMicro := int64(time.Nanosecond) * timeT.UnixNano() / int64(time.Microsecond)
fmt.Printf("timeUnixMicro: %d\n", tUnixMicro)
//Conversion to Time Unix Nanosecond
tUnixNano := timeT.UnixNano()
fmt.Printf("timeUnixNano: %d\n", tUnixNano)
}
输出:
time.Time 2020-01-27 23:03:21.528106 +0530 IST m=+0.000191621
timeUnix: 1580146401
timeUnixMilli: 1580146401528
timeUnixMicro: 1580146401528106
timeUnixNano: 1580146401528106000
时间 Unix 到其他
设
tUnix := time.Now().Unix()
time.Time
tUnix := timeT.Unix()
时间 Unix 毫秒
tUnixMili := tUnix * int64(time.Microsecond)
时间 Unix 微秒
tUnixMicro := tUnix * int64(time.Millisecond)
时间 Unix 纳秒
tUnixNano := tUnix * int64(time.Second)
完整工作代码
package main
import (
"fmt"
"time"
)
func main() {
tUnix := time.Now().Unix()
fmt.Printf("timeUnix %d\n", tUnix)
//Conversion to time.Time
timeT := time.Unix(tUnix, 0)
fmt.Printf("time.Time: %s\n", timeT)
//Conversion to Time Unix Millisecond
tUnixMili := tUnix * int64(time.Microsecond)
fmt.Printf("timeUnixMilli: %d\n", tUnixMili)
//Conversion to Time Unix Microsecond
tUnixMicro := tUnix * int64(time.Millisecond)
fmt.Printf("timeUnixMicro: %d\n", tUnixMicro)
//Conversion to Time Unix Nanosecond
tUnixNano := tUnix * int64(time.Second)
fmt.Printf("timeUnixNano: %d\n", tUnixNano)
}
输出:
timeUnix 1580146705
time.Time: 2020-01-27 23:08:25 +0530 IST
timeUnixMilli: 1580146705000
timeUnixMicro: 1580146705000000
timeUnixNano: 1580146705000000000
时间 Unix 毫秒到其他
让
tUnixMilli := int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Millisecond)
time.Time
tUnix := tUnixMilli / int64(time.Microsecond)
tUnixNanoRemainder := (tUnixMilli % int64(time.Microsecond)) * int64(time.Millisecond)
timeT := time.Unix(tUnix, tUnixNanoRemainder)
时间 Unix
tUnix = tUnixMilli / int64(time.Microsecond)
时间 Unix 微秒
tUnixMicro := tUnixMilli * int64(time.Microsecond)
时间 Unix 纳秒
tUnixNano := tUnixMilli * int64(time.Millisecond)
完整工作代码:
package main
import (
"fmt"
"time"
)
func main() {
tUnixMilli := int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Millisecond)
fmt.Printf("timeMilli %d\n", tUnixMilli)
//Conversion to time.Time
tUnix := tUnixMilli / int64(time.Microsecond)
tUnixNanoRemainder := (tUnixMilli % int64(time.Microsecond)) * int64(time.Millisecond)
timeT := time.Unix(tUnix, tUnixNanoRemainder)
fmt.Printf("time.Time: %s\n", timeT)
//Conversion to Time Unix
tUnix = tUnixMilli / int64(time.Microsecond)
fmt.Printf("timeUnix: %d\n", tUnix)
//Conversion to Time Unix Microsecond
tUnixMicro := tUnixMilli * int64(time.Microsecond)
fmt.Printf("timeUnixMicro: %d\n", tUnixMicro)
//Conversion to Time Unix Nanosecond
tUnixNano := tUnixMilli * int64(time.Millisecond)
fmt.Printf("timeUnixNano: %d\n", tUnixNano)
}
输出:
timeMilli 1580146846747
time.Time: 2020-01-27 23:10:46.747 +0530 IST
timeUnix: 1580146846
timeUnixMicro: 1580146846747000
timeUnixNano: 1580146846747000000
时间 Unix 微秒到其他
让
tUnixMicro := int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Microsecond)
time.Time
tUnix := tUnixMicro / int64(time.Millisecond)
tUnixNanoRemainder := (tUnixMicro % int64(time.Millisecond)) * int64(time.Microsecond)
timeT := time.Unix(tUnix, tUnixNanoRemainder)
时间 Unix
tUnix = tUnixMicro / int64(time.Millisecond)
时间 Unix 毫秒
tUnixMilli := tUnixMicro / int64(time.Microsecond)
时间 Unix 纳秒
tUnixNano := tUnixMicro * int64(time.Microsecond)
完整工作代码:
package main
import (
"fmt"
"time"
)
func main() {
tUnixMicro := int64(time.Nanosecond) * time.Now().UnixNano() / int64(time.Microsecond)
fmt.Printf("tUnixMicro %d\n", tUnixMicro)
//Conversion to time.Time
tUnix := tUnixMicro / int64(time.Millisecond)
tUnixNanoRemainder := (tUnixMicro % int64(time.Millisecond)) * int64(time.Microsecond)
timeT := time.Unix(tUnix, tUnixNanoRemainder)
fmt.Printf("time.Time: %s\n", timeT)
//Converstion to Time Unix
tUnix = tUnixMicro / int64(time.Millisecond)
fmt.Printf("timeUnix: %d\n", tUnix)
//Converstion to Time Unix Milli
tUnixMilli := tUnixMicro / int64(time.Microsecond)
fmt.Printf("timeUnixMill: %d\n", tUnixMilli)
//Converstion to Time Unix Nano
tUnixNano := tUnixMicro * int64(time.Microsecond)
fmt.Printf("timeUnixNano: %d\n", tUnixNano)
}
输出:
tUnixMicro 1580147023233931
time.Time: 2020-01-27 23:13:43.233931 +0530 IST
timeUnix: 1580147023
timeUnixMill: 1580147023233
timeUnixNano: 1580147023233931000
时间 Unix 纳秒到其他
让
tUnixNano := time.Now().UnixNano()
time.Time
tUnix := tUnixNano / int64(time.Second)
tUnixNanoRemainder := (tUnixNano % int64(time.Second))
timeT := time.Unix(tUnix, tUnixNanoRemainder)
时间 Unix
tUnix = tUnixNano / int64(time.Second)
时间 Unix 毫秒
tUnixMilli := tUnixNano / int64(time.Millisecond)
时间 Unix 微秒
tUnixMicro := tUnixNano / int64(time.Microsecond)
完整工作代码
package main
import (
"fmt"
"time"
)
func main() {
tUnixNano := time.Now().UnixNano()
fmt.Printf("tUnixNano %d\n", tUnixNano)
//Conversion to time.Time
tUnix := tUnixNano / int64(time.Second)
tUnixNanoRemainder := (tUnixNano % int64(time.Second))
timeT := time.Unix(tUnix, tUnixNanoRemainder)
fmt.Printf("time.Time: %s\n", timeT)
//Conversion to Time Unix
tUnix = tUnixNano / int64(time.Second)
fmt.Printf("timeUnix: %d\n", tUnix)
//Conversion to Time Unix Milli
tUnixMilli := tUnixNano / int64(time.Millisecond)
fmt.Printf("timeUnixMilli: %d\n", tUnixMilli)
//Conversion to Time Unix Micro
tUnixMicro := tUnixNano / int64(time.Microsecond)
fmt.Printf("timeUnixMicro: %d\n", tUnixMicro)
}
输出:
tUnixNano 1580147160564568000
time.Time: 2020-01-27 23:16:00.564568 +0530 IST
timeUnix: 1580147160
timeUnixMill: 1580147160564
timeUnixMicro: 1580147160564568
Go 语言中的时间/日期格式化
如果你在其他语言中处理过时间/日期格式化,你可能会注意到其他语言使用特殊占位符进行时间/日期格式化。例如,Ruby 语言使用
-
%d 表示日期
-
%Y 表示年份
等等
在 Golang 中,日期和时间格式占位符看起来像日期和时间。请参考以下占位符表
类型 | 占位符 |
---|---|
日期 | 2 或 02 或 _2 |
星期几 | Monday 或 Mon |
月 | 01 或 1 或 Jan 或 January |
年 | 2006 或 06 |
小时 | 03 或 3 或 15 |
分钟 | 04 或 4 |
秒 | 05 或 5 |
毫秒 (ms) | .000 //尾随零将被包含或 .999 //尾随零将被省略 |
微秒 (μs) | .000000 //尾随零将被包含或 .999999 //尾随零将被省略 |
纳秒 (ns) | .000000000 //尾随零将被包含或 .999999999 //尾随零将被省略 |
上午/下午 | PM 或 pm |
时区 | MST |
时区偏移 | Z0700 或 Z070000 或 Z07 或 Z07:00 或 Z07:00:00 或 -0700 或 -070000 或 -07 或 -07:00 或 -07:00:00 |
上述占位符按顺序表示时容易记忆。
星期-月-日-小时-分钟-秒-年-时区 将是 Mon-01-02-03-04-05-06–07
让我们看看一些时间格式代码示例
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
//Format YYYY-MM-DD
fmt.Printf("YYYY-MM-DD: %s\n", now.Format("2006-01-02"))
//Format YY-MM-DD
fmt.Printf("YY-MM-DD: %s\n", now.Format("06-01-02"))
//Format YYYY-#{MonthName}-DD
fmt.Printf("YYYY-#{MonthName}-DD: %s\n", now.Format("2006-Jan-02"))
//Format HH:MM:SS
fmt.Printf("HH:MM:SS: %s\n", now.Format("03:04:05"))
//Format HH:MM:SS Millisecond
fmt.Printf("HH:MM:SS Millisecond: %s\n", now.Format("03:04:05 .999"))
//Format YYYY-#{MonthName}-DD WeekDay HH:MM:SS
fmt.Printf("YYYY-#{MonthName}-DD WeekDay HH:MM:SS: %s\n", now.Format("2006-Jan-02 Monday 03:04:05"))
//Format YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset
fmt.Printf("YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset: %s\n", now.Format("2006-Jan-02 Monday 03:04:05 PM MST -07:00"))
}
输出:
YYYY-MM-DD: 2020-01-25
YY-MM-DD: 20-01-25
YYYY-#{MonthName}-DD: 2020-Jan-25
HH:MM:SS: 11:14:16
HH:MM:SS Millisecond: 11:14:16 .213
YYYY-#{MonthName}-DD WeekDay HH:MM:SS: 2020-Jan-25 Saturday 11:14:16
YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset: 2020-Jan-25 Saturday 11:14:16 PM IST +05:30
Go 中两个时间值之间的时间差
来源:
golangbyexample.com/time-difference-between-two-time-value-golang/
目录
-
概述
-
代码:
概述
在 Go 中,时间由 time.Time 结构体表示。该结构体有一个 Sub 方法,可用于获取两个不同时间值之间的差值。
currentTime := time.Now()
oldTime := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
diff := currentTime.Sub(oldTime)
Sub 函数返回的是类型为 Duration 的差值。它以 int64 纳秒计数表示。类型 Duration 还定义了多个实用函数,可以用来获取差值。
-
小时
-
分钟
-
秒
-
纳秒
代码:
让我们看一个实际的例子:
package main
import (
"fmt"
"time"
)
func main() {
currentTime := time.Now()
oldTime := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
diff := currentTime.Sub(oldTime)
//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())
//In nanoseconds
fmt.Printf("Nanoseconds: %d\n", diff.Nanoseconds())
}
输出:
Hours 712.985400
Minutes 42779.124024
Seconds 2566747.441457
Nanoseconds 2566747441457000
```*
<!--yml
分类: 未分类
日期: 2024-10-13 06:07:25
-->
# 在 Go(Golang)中触摸文件
> 来源:[`golangbyexample.com/touch-file-golang/`](https://golangbyexample.com/touch-file-golang/)
触摸一个文件意味着
+ 如果文件不存在,则创建一个空文件
+ 如果文件已经存在,则更新文件的修改时间。
```go
package main
import (
"fmt"
"log"
"os"
"time"
)
func main() {
fileName := "temp.txt"
_, err := os.Stat(fileName)
if os.IsNotExist(err) {
file, err := os.Create("temp.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
} else {
currentTime := time.Now().Local()
err = os.Chtimes(fileName, currentTime, currentTime)
if err != nil {
fmt.Println(err)
}
}
}
输出:
第一次运行时会创建文件。第一次之后,它会更新文件的修改时间。
Go (Golang)中的雨水困境问题
目录
-
概述
-
程序
概述
有一组宽度为 1 单位但高度不同的柱子并排放置。柱子的高度用数组表示
[2, 0 , 2, 1, 3, 1]
数组表示的是
-
总共有 5 根柱子
-
第一根柱子的高度为 2
-
第二根柱子的高度为 0
-
第三根柱子的高度为 2
-
第四根柱子的高度为 1
-
第五根柱子的高度为 3
-
第六根柱子的高度为 1
目标是找到这些柱子之间可以困住的最大水量。
从上面的图中可以看出
-
1 和 3 柱子之间可以困住 2 单位的水
-
3 和 6 柱子之间可以困住 1 单位的水
因此,总共可以困住 3 单位的水
让我们看看相同的程序。策略是使用栈。
-
遍历给定的数组
-
对于当前元素,持续从栈中弹出,直到栈为空或栈顶元素的高度大于当前元素的高度。
-
对于每个弹出的元素,计算可以在弹出元素与当前元素之间困住的水量。
-
最后,将当前元素推入栈中,循环重复直到数组中的所有元素都被遍历。
程序
package main
import (
"fmt"
"sync"
)
func main() {
output := trap([]int{2, 0, 2, 1, 3, 1})
fmt.Println(output)
output = trap([]int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1})
fmt.Println(output)
output = trap([]int{4, 2, 0, 3, 2, 5})
fmt.Println(output)
}
func trap(height []int) int {
output := 0
heightArrayLen := len(height)
customStack := customStack{
stack: make([]int, 0),
}
customStack.Push(0)
for i := 1; i < heightArrayLen; i++ {
for customStack.Size() != 0 {
front, _ := customStack.Front()
if height[front] <= height[i] {
output = output + (i-front-1)*(height[front]-max(height, front+1, i-1))
customStack.Pop()
} else {
output = output + (i-front-1)*(height[i]-max(height, front+1, i-1))
break
}
}
customStack.Push(i)
}
return output
}
func max(input []int, start, end int) int {
if start > end {
return 0
}
max := 0
for i := start; i <= end; i++ {
if input[i] > max {
max = input[i]
}
}
return max
}
type customStack struct {
stack []int
lock sync.RWMutex
}
func (c *customStack) Push(name int) {
c.lock.Lock()
defer c.lock.Unlock()
c.stack = append(c.stack, name)
}
func (c *customStack) Pop() error {
len := len(c.stack)
if len > 0 {
c.lock.Lock()
defer c.lock.Unlock()
c.stack = c.stack[:len-1]
return nil
}
return fmt.Errorf("Pop Error: Stack is empty")
}
func (c *customStack) Front() (int, error) {
len := len(c.stack)
if len > 0 {
c.lock.Lock()
defer c.lock.Unlock()
return c.stack[len-1], nil
}
return 0, fmt.Errorf("Peep Error: Stack is empty")
}
func (c *customStack) Size() int {
return len(c.stack)
}
func (c *customStack) Empty() bool {
return len(c.stack) == 0
}
输出
3
6
9
注意: 请查看我们的 Golang 高级教程。此系列教程详尽,我们尝试用示例覆盖所有概念。本教程适合那些希望获得 Golang 专业知识和扎实理解的人——Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,那么这篇文章就是为你准备的——所有设计模式 Golang
Go (Golang) 中的 Trie 实现
目录
-
概述
-
完整工作代码
概述
Trie 是一种用于高效信息检索的数据结构。它是一种特殊类型的树,从根节点到特定节点的路径可以定义存储在此树中的单词。可以根据使用案例构建整个 ASCII_SIZE、字母和数字的 Trie。例如,下面将是支持小写字母的 Trie 数据结构的属性。
-
每个节点都有 ALBHABET_SIZE=26 个子节点。每个子节点本身也是一个 trie 节点,并且有 ALBHABET_SIZE=26 个子节点。
-
每个节点在其父节点的子节点数组中具有一个索引,并表示一个 ASCII 字符。例如,对于某个特定节点,第一个非空子节点表示字符 'a' 的存在,第二个非空子节点表示 'b' 的存在,依此类推。在某个索引上缺少子节点意味着没有值。
-
每个节点还有一个布尔字段,指示该节点是否为单词的结束。
-
根节点是起始节点,有 ALBHABET_SIZE=26 个子节点。根节点与一个空值相关联。
例如,对于关键字 [“sam”, “john”, “tim”, “jose”],将创建以下 Trie。
完整工作代码
package main
import "fmt"
const (
//ALBHABET_SIZE total characters in english alphabet
ALBHABET_SIZE = 26
)
type trieNode struct {
childrens [ALBHABET_SIZE]*trieNode
isWordEnd bool
}
type trie struct {
root *trieNode
}
func initTrie() *trie {
return &trie{
root: &trieNode{},
}
}
func (t *trie) insert(word string) {
wordLength := len(word)
current := t.root
for i := 0; i < wordLength; i++ {
index := word[i] - 'a'
if current.childrens[index] == nil {
current.childrens[index] = &trieNode{}
}
current = current.childrens[index]
}
current.isWordEnd = true
}
func (t *trie) find(word string) bool {
wordLength := len(word)
current := t.root
for i := 0; i < wordLength; i++ {
index := word[i] - 'a'
if current.childrens[index] == nil {
return false
}
current = current.childrens[index]
}
if current.isWordEnd {
return true
}
return false
}
func main() {
trie := initTrie()
words := []string{"sam", "john", "tim", "jose", "rose",
"cat", "dog", "dogg", "roses"}
for i := 0; i < len(words); i++ {
trie.insert(words[i])
}
wordsToFind := []string{"sam", "john", "tim", "jose", "rose",
"cat", "dog", "dogg", "roses", "rosess", "ans", "san"}
for i := 0; i < len(wordsToFind); i++ {
found := trie.find(wordsToFind[i])
if found {
fmt.Printf("Word \"%s\" found in trie\n", wordsToFind[i])
} else {
fmt.Printf("Word \"%s\" not found in trie\n", wordsToFind[i])
}
}
}
输出:
Word "sam" found in trie
Word "john" found in trie
Word "tim" found in trie
Word "jose" found in trie
Word "rose" found in trie
Word "cat" found in trie
Word "dog" found in trie
Word "dogg" found in trie
Word "roses" found in trie
Word "rosess" not found in trie
Word "ans" not found in trie
Word "san" not found in trie
在 Go (Golang) 中去除字符串的前导和尾随空格
目录
-
概述
-
代码:
概述
在 GO 中,字符串是 UTF-8 编码的。strings 包提供了一个 TrimSpace 方法,可以用来去除前导和尾随空格。此外,请注意此函数返回的是字符串的副本。
以下是该函数的签名
func TrimSpace(s string) string
让我们来看一下工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
//This will output removed
res := strings.TrimSpace(" test ")
fmt.Println(res)
res = strings.TrimSpace(" This is test ")
fmt.Println(res)
}
输出:
test
This is test
```*
<!--yml
分类:未分类
日期:2024-10-13 06:12:35
-->
# 在 Go (Golang)中修剪字符串的前缀
> 来源:[`golangbyexample.com/trim-prefix-string-go/`](https://golangbyexample.com/trim-prefix-string-go/)
目录
+ 概述
+ 代码
# **概述**
在 GO 中,字符串是 UTF-8 编码的。GO 的**strings**包提供了一个**TrimPrefix**方法,可用于从输入字符串中移除前缀字符串。如果输入字符串不以给定前缀开头,则输入字符串保持不变。此外,请注意此函数返回字符串的副本。
下面是该函数的签名
```go
func TrimPrefix(s, prefix string) string
让我们来看一下工作代码
代码
package main
import (
"fmt"
"strings"
)
func main() {
//This will output removed
res := strings.TrimPrefix("testremoved", "test")
fmt.Println(res)
//The input string will remain unchanged as it doesn't contain the test as prefix
res2 := strings.TrimPrefix("tesremoved", "test")
fmt.Println(res2)
}
输出:
removed
tesremoved
```*
<!--yml
类别:未分类
日期:2024-10-13 06:12:40
-->
# 在 Go 语言中修剪字符串的后缀
> 来源:[`golangbyexample.com/trim-suffix-string-golang/`](https://golangbyexample.com/trim-suffix-string-golang/)
目录
+ 概述
+ 代码:
# **概述**
在 GO 中,字符串是 UTF-8 编码的。GO 的**strings**包提供了一个**TrimSuffix**方法,可以用来从输入字符串中移除后缀字符串。如果输入字符串不以给定的后缀结尾,那么输入字符串将保持不变。同时,请注意该函数返回的是字符串的副本。
以下是该函数的签名
```go
func TrimSuffix(s, suffix string)
让我们看看工作代码
代码:
package main
import (
"fmt"
"strings"
)
func main() {
//This will output "removed"
res := strings.TrimSuffix("removedtest", "test")
fmt.Println(res)
//The input string will remain unchanged as it doesn't contain the test as suffix
res2 := strings.TrimSuffix("removedtes", "test")
fmt.Println(res2)
}
输出:
removed
removedtes
```*
<!--yml
分类: 未分类
日期: 2024-10-13 06:18:33
-->
# Go(Golang)中的二维(2d)和多维数组及切片
> 来源:[`golangbyexample.com/two-dimensional-array-slice-golang/`](https://golangbyexample.com/two-dimensional-array-slice-golang/)
在 Go 语言中,多维数组和切片都是可以的。让我们详细了解它们。
目录
** 多维数组
+ 概述
+ 访问多维数组的元素
+ 多维数组的遍历
+ 多维数组在内存中是如何存储的
+ 多维切片
+ 概述
+ 访问多维切片元素
+ 多维切片的遍历。
+ 多维切片在内存中是如何存储的
+ 结论
# **多维数组**
## **概述**
下面是声明多维数组的格式
```go
[len1][len2][len3]....[lenN]T{}
其中
-
len1、len2 .. lenN 是每个维度的长度
-
T 是数据类型
所有适用于一维数组的规则也适用于多维数组。声明时也可以指定数组元素。如果在声明时未指定数组元素,则所有数组元素都分配了<data_type> 的默认零值。
下面是声明一个指定数组元素的二维数组的格式。
var sample = [len1][len2]T{{a11, a12 .. a1y},
{a21, a22 .. a2y},
{.. },
{ax1, ax2 .. axy}}
其中
-
len1 表示行数
-
len2 表示列的数量
-
aij 表示位于第 i 行第 j 列的元素
-
T 是数据类型
让我们看看一个小示例,说明上述要点:
package main
import "fmt"
func main() {
sample := [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Printf("Number of rows in array: %d\n", len(sample))
fmt.Printf("Number of columns in array: %d\n", len(sample[0]))
fmt.Printf("Total number of elements in array: %d\n", len(sample)*len(sample[0]))
fmt.Println("Traversing Array")
for _, row := range sample {
for _, val := range row {
fmt.Println(val)
}
}
}
输出
Number of rows in array: 2
Number of columns in array: 3
Total number of elements in array: 6
Traversing Array
1
2
3
4
5
6
请注意在上面的程序中,我们是如何得到数组的行数、列数以及总元素数的
-
行数 = len(sample)
-
列数 = len(sample[0])
-
总元素数 = len(sample) * len(sample[0])
相同的思路可以扩展到三维、四维等等。让我们看看一个小的三维数组示例。在下面的程序中,我们创建了一个 223 的三维数组。
package main
import "fmt"
func main() {
sample := [2][2][3]int{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}}
fmt.Printf("Length of first dimension: %d\n", len(sample))
fmt.Printf("Length of second dimension: %d\n", len(sample[0]))
fmt.Printf("Length of third dimension: %d\n", len(sample[0][0]))
fmt.Printf("Overall Dimension of the array: %d*%d*%d\n", len(sample), len(sample[0]), len(sample[0][0]))
fmt.Printf("Total number of elements in array: %d\n", len(sample)*len(sample[0])*len(sample[0][0]))
for _, first := range sample {
for _, second := range first {
for _, value := range second {
fmt.Println(value)
}
}
}
}
输出
Length of first dimension: 2
Length of second dimension: 2
Length of third dimension: 3
Overall Dimension of the array: 2*2*3
Total number of elements in array: 12
1
2
3
4
5
6
7
8
9
10
11
12
访问多维数组的元素
多维数组的元素可以通过每个维度的索引进行访问。例如,二维数组可以通过提供其行索引和列索引进行访问。一旦我们能够使用每个维度的索引访问它们,那么也可以给它分配一个新值。让我们看看一个程序。
package main
import "fmt"
func main() {
sample := [2][3]int{{1, 2, 3}, {4, 5, 6}}
//Print array element
fmt.Println(sample[0][0])
fmt.Println(sample[0][1])
fmt.Println(sample[0][2])
fmt.Println(sample[1][0])
fmt.Println(sample[1][1])
fmt.Println(sample[1][2])
//Assign new values
sample[0][0] = 6
sample[0][1] = 5
sample[0][2] = 4
sample[1][0] = 3
sample[1][1] = 2
sample[1][2] = 1
fmt.Println()
fmt.Println(sample[0][0])
fmt.Println(sample[0][1])
fmt.Println(sample[0][2])
fmt.Println(sample[1][0])
fmt.Println(sample[1][1])
fmt.Println(sample[1][2])
}
输出
1
2
3
4
5
6
6
5
4
3
2
1
遍历多维数组
多维数组可以通过以下方式遍历:
-
for-range 循环
-
for 循环
让我们来看一个遍历二维数组的代码示例。
package main
import "fmt"
func main() {
sample := [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println("Using for-range")
for _, row := range sample {
for _, val := range row {
fmt.Println(val)
}
}
fmt.Println("\nUsing for loop")
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
fmt.Println(sample[i][j])
}
}
fmt.Println("\nUsing for loop - Second way")
for i := 0; i < len(sample); i++ {
for j := 0; j < len(sample[i]); j++ {
fmt.Println(sample[i][j])
}
}
}
输出
Using for-rage
1
2
3
4
5
6
Using for loop
1
2
3
4
5
6
Using for loop
1
2
3
4
5
6
关于上述程序的一些要点
-
我们必须使用嵌套范围进行遍历,使用 for-range 循环。第一个范围遍历每一行。第二个范围遍历该行中的各个数组。
-
迭代使用 for 循环也是如此。
-
len(sample) 给出行数。
-
len(sample[i]) 给出行 i 中列的数量。
-
同样的思想也可以扩展到三维、四维数组元素。
多维数组在内存中的存储方式
为数组分配的内存是连续的,无论数组是一维还是二维。例如,在二维数组的情况下,第二行在内存中从第一行结束的地方开始。让我们看看一个说明这一点的程序。
package main
import "fmt"
func main() {
sample := [2][3]byte{}
fmt.Println("First row")
fmt.Println(&sample[0][0])
fmt.Println(&sample[0][1])
fmt.Println(&sample[0][2])
fmt.Println("\nSecond row")
fmt.Println(&sample[1][0])
fmt.Println(&sample[1][1])
fmt.Println(&sample[1][2])
}
输出
First row
0xc0000b4002
0xc0000b4003
0xc0000b4004
Second row
0xc0000b4005
0xc0000b4006
0xc0000b4007
注意所有地址是连续的。第二行从第一行结束的地方开始。
多维切片
概述
由于多维数组是数组的数组,因此多维切片也是切片的切片。要理解这一点,我们先来看切片的定义。切片指向一个底层数组,并由切片头表示。切片头是一个结构体,样子如下。
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
切片头中的数据字段是指向底层数组的指针。对于一维切片,我们有如下声明。
oneDSlice := make([]int, 2)
要声明一个二维切片,声明如下。
twoDSlice := make([][]int, 2)
上述声明意味着我们想要创建一个切片,包含 2 个切片。仔细理解这一点。但在这里等一下,我们还没有指定第二个维度,这意味着每个内层切片的长度。在切片的情况下,每个内层切片必须像下面这样明确初始化。
for i := range twoDSlice {
twoDSlice[i] = make([]int, 3)
}
所以使用原始切片上的范围,我们使用 make 指定每个 2 个切片的长度。下面是另一种相同的方法,但指定了切片元素。
var twoDSlice = make([][]int, 2)
twoDSlice[0] = []int{1, 2, 3}
twoDSlice[1] = []int{4, 5, 6}
基本上,通过上述声明,我们创建了一个 2*3 维的切片,这就是一个二维切片。同样的思想可以扩展到二维、三维,等等。
上述两点的完整工作示例
package main
import "fmt"
func main() {
twoDSlice1 := make([][]int, 3)
for i := range twoDSlice1 {
twoDSlice1[i] = make([]int, 3)
}
fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice1))
fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice1[0]))
fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice1)*len(twoDSlice1[0]))
fmt.Println("First Slice")
for _, row := range twoDSlice1 {
for _, val := range row {
fmt.Println(val)
}
}
twoDSlice2 := make([][]int, 2)
twoDSlice2[0] = []int{1, 2, 3}
twoDSlice2[1] = []int{4, 5, 6}
fmt.Println()
fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice2))
fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice2[0]))
fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice2)*len(twoDSlice2[0]))
fmt.Println("Second Slice")
for _, row := range twoDSlice2 {
for _, val := range row {
fmt.Println(val)
}
}
}
输出
Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
First Slice
0
0
0
0
0
0
Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
Second Slice
1
2
3
4
5
6
我们上面提到,我们正在创建一个二维切片,维度为 2*3。也就是说,您可能会想到的问题是,内层切片是否可以有不同的长度。是的,这是可能的。与具有相同长度内层数组的数组不同,在切片的情况下,由于我们分别初始化每个内层切片,因此内层切片可以具有不同的长度。
让我们来看一个示例。
package main
import "fmt"
func main() {
twoDSlice := make([][]int, 2)
twoDSlice[0] = []int{1, 2, 3}
twoDSlice[1] = []int{4, 5}
fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice))
fmt.Printf("Len of first row: %d\n", len(twoDSlice[0]))
fmt.Printf("Len of second row: %d\n", len(twoDSlice[1]))
fmt.Println("Traversing slice")
for _, row := range twoDSlice {
for _, val := range row {
fmt.Println(val)
}
}
}
输出
Number of rows in slice: 2
Len of first row: 3
Len of second row: 2
Traversing slice
1
2
3
4
5
让我们看看一个三维切片的小示例。在下面的程序中,我们创建一个维度为 223 的切片。
package main
import "fmt"
func main() {
sample := make([][][]int, 2)
for i := range sample {
sample[i] = make([][]int, 2)
for j := range sample[i] {
sample[i][j] = make([]int, 3)
}
}
fmt.Printf("Length of first dimension: %d\n", len(sample))
fmt.Printf("Length of second dimension: %d\n", len(sample[0]))
fmt.Printf("Length of third dimension: %d\n", len(sample[0][0]))
fmt.Printf("Overall Dimension of the slice: %d*%d*%d\n", len(sample), len(sample[0]), len(sample[0][0]))
fmt.Printf("Total number of elements in slice: %d\n", len(sample)*len(sample[0])*len(sample[0][0]))
for _, first := range sample {
for _, second := range first {
for _, value := range second {
fmt.Println(value)
}
}
}
}
输出
Length of first dimension: 2
Length of second dimension: 2
Length of third dimension: 3
Overall Dimension of the slice: 2*2*3
Total number of elements in slice: 12
0
0
0
0
0
0
0
0
0
0
0
访问多维切片元素
访问切片元素与访问数组元素是相同的。让我们来看一个示例。
package main
import "fmt"
func main() {
sample := make([][]int, 2)
sample[0] = []int{1, 2, 3}
sample[1] = []int{4, 5, 6}
//Print array element
fmt.Println(sample[0][0])
fmt.Println(sample[0][1])
fmt.Println(sample[0][2])
fmt.Println(sample[1][0])
fmt.Println(sample[1][1])
fmt.Println(sample[1][2])
//Assign new values
sample[0][0] = 6
sample[0][1] = 5
sample[0][2] = 4
sample[1][0] = 3
sample[1][1] = 2
sample[1][2] = 1
fmt.Println()
fmt.Println(sample[0][0])
fmt.Println(sample[0][1])
fmt.Println(sample[0][2])
fmt.Println(sample[1][0])
fmt.Println(sample[1][1])
fmt.Println(sample[1][2])
}
输出
1
2
3
4
5
6
6
5
4
3
2
1
多维切片的遍历。
遍历多维切片与遍历多维数组是相同的。可以使用以下方式遍历多维切片:
-
for-range 循环
-
for 循环
让我们来看一个示例:
package main
import "fmt"
func main() {
sample := make([][]int, 2)
sample[0] = []int{1, 2, 3}
sample[1] = []int{4, 5, 6}
fmt.Println("Using for-range")
for _, row := range sample {
for _, val := range row {
fmt.Println(val)
}
}
fmt.Println("\nUsing for loop - Second way")
for i := 0; i < len(sample); i++ {
for j := 0; j < len(sample[i]); j++ {
fmt.Println(sample[i][j])
}
}
}
输出
多维切片在内存中的存储方式
由于在切片的情况下,每个内层切片是分别初始化的,因此内层切片在内存中可能不是彼此连续的。尽管每个内层切片内的每个元素将在连续的位置上。让我们看一个程序来说明这一点。
package main
import "fmt"
func main() {
sample := make([][]byte, 2)
sample[0] = make([]byte, 3)
//testVariable := "s"
//fmt.Println(testVariable)
sample[1] = make([]byte, 3)
fmt.Println("First row")
fmt.Println(&sample[0][0])
fmt.Println(&sample[0][1])
fmt.Println(&sample[0][2])
fmt.Println("\nSecond row")
fmt.Println(&sample[1][0])
fmt.Println(&sample[1][1])
fmt.Println(&sample[1][2])
}
输出
First row
0xc000018072
0xc000018073
0xc000018074
Second row
0xc000018080
0xc000018081
0xc000018082
请注意: 在上面的程序中有一个警告。由于第二个内层切片是在第一个内层切片之后初始化的,因此它们被分配的地址有可能是连续的。这可能发生,但并不总是如此。取消注释测试变量的那一行,然后两个内层切片将不会被分配连续的地址。在数组的情况下,所有内层数组的存储位置总是连续的。
结论
这就是关于 Golang 中的多维数组和切片的全部内容。希望您喜欢这篇文章。请在评论中分享您的反馈。
Go 语言中颜色不同的最远房子
来源:
golangbyexample.com/two-furthest-houses-different-color-golang/
目录
-
概述
-
程序
概述
给定一个数组,表示房子的颜色。所以 array[i]表示索引 i 处房子的颜色。目标是找到两个颜色不同且距离最远的房子。
如果不存在这样的索引,则返回-1。
示例 1
Input: intervals = [2,2,2,1,2,2]
Output: 3
Explanation: House at index 0 and house at index 3 is of different colors
示例 2
Input: intervals = [1, 2 ,3, 1, 2]
Output: 2
Explanation: House at index 0 and house at index 4 is of different colors
以下是我们可以采取的方法。
-
其中一个房子将位于第 0 个索引或 n-1 索引处,其中 n 是数组的长度。
-
我们可以先假设第 0 个索引的房子在解决方案中,然后进行计算。
-
我们可以接着假设 n-1 索引的房子在解决方案中,然后进行计算。
程序
下面是相应的程序。
package main
import "fmt"
func maxDistance(colors []int) int {
lenColors := len(colors)
if lenColors == 0 || lenColors == 1 {
return 0
}
maxDiff := 0
leftColor := colors[0]
rightColor := colors[lenColors-1]
for i := 1; i < lenColors; i++ {
if colors[i] != leftColor {
maxDiff = i
}
}
for i := lenColors - 2; i >= 0; i-- {
if colors[i] != rightColor {
diff := lenColors - i - 1
if diff > maxDiff {
maxDiff = diff
}
}
}
return maxDiff
}
func main() {
output := maxDistance([]int{2, 2, 2, 1, 2, 2})
fmt.Println(output)
output = maxDistance([]int{1, 2, 3, 1, 2})
fmt.Println(output)
}
输出
4
-1
注意: 请查看我们的 Golang 高级教程。本系列的教程内容详尽,我们尽力涵盖所有概念和示例。这个教程适合那些希望获得 Golang 专业知识和扎实理解的人——Golang 高级教程
如果你有兴趣了解如何在 Golang 中实现所有设计模式。如果是的话,这篇文章就是为你准备的——所有设计模式 Golang
类型在 Go 中实现多个接口
来源:
golangbyexample.com/type-implementing-multiple-interfaces-go/
目录
-
概述
-
代码
概述
一个类型如果定义了接口的所有方法,则实现该接口。如果该类型定义了另一个接口的所有方法,则它也实现那个接口。本质上,一个类型可以实现多个接口。
让我们看一个例子
假设我们有一个接口动物如下
type animal interface {
breathe()
walk()
}
另外假设我们也有一个哺乳动物接口如下
type mammal interface {
feed()
}
我们还有一个狮子结构体实现了这个动物和哺乳动物接口
type lion struct {
age int
}
代码
package main
import "fmt"
type animal interface {
breathe()
walk()
}
type mammal interface {
feed()
}
type lion struct {
age int
}
func (l lion) breathe() {
fmt.Println("Lion breathes")
}
func (l lion) walk() {
fmt.Println("Lion walk")
}
func (l lion) feed() {
fmt.Println("Lion feeds young")
}
func main() {
var a animal
l := lion{}
a = l
a.breathe()
a.walk()
var m mammal
m = l
m.feed()
}
输出
Lion breathes
Lion walk
Lion feeds young
在上面的程序中,狮子结构体定义了动物接口的所有方法。它也定义了哺乳动物接口的所有方法。这就是为什么这样可行
var a animal
l := lion{}
a = l
a.breathe()
a.walk()
这同样也是可行的
var m mammal
m = l
m.feed()
Go (Golang) 中的已命名与未命名常量
目录
** 已命名与未命名常量
-
已命名常量
-
未命名常量
已命名与未命名常量
在 Go 中,常量的处理方式与其他语言不同。Go 具有非常强的类型系统,不允许类型之间的隐式转换。即使是相同的数值类型,也不允许在没有显式转换的情况下进行任何操作。例如,你不能将int32和int64的值相加。要相加,必须将int32显式转换为int64,或反之亦然。然而,未命名常量可以暂时摆脱 Go 的类型系统的限制,正如我们在本文中将看到的。
已命名常量
在声明中指定类型的常量是已命名常量。例如,下面我们声明一个类型为 int32 的常量。
const a int32 = 8
这个常量 a 只能赋值给类型为 int32 的变量。如果将其赋值给其他类型的变量,则会引发错误。有关说明,请参见下面的程序。
package main
func main() {
const a int32 = 8
var i1 int32
var i2 int64
i1 = a
i2 = a
}
输出:
cannot use a (type int32) as type int64 in assignment
未命名常量
未命名常量是未指定类型的常量。在 Go 中,未命名常量可以是已命名的或未命名的。在这两种情况下,它都没有与之关联的类型。
未命名未指定常量的示例。
123 //Default hidden type is int
"circle" //Default hidden type is string
5.6\. //Default hidden type is float64
true //Default hidden type is bool
'a' //Default hidden type is rune
3+5i //Default hidden type is complex128
已命名未命名常量的示例。
const a = 123 //Default hidden type is int
const b = "circle" //Default hidden type is string
const c = 5.6 //Default hidden type is float64
const d = true //Default hidden type is bool
const e = 'a' //Default hidden type is rune
const f = 3+5i //Default hidden type is complex128
未命名常量确实有一个默认的隐式类型。以下表格说明了数值、字符串、字符和布尔值的隐式默认类型。
常量的默认隐式类型
整数 | int |
---|---|
浮点数 | float64 |
复数 | complex128 |
字符串 | string |
布尔值 | bool |
字符 | int32 或 rune |
当你使用fmt.Printf打印任何未命名常量时,它将打印默认的隐式类型。请参见下面的程序及未命名和已命名的未命名常量的输出。
package main
import "fmt"
func main() {
//Unanamed untyped constant
fmt.Printf("Type: %T Value: %v\n", 123, 123)
fmt.Printf("Type: %T Value: %v\n", "circle", "circle")
fmt.Printf("Type: %T Value: %v\n", 5.6, 5.6)
fmt.Printf("Type: %T Value: %v\n", true, true)
fmt.Printf("Type: %T Value: %v\n", 'a', 'a')
fmt.Printf("Type: %T Value: %v\n", 3+5i, 3+5i)
//Named untyped constant
const a = 123 //Default hidden type is int
const b = "circle" //Default hidden type is string
const c = 5.6 //Default hidden type is float64
const d = true //Default hidden type is bool
const e = 'a' //Default hidden type is rune
const f = 3 + 5i //Default hidden type is complex128
fmt.Println("")
fmt.Printf("Type: %T Value: %v\n", a, a)
fmt.Printf("Type: %T Value: %v\n", b, b)
fmt.Printf("Type: %T Value: %v\n", c, c)
fmt.Printf("Type: %T Value: %v\n", d, d)
fmt.Printf("Type: %T Value: %v\n", e, e)
fmt.Printf("Type: %T Value: %v\n", f, f)
}
输出:
Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)
Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)
上述程序输出int32而不是 rune,因为 rune 是int32的别名。
已命名或未命名常量的默认类型将成为其赋值变量的类型。例如,在下面的代码中,变量 a 将获得来自未命名常量123的默认类型int。
var a = 123
让我们看一个程序,说明上述所有未命名类型常量的要点。
package main
import "fmt"
func main() {
//Untyped
var u = 123 //Default hidden type is int
var v = "circle" //Default hidden type is string
var w = 5.6 //Default hidden type is float64
var x = true //Default hidden type is bool
var y = 'a' //Default hidden type is rune
var z = 3 + 5i //Default hidden type is complex128
fmt.Printf("Type: %T Value: %v\n", u, u)
fmt.Printf("Type: %T Value: %v\n", v, v)
fmt.Printf("Type: %T Value: %v\n", w, w)
fmt.Printf("Type: %T Value: %v\n", x, x)
fmt.Printf("Type: %T Value: %v\n", y, y)
fmt.Printf("Type: %T Value: %v\n", z, z)
}
输出:
Type: int Value: 123
Type: string Value: circle
Type: float64 Value: 5.6
Type: bool Value: true
Type: int32 Value: 97
Type: complex128 Value: (3+5i)
现在,脑海中浮现的问题是未命名常量的用途是什么。未命名常量的用途在于常量的类型将根据其被赋值的变量的类型来决定。听起来令人困惑吗?让我们通过一个示例来看。
数学包中的π常量值如下所示。
const Pi = 3.14159265358979323846264338327950288419716939937510582097494459
请注意,类型未指定,它仅具有一个隐式默认类型(在此为float64)。让我们来看一段代码。
package main
import (
"fmt"
"math"
)
func main() {
var f1 float32
var f2 float64
f1 = math.Pi
f2 = math.Pi
fmt.Printf("Type: %T Value: %v\n", math.Pi, math.Pi)
fmt.Printf("Type: %T Value: %v\n", f1, f1)
fmt.Printf("Type: %T Value: %v\n", f2, f2)
}
输出:
Type: float64 Value: 3.141592653589793
Type: float32 Value: 3.1415927
Type: float64 Value: 3.141592653589793
请注意上面的程序。
-
由于math.Pi常量的无类型特性,它可以被赋值给float32和float64类型的变量。在 GO 中,类型固定后这是不可能的。
-
当我们打印math.Pi的类型时,它打印出默认类型,即float64。
根据使用案例,无类型常量可以被赋值给低精度类型(float32)或高精度类型(float64)。
理解 MAC 上的 /etc/paths 文件和 /etc/paths.d 目录
MAC OS 使用 /etc/paths 文件和 /etc/paths.d 目录来设置 PATH 环境变量的值。一个工具 path_helper 用于根据 /etc/paths 文件的内容和 /etc/paths.d 目录中文件的内容来设置 PATH 环境变量。这就是如何运行 path_helper。
eval `/usr/libexec/path_helper -s`
让我们看看这两者在设置路径时的作用。
/etc/paths 文件
该文件包含需要设置在 PATH 环境变量中的路径。如果我在 MAC 上打印该文件的内容,它输出
/ $ cat /etc/paths
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
上述所有路径都被添加到 PATH 变量中。
/etc/paths.d 目录
此目录包含文件列表。每个文件包含需要添加到 PATH 变量的路径。作为演示,如果我使用这里提供的 GO 的 .pkg 安装程序在我的 MAC 上安装 GO – golang.org/dl/
。
安装后,它将在 ‘/etc/paths.d’ 目录中创建一个名为 ‘go’ 的文件。让我们输出该文件的内容。
/ $ cat /etc/paths.d/go
/usr/local/go/bin
它输出‘/usr/local/go/bin’,该路径将被添加到 PATH 环境变量中。
注意事项:
使用 ‘/etc/paths.d’ 目录的主要原因是 ‘/etc/paths’ 会在系统升级时被修改和/或替换,而 ‘/etc/paths.d’ 目录的内容不会受到这种变化的影响。
在 go (golang) 中理解 for-range 循环 – 完整指南
这是 golang 综合教程系列的第十一章。请参考此链接获取该系列的其他章节 – Golang 综合教程系列
下一个教程 – 如果-否则
上一个教程 – for 循环
现在让我们查看当前的教程。以下是当前教程的目录。
概述
说到循环,golang 有:
-
for 循环
-
for-range 循环
我们在上一个教程中看到了 for 循环。在本教程中,我们将学习 for-range 循环。
for-range 循环用于遍历 golang 中不同的集合数据结构,如
-
数组或切片
-
字符串
-
映射
-
通道
现在让我们来看一些示例
示例
数组/切片的 for-range 循环
这是 for-range 与数组/切片一起使用时的格式
for index, value := range array/slice {
//Do something with index and value
}
这就是 for-range 循环在数组/切片情况下的工作方式。它从索引零开始遍历给定的数组/切片,并为每个存在于索引的值执行 for-range 循环体。在与数组/切片一起使用时,for-range 中的索引和值都是可选的。
下面的示例展示了如何对切片使用 for-range 循环
-
带索引和值
-
仅包含值
-
仅带索引
-
无索引和值
package main
import "fmt"
func main() {
letters := []string{"a", "b", "c"}
//With index and value
fmt.Println("Both Index and Value")
for i, letter := range letters {
fmt.Printf("Index: %d Value:%s\n", i, letter)
}
//Only value
fmt.Println("\nOnly value")
for _, letter := range letters {
fmt.Printf("Value: %s\n", letter)
}
//Only index
fmt.Println("\nOnly Index")
for i := range letters {
fmt.Printf("Index: %d\n", i)
}
//Without index and value. Just print array values
fmt.Println("\nWithout Index and Value")
i := 0
for range letters {
fmt.Printf("Index: %d Value: %s\n", i, letters[i])
i++
}
}
输出:
Both Index and Value
Index: 0 Value:a
Index: 1 Value:b
Index: 2 Value:c
Only value
Value: a
Value: b
Value: c
Only Index
Index: 0
Index: 1
Index: 2
Without Index and Value
Index: 0 Value: a
Index: 1 Value: b
Index: 2 Value: c
带字符串的 for-range 循环
在 Golang 中,字符串是字节序列。字符串字面量实际上表示 UTF-8 字节序列。在 UTF-8 中,ASCII 字符是单字节的,对应前 128 个 Unicode 字符。所有其他字符则在 1 - 4 字节之间。要更好地理解,请考虑下面的字符串
sample := "a£c"
在上面的字符串中
-
‘a’ 按照 UTF-8 占用一个字节
-
‘£’ 按照 UTF-8 占用两个字节
-
‘b’ 按照 UTF-8 占用一个字节
上述字符串总共有 1+2+1 = 4 字节。因此,当我们尝试使用标准 len() 函数打印字符串的长度时,它将输出 4,而不是 3,因为 len() 函数返回字符串中的字节数。
fmt.Printf("Length is %d\n", len(sample))
因此,独立的 for 循环不能用于遍历字符串中的所有字符,因为它会遍历字节而不是字符。因此,下面的 for 循环将遍历四次,并打印出与该索引处的字节对应的值。
for i := 0; i < len(sample); i++ {
fmt.Printf("%c\n", sample[i])
}
它将输出下面的字符串,与 "a£c" 字符串不同
a£b
上述输出不是我们想要的。这就是 for-range 循环在字符串中的应用。它遍历字符串中的 Unicode 点(在 golang 中也称为 rune),并正确输出 a, £, b。以下是与字符串一起使用 for-range 的格式
for index, character := range string {
//Do something with index and character
}
在我们进入代码示例之前需要注意的一些要点
-
索引是字符串中 Unicode 字符的起始点。例如在字符串 "a£c" 中,字符 "a" 的起始索引为 0,字符 "£" 的起始索引为 1,而字符 "c" 的起始索引为 2。
-
值是 Unicode 点,基本上是字符串中的每个字符,而不是字节。它也被称为 rune。golang 中的 rune 表示一个 Unicode 代码点。
-
索引和值都是可选的。
现在让我们看一个代码示例。
package main
import "fmt"
func main() {
sample := "a£b"
//With index and value
fmt.Println("Both Index and Value")
for i, letter := range sample {
fmt.Printf("Start Index: %d Value:%s\n", i, string(letter))
}
//Only value
fmt.Println("\nOnly value")
for _, letter := range sample {
fmt.Printf("Value:%s\n", string(letter))
}
//Only index
fmt.Println("\nOnly Index")
for i := range sample {
fmt.Printf("Start Index: %d\n", i)
}
}
输出:
Both Index and Value
Start Index: 0 Value:a
Start Index: 1 Value:£
Start Index: 3 Value:b
Only value
Value:a
Value:£
Value:b
Only Index
Start Index: 0
Start Index: 1
Start Index: 3
带有映射的 for-range 循环
在映射的情况下,for-range 遍历映射的键和值。以下是与映射一起使用时的 for-range 格式。
for key, value := range map {
//Do something with key and value
}
值得注意的是,在使用 for-range 与映射时,键和值都是可选的。我们来看一个简单的代码示例。
package main
import "fmt"
func main() {
sample := map[string]string{
"a": "x",
"b": "y",
}
//Iterating over all keys and values
fmt.Println("Both Key and Value")
for k, v := range sample {
fmt.Printf("key :%s value: %s\n", k, v)
}
//Iterating over only keys
fmt.Println("\nOnly keys")
for k := range sample {
fmt.Printf("key :%s\n", k)
}
//Iterating over only values
fmt.Println("\nOnly values")
for _, v := range sample {
fmt.Printf("value :%s\n", v)
}
}
输出
Both Key and Value
key :a value: x
key :b value: y
Only keys
key :a
key :b
Only values
value :x
value :y
带有通道的 for-range 循环
for-range 循环在通道中也有不同的工作方式。对于通道,索引没有任何意义,因为通道类似于一个管道,值从一端进入,从另一端退出。
因此在通道的情况下,for-range 循环将遍历当前在通道中的值。在遍历完所有当前存在的值(如果有)后,for-range 循环不会退出,而是等待可能被推送到通道的下一个值,只有在通道关闭时才会退出。
使用 for-range 与通道时的格式如下。
for value := range channel {
//Do something value
}
让我们看一个代码示例。
package main
import "fmt"
func main() {
ch := make(chan string)
go pushToChannel(ch)
for val := range ch {
fmt.Println(val)
}
}
func pushToChannel(ch chan<- string) {
ch <- "a"
ch <- "b"
ch <- "c"
close(ch)
}
输出:
a
b
c
结论
这就是 golang 中 for-range 循环的全部内容。希望你喜欢它。请在评论中分享反馈/改进/错误。
下一个教程 – 如果否则
上一个教程 – For 循环