日常我们在Go语言开发中经常因为对某个源码包的使用不熟悉导致在代码中出现一些调用错误,或者说有现成好用的源码方法我们不知道导致我们自己又重复实现了一遍既浪费时间又可能由于我们自己实现的方法性能或者健壮性不够导致程序报错,所以Go语言提供的源码包的常见函数使用方法和常见用法我们一定需要花时间去熟悉去了解,只有我们充分熟悉了源码包的用法后,我们在写代码的过程中才高效率的完成日常开发中的需求,没有熟悉或者完整了解过GO语言源码包的用法的开发有福了,我这里整理了一份我们日常开发中高频使用的的源码包以及每个函数的用法和常见函数在代码中的应用,方便你开始学习Go语言的源码包和重新复习一遍各个函数的用法
1.源码包学习计划之sync包
Go语言的sync
包提供了多种同步原语,包括:
sync.Mutex
:互斥锁,用于保护临界区。sync.RWMutex
:读写锁,用于读多写少的场景。sync.WaitGroup
:等待组,用于协调多个goroutine的执行。sync.Once
:只执行一次的函数调用。sync.Cond
:条件变量,用于在多个goroutine之间进行信号传递和通信。sync.Pool
:对象池,用于减少内存分配和垃圾回收的开销。sync.Map
:并发安全的映射,用于在多个goroutine之间共享和访问映射对象。sync/atomic
包:原子操作,提供了基本的原子操作函数,用于在多个goroutine之间进行共享内存的访问和修改。
这些函数提供了多种不同的同步机制,可以满足不同的并发需求。
Go语言的sync
包提供了一些用于多协程同步的工具。下面是sync
包中常见的代码用法:
- 使用
sync.WaitGroup
等待一组协程执行完成。
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(num int) {
defer wg.Done()
fmt.Println("Goroutine", num, "done")
}(i)
}
wg.Wait()
fmt.Println("All goroutines finished executing")
}
- 使用
sync.Mutex
对共享资源进行锁定,避免并发读写导致的数据竞争问题。
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
func main() {
var wg sync.WaitGroup
c := Counter{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Increment()
}()
}
wg.Wait()
fmt.Println("Final counter value:", c.Value())
}
- 使用
sync.Once
保证某个操作只被执行一次。
var once sync.Once
var config Config
func getConfig() Config {
once.Do(func() {
// Load the config from a file or other data source
config = Config{Host: "localhost", Port: 8080}
})
return config
}
func main() {
fmt.Println(getConfig())
fmt.Println(getConfig())
}
以上是sync
包中的一些常见代码用法,它们都有助于解决并发编程中的一些常见问题。
2.源码包学习计划之atomic包
Go 语言的 sync/atomic
包提供了一些基本的原子操作函数,用于在多个 goroutine 之间进行共享内存的访问和修改。这些函数包括:
-
func AddInt32(addr *int32, delta int32) (new int32)
:原子地将 32 位整数加上 delta,并返回加上 delta 后的值。 -
func AddInt64(addr *int64, delta int64) (new int64)
:原子地将 64 位整数加上 delta,并返回加上 delta 后的值。 -
func AddUint32(addr *uint32, delta uint32) (new uint32)
:原子地将 32 位无符号整数加上 delta,并返回加上 delta 后的值。 -
func AddUint64(addr *uint64, delta uint64) (new uint64)
:原子地将 64 位无符号整数加上 delta,并返回加上 delta 后的值。 -
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
:比较 *addr 和 old 是否相等,如果相等,将 *addr 替换为 new,并返回 true;否则不做任何操作,返回 false。 -
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
:比较 *addr 和 old 是否相等,如果相等,将 *addr 替换为 new,并返回 true;否则不做任何操作,返回 false。 -
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
:比较 *addr 和 old 是否相等,如果相等,将 *addr 替换为 new,并返回 true;否则不做任何操作,返回 false。 -
func LoadInt32(addr *int32) (val int32)
:原子地加载 32 位整数的值,并返回加载的值。 -
func LoadInt64(addr *int64) (val int64)
:原子地加载 64 位整数的值,并返回加载的值。 -
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
:原子地加载指针的值,并返回加载的值。 -
func StoreInt32(addr *int32, val int32)
:原子地将 32 位整数的值设置为 val。 -
func StoreInt64(addr *int64, val int64)
:原子地将 64 位整数的值设置为 val。 -
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
:原子地将指针的值设置为 val。 -
func SwapInt32(addr *int32, new int32) (old int32)
:原子地将 32 位整数替换为 new,并返回替换前的值。 -
func SwapInt64(addr *int64, new int64) (old int64)
:原子地将 64 位整数替换为 new,并返回替换前的值。 -
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
:原子地将指针替换为 new,并返回替换前的值。
Go语言的atomic
包提供了原子操作函数,用于在多协程环境下对共享变量进行安全的原子操作。下面是atomic
包中常见的代码用法:
- 使用
atomic.AddInt32
和atomic.LoadInt32
对一个共享变量进行原子加法操作和读取操作。
var counter int32
func increment(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 100000; i++ {
atomic.AddInt32(&counter, 1)
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Counter:", atomic.LoadInt32(&counter))
}
- 使用
atomic.CompareAndSwapInt32
实现无锁并发操作。
type Queue struct {
items []int32
}
func (q *Queue) Push(item int32) {
for {
items := q.items
newItems := append(items, item)
if atomic.CompareAndSwapPointer(
(*unsafe.Pointer)(unsafe.Pointer(&q.items)),
unsafe.Pointer(items),
unsafe.Pointer(newItems),
) {
return
}
}
}
func (q *Queue) Pop() int32 {
for {
items := q.items
if len(items) == 0 {
return 0
}
newItems := items[1:]
if atomic.CompareAndSwapPointer(
(*unsafe.Pointer)(unsafe.Pointer(&q.items)),
unsafe.Pointer(items),
unsafe.Pointer(newItems),
) {
return items[0]
}
}
}
func main() {
q := Queue{}
q.Push(1)
q.Push(2)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.Pop())
}
以上是atomic
包中的一些常见代码用法,它们都可以用于实现高效且线程安全的并发操作。
3.源码包学习计划之time包
Go 语言的 time
包提供了许多函数,用于处理时间和计时器,常用的函数包括:
-
func Sleep(d Duration)
:让当前 goroutine 暂停指定的时间,以纳秒为单位。 -
func After(d Duration) <-chan Time
:返回一个chan Time
,在指定的时间间隔后会向该 channel 发送当前时间。 -
func AfterFunc(d Duration, f func()) *Timer
:返回一个 Timer,它在指定的时间间隔后调用函数 f。 -
func Tick(d Duration) <-chan Time
:返回一个chan Time
,定期发送当前时间,每个时间间隔为指定的时间间隔。 -
func Now() Time
:返回当前本地时间。 -
func Since(t Time) Duration
:返回当前时间与 t 之间的时间差。 -
func Until(t Time) Duration
:返回 t 与当前时间之间的时间差。 -
func ParseDuration(s string) (Duration, error)
:将字符串解析为 Duration 类型。 -
func Parse(layout, value string) (Time, error)
:将字符串解析为 Time 类型,使用指定的布局。 -
func Format(t Time, layout string) string
:将时间格式化为字符串,使用指定的布局。 -
type Duration int64
:代表一段时间,单位为纳秒。 -
type Time struct
:代表时间的类型,包含年月日时分秒和时区等信息。 -
type Timer struct
:代表计时器,可以在指定时间后执行某个函数或向 channel 发送事件。 -
type Ticker struct
:代表定时器,会周期性地向 channel 发送事件,可以通过 Stop 方法停止定时器。
Go语言的time
包提供了与时间相关的函数和结构体,下面是time
包中常见的代码用法:
- 使用
time.Now()
获取当前时间。
now := time.Now()
fmt.Println("Current time:", now)
- 使用
time.Sleep()
暂停执行一段时间。
fmt.Println("Before sleep")
time.Sleep(2 * time.Second)
fmt.Println("After sleep")
- 使用
time.Tick()
定时触发某个操作。
tick := time.Tick(1 * time.Second)
for {
select {
case <-tick:
fmt.Println("Tick")
}
}
- 使用
time.After()
在一段时间后触发某个操作。
after := time.After(2 * time.Second)
fmt.Println("Before after")
<-after
fmt.Println("After after")
- 使用
time.Parse()
解析时间字符串。
t, err := time.Parse("2006-01-02 15:04:05", "2023-04-28 12:30:00")
if err != nil {
panic(err)
}
fmt.Println("Parsed time:", t)
- 使用
time.Format()
格式化时间为字符串。
now := time.Now()
formatted := now.Format("2006-01-02 15:04:05")
fmt.Println("Formatted time:", formatted)
以上是time
包中的一些常见代码用法,它们都可以用于处理与时间相关的任务。
4.源码包学习计划之strconv包
Go 语言的 strconv
包提供了一些函数,用于字符串和基本数据类型之间的转换。常用的函数包括:
-
func ParseBool(str string) (bool, error)
:将字符串解析为 bool 类型。 -
func FormatBool(b bool) string
:将 bool 类型格式化为字符串。 -
func ParseInt(s string, base int, bitSize int) (i int64, err error)
:将字符串解析为指定进制和位数的 int 类型。 -
func FormatInt(i int64, base int) string
:将 int 类型格式化为指定进制的字符串。 -
func ParseUint(s string, base int, bitSize int) (uint64, error)
:将字符串解析为指定进制和位数的 uint 类型。 -
func FormatUint(i uint64, base int) string
:将 uint 类型格式化为指定进制的字符串。 -
func ParseFloat(s string, bitSize int) (float64, error)
:将字符串解析为 float 类型。 -
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
:将 float 类型格式化为字符串,可以指定格式和精度。 -
func Atoi(s string) (int, error)
:将字符串解析为 int 类型。 -
func Itoa(i int) string
:将 int 类型格式化为字符串。 -
func AppendBool(dst []byte, b bool) []byte
:将 bool 类型追加到 byte slice 中。 -
func AppendInt(dst []byte, i int64, base int) []byte
:将 int 类型追加到 byte slice 中。 -
func AppendUint(dst []byte, i uint64, base int) []byte
:将 uint 类型追加到 byte slice 中。 -
func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte
:将 float 类型追加到 byte slice 中。
以上是 strconv
包中常用的函数,它们可以方便地进行字符串和基本数据类型之间的转换。
Go语言的strconv
包提供了字符串和基本数据类型之间的转换函数,下面是strconv
包中常见的代码用法:
- 使用
strconv.Atoi()
将字符串转换为整数。
n, err := strconv.Atoi("123")
if err != nil {
panic(err)
}
fmt.Println("Parsed integer:", n)
- 使用
strconv.ParseFloat()
将字符串转换为浮点数。
f, err := strconv.ParseFloat("3.14", 64)
if err != nil {
panic(err)
}
fmt.Println("Parsed float:", f)
- 使用
strconv.ParseBool()
将字符串转换为布尔值。
b, err := strconv.ParseBool("true")
if err != nil {
panic(err)
}
fmt.Println("Parsed bool:", b)
- 使用
strconv.Itoa()
将整数转换为字符串。
s := strconv.Itoa(123)
fmt.Println("Converted string:", s)
- 使用
strconv.FormatFloat()
将浮点数转换为字符串。
s := strconv.FormatFloat(3.14, 'f', 2, 64)
fmt.Println("Converted string:", s)
- 使用
strconv.FormatBool()
将布尔值转换为字符串。
s := strconv.FormatBool(true)
fmt.Println("Converted string:", s)
以上是strconv
包中的一些常见代码用法,它们都可以用于处理字符串和基本数据类型之间的转换。
5.源码包学习计划之strings包
Go 语言的 strings
包提供了很多字符串操作函数。常用的函数包括:
-
func Contains(s, substr string) bool
:判断字符串 s 中是否包含子字符串 substr。 -
func Count(s, sep string) int
:统计字符串 s 中 sep 子字符串出现的次数。 -
func HasPrefix(s, prefix string) bool
:判断字符串 s 是否以 prefix 开头。 -
func HasSuffix(s, suffix string) bool
:判断字符串 s 是否以 suffix 结尾。 -
func Index(s, sep string) int
:查找字符串 sep 在字符串 s 中第一次出现的位置,返回索引值,未找到返回 -1。 -
func Join(a []string, sep string) string
:用 sep 分隔符将字符串数组 a 连接起来成为一个单独的字符串。 -
func Repeat(s string, count int) string
:将字符串 s 重复 count 次。 -
func Replace(s, old, new string, n int) string
:将字符串 s 中的前 n 个 old 子字符串替换为 new 字符串。 -
func Split(s, sep string) []string
:将字符串 s 按照 sep 分隔符进行分割,返回一个字符串数组。 -
func Trim(s string, cutset string) string
:将字符串 s 中前后端包含 cutset 中任一字符的所有字符去掉。 -
func TrimLeft(s string, cutset string) string
:将字符串 s 左端包含 cutset 中任一字符的所有字符去掉。 -
func TrimRight(s string, cutset string) string
:将字符串 s 右端包含 cutset 中任一字符的所有字符去掉。 -
func ToLower(s string) string
:将字符串 s 转换为小写。 -
func ToUpper(s string) string
:将字符串 s 转换为大写。
以上是 strings
包中常用的函数,它们可以方便地进行字符串操作。需要注意的是,字符串是不可变的,因此所有这些操作都返回一个新的字符串。
Go语言的strings
包提供了字符串处理函数,下面是strings
包中常见的代码用法:
- 使用
strings.Contains()
判断一个字符串是否包含另一个字符串。
s := "hello world"
if strings.Contains(s, "world") {
fmt.Println("Contains world")
}
- 使用
strings.Index()
查找子串在字符串中第一次出现的位置。
s := "hello world"
i := strings.Index(s, "world")
fmt.Println("Index of world:", i)
- 使用
strings.Split()
将字符串按照某个分隔符分割成多个子串。
s := "a,b,c"
parts := strings.Split(s, ",")
for _, p := range parts {
fmt.Println("Part:", p)
}
- 使用
strings.Join()
将多个字符串连接成一个字符串。
parts := []string{"a", "b", "c"}
s := strings.Join(parts, ",")
fmt.Println("Joined string:", s)
- 使用
strings.Replace()
替换字符串中的子串。
s := "hello world"
s = strings.Replace(s, "world", "gopher", -1)
fmt.Println("Replaced string:", s)
- 使用
strings.TrimSpace()
去除字符串两端的空白字符。
s := " hello world "
s = strings.TrimSpace(s)
fmt.Println("Trimmed string:", s)
以上是strings
包中的一些常见代码用法,它们都可以用于处理字符串。
6.源码包学习计划之sort包
Go语言的sort包提供了多个函数来对切片进行排序,包括:
- func Ints(a []int):对int类型的切片进行升序排序。
- func Float64s(a []float64):对float64类型的切片进行升序排序。
- func Strings(a []string):对string类型的切片进行升序排序。
- func Reverse(data Interface):对实现了sort.Interface接口的数据类型进行逆序排序。
- func Sort(data Interface):对实现了sort.Interface接口的数据类型进行升序排序。
- func IsSorted(data Interface):判断实现了sort.Interface接口的数据类型是否已经升序排好序。
其中sort.Interface接口需要实现Len()、Less(i, j int)和Swap(i, j int)三个方法,可以让自定义类型支持排序。
Go语言的sort包提供了对切片排序的支持,可以方便地对切片进行排序操作。下面是sort包的一些常见用法:
- 对切片进行排序
使用sort.Slice()或sort.SliceStable()函数可以对切片进行排序,例如:
a := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}
sort.Slice(a, func(i, j int) bool {
return a[i] < a[j]
})
fmt.Println(a) // 输出 [1 1 2 3 3 4 5 5 5 6 9]
- 自定义排序规则
使用sort.Slice()或sort.SliceStable()函数时,可以传入一个比较函数,自定义排序规则,例如:
type Person struct {
Name string
Age int
}
people := []Person{
{"Alice", 23},
{"Bob", 21},
{"Charlie", 25},
}
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println(people) // 输出 [{Bob 21} {Alice 23} {Charlie 25}]
- 对已排序的切片进行搜索
使用sort.Search()函数可以在已排序的切片中搜索元素,例如:
a := []int{1, 3, 5, 7, 9}
index := sort.Search(len(a), func(i int) bool {
return a[i] >= 5
})
fmt.Println(index) // 输出 2
以上是sort包的一些常见用法,它可以方便地对切片进行排序操作,使得开发者可以更加轻松地处理排序问题。
7.源码包学习计划之regexp包
Go语言的regexp包是用于处理正则表达式的包,提供了多个函数,包括:
- func Compile(expr string) (Regexp, error):编译正则表达式,返回一个Regexp对象和可能发生的错误。
- func MustCompile(str string) *Regexp:编译正则表达式,如果出错则直接panic。
- func (re *Regexp) FindString(s string) string:查找并返回第一个匹配到的字符串。
- func (re *Regexp) FindStringIndex(s string) []int:查找并返回第一个匹配到的字符串的开始和结束位置。
- func (re *Regexp) FindAllString(s string, n int) []string:查找并返回前n个匹配到的字符串。
- func (re *Regexp) FindAllStringIndex(s string, n int) [][]int:查找并返回前n个匹配到的字符串的开始和结束位置。
- func (re *Regexp) ReplaceAllString(src, repl string) string:将src中所有匹配到的字符串替换为repl。
- func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string:使用函数repl来替换src中所有匹配到的字符串。
- func (re *Regexp) Split(s string, n int) []string:根据正则表达式将s切分为多个字符串,最多切分n次。
- type Regexp:正则表达式类型,支持多种正则表达式操作方法。
这些函数和类型提供了处理正则表达式的基本操作,可以满足大部分的需求。
Go语言的regexp包提供了对正则表达式的支持,可以用来进行字符串匹配和替换等操作。下面是regexp包的一些常见用法:
- 编译正则表达式
使用Compile()函数可以将正则表达式字符串编译为正则表达式对象,例如:
pattern := `^[a-z]+\[[0-9]+\]$`
reg, err := regexp.Compile(pattern)
if err != nil {
fmt.Println(err)
}
- 判断是否匹配
使用Match()或MatchString()函数可以判断字符串是否匹配正则表达式,例如:
matched := reg.MatchString("abc[123]")
fmt.Println(matched) // 输出 true
- 查找匹配的字符串
使用FindString()或FindStringSubmatch()函数可以查找匹配的字符串,例如:
str := "abc[123]"
match := reg.FindString(str)
fmt.Println(match) // 输出 abc[123]
- 查找所有匹配的字符串
使用FindAllString()或FindAllStringSubmatch()函数可以查找所有匹配的字符串,例如:
str := "abc[123] def[456] ghi[789]"
matches := reg.FindAllString(str, -1)
fmt.Println(matches) // 输出 [abc[123] def[456] ghi[789]]
- 替换匹配的字符串
使用ReplaceAllString()函数可以替换匹配的字符串,例如:
str := "abc[123] def[456] ghi[789]"
replaced := reg.ReplaceAllString(str, "foo")
fmt.Println(replaced) // 输出 foo foo foo
以上是regexp包的一些常见用法,通过对正则表达式的支持,开发者可以轻松地进行字符串的匹配和替换等操作。
8.源码包学习计划之reflect包
Go语言的reflect包提供了一系列函数,用于在运行时动态地进行类型信息的获取和操作。主要包括以下函数:
- func TypeOf(i interface{}) Type:返回i的动态类型信息,即reflect.Type类型。
- func ValueOf(i interface{}) Value:返回i的动态值信息,即reflect.Value类型。
- func Indirect(v Value) Value:获取指针v指向的值,如果v不是指针,则返回v本身。
- func New(typ Type) Value:根据指定类型创建一个新的零值,并返回其动态值信息。
- func Zero(typ Type) Value:根据指定类型创建一个新的零值,并返回其动态值信息。
- func MakeSlice(typ Type, len, cap int) Value:创建一个指定类型的切片,并返回其动态值信息。
- func MakeMap(typ Type) Value:创建一个指定类型的映射表,并返回其动态值信息。
- func Append(slice Value, elems …Value) Value:向切片slice中追加元素,并返回新的切片的动态值信息。
- func FieldByName(v Value, name string) Value:根据字段名称获取结构体v中的字段的值。
- func MethodByName(v Value, name string) Value:根据方法名称获取类型v中的方法的值。
- func TypeOf(i interface{}) Type:返回i的动态类型信息,即reflect.Type类型。
除了这些函数,reflect包还提供了一些类型,例如reflect.Type、reflect.Value和reflect.Kind等,用于在运行时获取和操作变量的类型信息和值信息。通过使用这些函数和类型,我们可以在运行时实现一些非常灵活的操作,例如获取变量的类型信息、获取结构体字段的值、动态调用方法等。
reflect包是Go语言中的反射机制,它提供了在运行时动态获取变量类型、判断变量类型、获取结构体的字段信息等操作。下面是reflect包的一些常见用法:
- 获取变量的类型信息
使用reflect.TypeOf()函数可以获取变量的类型信息,返回值是reflect.Type类型的对象。例如:
var x int = 100
t := reflect.TypeOf(x)
fmt.Println(t.Name()) // 输出 int
- 获取变量的值信息
使用reflect.ValueOf()函数可以获取变量的值信息,返回值是reflect.Value类型的对象。例如:
var x int = 100
v := reflect.ValueOf(x)
fmt.Println(v.Int()) // 输出 100
- 获取结构体的字段信息
使用reflect.TypeOf()函数可以获取结构体类型信息,然后通过Field()方法可以获取结构体中的字段信息。例如:
type Person struct {
Name string
Age int
}
p := Person{"Tom", 20}
t := reflect.TypeOf(p)
field := t.Field(0)
fmt.Println(field.Name) // 输出 Name
fmt.Println(field.Type) // 输出 string
- 动态调用函数
使用reflect.ValueOf()函数可以获取函数的值信息,然后通过Call()方法可以动态调用函数。例如:
func add(a, b int) int {
return a + b
}
f := reflect.ValueOf(add)
result := f.Call([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)})
fmt.Println(result[0].Int()) // 输出 3
- 修改变量的值
使用reflect.ValueOf()函数可以获取变量的值信息,然后通过SetInt()、SetString()等方法可以修改变量的值。例如:
var x int = 100
v := reflect.ValueOf(&x).Elem()
v.SetInt(200)
fmt.Println(x) // 输出 200
以上是reflect包的一些常见用法,它可以实现一些非常灵活的操作,但也需要注意它的性能和使用场景。在实际应用中,应该根据具体需求和情况进行选择和使用。
9.源码包学习计划之path包
Go语言的path包提供了一些用于处理路径的函数。下面是path包的主要函数:
- func Base(path string) string:返回路径的最后一个元素。
- func Clean(path string) string:返回经过简化的路径,其中".“和”…"会被转换为相应的目录。
- func Dir(path string) string:返回路径除去最后一个元素后的目录。
- func Ext(path string) string:返回路径文件扩展名。
- func IsAbs(path string) bool:判断路径是否为绝对路径。
- func Join(elem …string) string:使用路径分隔符将所有元素连接为一个路径,并返回。
- func Match(pattern, name string) (matched bool, err error):判断name是否匹配pattern,其中pattern支持通配符。
- func Split(path string) (dir, file string):将路径分为目录和文件两部分,并返回。
以上是path包中的一些常用函数,可以方便地进行路径的处理和操作。
path包提供了一些常用的函数,可以方便地进行路径的处理和操作,下面是一些常见的用法:
- 获取路径的最后一个元素
使用Base()函数可以获取路径的最后一个元素,例如:
path := "/home/user/go/src"
fmt.Println(path.Base()) // 输出 src
- 简化路径
使用Clean()函数可以简化路径,其中".“和”…"会被转换为相应的目录,例如:
path := "/home/user/go/../go/src"
fmt.Println(path.Clean()) // 输出 /home/user/go/src
- 获取路径的目录
使用Dir()函数可以获取路径除去最后一个元素后的目录,例如:
path := "/home/user/go/src"
fmt.Println(path.Dir()) // 输出 /home/user/go
- 获取文件扩展名
使用Ext()函数可以获取路径文件扩展名,例如:
path := "/home/user/go/src/main.go"
fmt.Println(path.Ext()) // 输出 .go
- 判断路径是否为绝对路径
使用IsAbs()函数可以判断路径是否为绝对路径,例如:
path := "/home/user/go/src/main.go"
fmt.Println(path.IsAbs()) // 输出 true
- 连接多个元素为路径
使用Join()函数可以使用路径分隔符将所有元素连接为一个路径,并返回,例如:
fmt.Println(path.Join("/home/user", "go", "src")) // 输出 /home/user/go/src
- 判断路径是否匹配
使用Match()函数可以判断路径是否匹配,其中pattern支持通配符,例如:
matched, err := path.Match("/home/user/*.go", "/home/user/go/src/main.go")
fmt.Println(matched, err) // 输出 true <nil>
- 将路径分为目录和文件两部分
使用Split()函数可以将路径分为目录和文件两部分,并返回,例如:
dir, file := path.Split("/home/user/go/src/main.go")
fmt.Println(dir, file) // 输出 /home/user/go/src/ main.go
以上是path包的一些常见用法,它可以方便地进行路径的处理和操作,使得开发者可以更加轻松地处理文件路径。
10.源码包学习计划之os包
Go语言中的os包提供了许多函数来处理操作系统相关的操作,主要包括以下一些常用函数:
-
os.Open(file string) (*os.File, error):打开指定文件并返回对应的文件对象指针。如果操作失败会返回一个错误。
-
os.Create(file string) (*os.File, error):创建指定文件并返回对应的文件对象指针。如果文件已经存在,则截断文件内容,如果文件不存在,则创建文件。如果操作失败会返回一个错误。
-
os.Remove(name string) error:删除指定的文件或空目录。如果操作失败会返回一个错误。
-
os.RemoveAll(path string) error:递归删除指定目录及其所有子目录和文件。如果操作失败会返回一个错误。
-
os.Rename(oldpath, newpath string) error:将指定的文件或目录从旧路径移动到新路径。如果操作失败会返回一个错误。
-
os.Getwd() (dir string, err error):返回当前工作目录的路径。如果操作失败会返回一个错误。
-
os.Chdir(dir string) error:将当前工作目录更改为指定的目录。如果操作失败会返回一个错误。
-
os.Mkdir(name string, perm FileMode) error:创建指定名称的目录。如果操作失败会返回一个错误。
-
os.MkdirAll(path string, perm FileMode) error:递归创建指定名称的目录及其所有父目录。如果操作失败会返回一个错误。
-
os.Stat(name string) (os.FileInfo, error):返回指定文件或目录的元信息。如果操作失败会返回一个错误。
-
os.IsNotExist(err error) bool:判断指定错误是否表示文件或目录不存在。
-
os.IsExist(err error) bool:判断指定错误是否表示文件或目录已经存在。
-
os.Stdout:标准输出流对象。
-
os.Stderr:标准错误流对象。
-
os.Stdin:标准输入流对象。
这些函数可以满足大多数操作系统相关的需求。需要注意的是,这些函数可能会因为操作系统的不同而有所不同,所以在使用之前应该阅读相关文档。
os包是Go语言标准库中的一个重要包,提供了许多操作系统相关的函数。以下是一些常见的用法:
-
文件操作:os包提供了很多文件操作的函数,如打开文件(os.Open)、创建文件(os.Create)、删除文件(os.Remove)、重命名文件(os.Rename)、获取文件信息(os.Stat)等。这些函数能够满足大多数文件操作的需求。
-
目录操作:os包提供了创建目录(os.Mkdir)、递归创建目录(os.MkdirAll)、获取当前工作目录(os.Getwd)、改变当前工作目录(os.Chdir)等函数。
-
进程和信号:os包提供了获取当前进程ID(os.Getpid)、获取当前进程的父进程ID(os.Getppid)、发送信号(os.Signal)等函数。
-
环境变量:os包提供了获取环境变量(os.Getenv)、设置环境变量(os.Setenv)等函数。
-
标准输入输出流:os包提供了标准输入流(os.Stdin)、标准输出流(os.Stdout)、标准错误流(os.Stderr)等对象,可以方便地进行输入输出操作。
-
错误处理:os包提供了一些处理错误的函数,如判断错误是否表示文件或目录不存在(os.IsNotExist)、判断错误是否表示文件或目录已经存在(os.IsExist)等。
总之,os包提供了很多操作系统相关的函数,可以方便地进行文件操作、进程管理、环境变量设置等操作。需要注意的是,这些函数的行为可能会因为操作系统的不同而有所不同,所以在使用之前应该阅读相关文档。
11.源码包学习计划之math包
Go语言的math包提供了很多数学函数,包括以下一些常用函数:
-
Abs(x float64) float64:返回x的绝对值。
-
Ceil(x float64) float64:返回不小于x的最小整数值。
-
Floor(x float64) float64:返回不大于x的最大整数值。
-
Max(x, y float64) float64:返回x和y中的最大值。
-
Min(x, y float64) float64:返回x和y中的最小值。
-
Round(x float64) float64:返回最接近x的整数值,如果x恰好在两个整数之间,则返回偶数的那个整数。
-
Pow(x, y float64) float64:返回x的y次方。
-
Sqrt(x float64) float64:返回x的平方根。
-
Trunc(x float64) float64:返回x的整数部分。
-
Mod(x, y float64) float64:返回x除以y的余数。
-
Exp(x float64) float64:返回e的x次方。
-
Log(x float64) float64:返回x的自然对数。
-
Log10(x float64) float64:返回x的以10为底的对数。
-
Sin(x float64) float64:返回x的正弦值。
-
Cos(x float64) float64:返回x的余弦值。
-
Tan(x float64) float64:返回x的正切值。
-
Asin(x float64) float64:返回x的反正弦值。
-
Acos(x float64) float64:返回x的反余弦值。
-
Atan(x float64) float64:返回x的反正切值。
这些函数可以满足大多数数学计算的需求。需要注意的是,由于浮点数计算的误差问题,这些函数在处理较大或较小的数值时可能会出现一定的误差。如果需要更高精度的计算,可以考虑使用Go语言的big包。
以下是math包的一些常见代码用法:
- 计算绝对值
import "math"
x := -3.14
abs := math.Abs(x)
- 四舍五入
import "math"
x := 3.6
round := math.Round(x)
- 计算平方根
import "math"
x := 16.0
sqrt := math.Sqrt(x)
- 计算三角函数
import "math"
x := math.Pi / 6
sin := math.Sin(x)
cos := math.Cos(x)
tan := math.Tan(x)
asin := math.Asin(x)
acos := math.Acos(x)
atan := math.Atan(x)
- 计算指数和对数
import "math"
x := 2.0
y := 3.0
pow := math.Pow(x, y)
exp := math.Exp(x)
log := math.Log(x)
log10 := math.Log10(x)
- 获取最大值和最小值
import "math"
x := 10.0
y := 20.0
max := math.Max(x, y)
min := math.Min(x, y)
- 计算余数
import "math"
x := 7.0
y := 3.0
mod := math.Mod(x, y)
这些是math包的一些常见代码用法,可以满足大多数数学计算的需求。需要注意的是,由于浮点数计算的误差问题,这些函数在处理较大或较小的数值时可能会出现一定的误差。如果需要更高精度的计算,可以考虑使用Go语言的big包。
12.源码包学习计划之log包
Go语言的log包提供了一些用于记录日志信息的函数。常用的函数包括:
-
func Print(v …interface{}):输出v的字符串表示形式到标准日志记录器,以空格分隔。
-
func Printf(format string, v …interface{}):格式化输出v的字符串表示形式到标准日志记录器,格式由format指定。
-
func Println(v …interface{}):输出v的字符串表示形式到标准日志记录器,以换行符分隔。
-
func Fatal(v …interface{}):等同于Print,但在输出信息后会调用os.Exit(1)退出程序。
-
func Fatalf(format string, v …interface{}):等同于Printf,但在输出信息后会调用os.Exit(1)退出程序。
-
func Fatalln(v …interface{}):等同于Println,但在输出信息后会调用os.Exit(1)退出程序。
-
func Panic(v …interface{}):等同于Print,但在输出信息后会调用panic函数引发panic异常。
-
func Panicf(format string, v …interface{}):等同于Printf,但在输出信息后会调用panic函数引发panic异常。
-
func Panicln(v …interface{}):等同于Println,但在输出信息后会调用panic函数引发panic异常。
这些函数都会将日志信息记录到标准日志记录器(即os.Stderr),可以通过log.SetOutput函数修改输出目标。此外,还有一些其他的配置函数可以用于配置日志记录器的行为,例如log.SetFlags和log.SetPrefix等函数。
需要注意的是,log包提供的是基本的日志功能,如果需要更强大的日志记录功能,可以考虑使用第三方的日志库,例如zap、logrus等。
Go语言的log包提供了一个简单的接口来记录应用程序的日志信息。下面是一些常见的使用方式:
- 打印信息日志:
package main
import (
"log"
)
func main() {
log.Println("This is an info message.")
}
- 打印警告日志:
package main
import (
"log"
)
func main() {
log.Println("This is a warning message.")
}
- 打印错误日志:
package main
import (
"log"
)
func main() {
log.Println("This is an error message.")
}
- 打印自定义格式的日志:
package main
import (
"log"
)
func main() {
log.Printf("This is a custom message with value=%d", 42)
}
- 更改日志输出位置:
package main
import (
"log"
"os"
)
func main() {
file, err := os.Create("output.log")
if err != nil {
log.Fatal("Failed to create log file:", err)
}
defer file.Close()
log.SetOutput(file)
log.Println("This message will be written to the log file.")
}
- 更改日志前缀:
package main
import (
"log"
)
func main() {
log.SetPrefix("[MyApp] ")
log.Println("This message will have the prefix '[MyApp]'.")
}
- 更改日志输出格式:
package main
import (
"log"
"os"
)
func main() {
log.SetOutput(os.Stdout)
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
log.Println("This message will have a custom log format.")
}
以上是一些常见的使用方式,还有其他更高级的用法,例如自定义日志记录器、设置日志级别等。
13.源码包学习计划之io包
Go语言的io
包提供了一组接口和函数,用于进行I/O操作。以下是一些常用的函数:
-
io.Copy(dst io.Writer, src io.Reader) (written int64, err error)
:从源src
中读取数据并写入目标dst
中,返回写入的字节数和错误信息。 -
io.ReadFull(r io.Reader, buf []byte) (n int, err error)
:从r
中读取指定长度的字节到buf
中,返回读取的字节数和错误信息。 -
io.ReadAll(r io.Reader) ([]byte, error)
:从r
中读取所有可用的数据并返回读取的字节切片和错误信息。 -
io.WriteString(w io.Writer, s string) (n int, err error)
:将字符串s
写入w
中,返回写入的字节数和错误信息。 -
io.MultiReader(readers ...io.Reader) io.Reader
:将多个io.Reader
合并成一个,返回一个新的io.Reader
。 -
io.MultiWriter(writers ...io.Writer) io.Writer
:将多个io.Writer
合并成一个,返回一个新的io.Writer
。 -
io.NewSectionReader(r io.ReaderAt, off int64, n int64) *io.SectionReader
:创建一个io.Reader
接口,它读取指定长度的数据,并从偏移量off
开始读取数据。 -
io.Pipe() (*io.PipeReader, *io.PipeWriter)
:创建一个同步的管道,返回一个读取器和一个写入器,它们可以在不同的goroutine中使用。
这些是一些常用的io
包函数,还有一些其他的接口和函数可供使用。
Go语言的io
包提供了一组接口和函数,用于进行I/O操作。以下是一些常见的用法:
- 从文件中读取数据:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("file.txt")
if err != nil {
fmt.Println("Failed to read file:", err)
return
}
fmt.Println(string(data))
}
- 从标准输入读取数据:
package main
import (
"fmt"
"io"
"os"
)
func main() {
fmt.Print("Enter your name: ")
name, err := readFromStdin()
if err != nil {
fmt.Println("Failed to read from stdin:", err)
return
}
fmt.Println("Hello,", name)
}
func readFromStdin() (string, error) {
var buf [1024]byte
n, err := os.Stdin.Read(buf[:])
if err != nil {
return "", err
}
return string(buf[:n]), nil
}
- 将数据写入文件:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data := []byte("Hello, world!")
err := ioutil.WriteFile("file.txt", data, 0644)
if err != nil {
fmt.Println("Failed to write file:", err)
return
}
fmt.Println("File written successfully.")
}
- 将数据写入标准输出:
package main
import (
"fmt"
"os"
)
func main() {
data := []byte("Hello, world!")
_, err := os.Stdout.Write(data)
if err != nil {
fmt.Println("Failed to write to stdout:", err)
return
}
}
- 通过管道读取和写入数据:
package main
import (
"fmt"
"io"
"os"
)
func main() {
pr, pw := io.Pipe()
go func() {
defer pw.Close()
_, err := pw.Write([]byte("Hello, pipe!"))
if err != nil {
fmt.Println("Failed to write to pipe:", err)
}
}()
_, err := io.Copy(os.Stdout, pr)
if err != nil {
fmt.Println("Failed to read from pipe:", err)
}
}
这些是一些常见的io
包用法
14.源码包学习计划之ioutil包
Go语言的ioutil
包提供了一组函数,用于进行文件I/O操作。以下是一些常用的函数:
-
ioutil.ReadFile(filename string) ([]byte, error)
:读取指定文件的所有内容到内存中,并返回读取的字节切片和错误信息。 -
ioutil.WriteFile(filename string, data []byte, perm os.FileMode) error
:将指定的数据写入指定的文件中,并返回错误信息。 -
ioutil.TempFile(dir, prefix string) (f *os.File, err error)
:在指定目录下创建一个新的临时文件,并返回文件句柄和错误信息。文件名以指定的前缀开头。 -
ioutil.TempDir(dir, prefix string) (name string, err error)
:在指定目录下创建一个新的临时目录,并返回目录名和错误信息。目录名以指定的前缀开头。 -
ioutil.ReadDir(dirname string) ([]os.FileInfo, error)
:读取指定目录下的所有文件和子目录的信息,并返回一个文件信息切片和错误信息。
这些是一些常用的ioutil
包函数,还有一些其他的接口和函数可供使用。
Go语言的ioutil
包提供了一些函数,用于进行文件I/O操作。以下是一些常见的用法:
- 从文件中读取数据:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("file.txt")
if err != nil {
fmt.Println("Failed to read file:", err)
return
}
fmt.Println(string(data))
}
- 将数据写入文件:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data := []byte("Hello, world!")
err := ioutil.WriteFile("file.txt", data, 0644)
if err != nil {
fmt.Println("Failed to write file:", err)
return
}
fmt.Println("File written successfully.")
}
- 读取目录中的所有文件:
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
files, err := ioutil.ReadDir(".")
if err != nil {
fmt.Println("Failed to read directory:", err)
return
}
for _, file := range files {
if !file.IsDir() {
fmt.Println(file.Name())
}
}
}
- 创建临时文件:
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
fmt.Println("Failed to create temporary file:", err)
return
}
defer os.Remove(tmpfile.Name())
fmt.Println("Temporary file created:", tmpfile.Name())
}
这些是一些常见的ioutil
包用法
15.源码包学习计划之bytes包
Go语言的bytes
包提供了一系列函数,用于处理字节切片([]byte
)类型。以下是bytes
包中一些常用的函数:
func Contains(b, subslice []byte) bool
:判断切片b
是否包含子切片subslice
。func Count(s, sep []byte) int
:计算切片s
中出现子切片sep
的次数。func Equal(a, b []byte) bool
:比较两个切片a
和b
是否相等。func Index(s, sep []byte) int
:查找子切片sep
在切片s
中第一次出现的位置。func Join(s [][]byte, sep []byte) []byte
:使用分隔符sep
连接一个字节切片的切片s
并返回一个新的字节切片。func Repeat(b []byte, count int) []byte
:重复字节切片b
count
次并返回一个新的字节切片。func Replace(s, old, new []byte, n int) []byte
:将切片s
中的前n
个old
替换为new
,并返回一个新的字节切片。func Split(s, sep []byte) [][]byte
:将切片s
按照子切片sep
分割成多个子切片并返回一个切片的切片。func ToLower(s []byte) []byte
:将切片s
中所有的ASCII字母转换为小写字母,并返回一个新的字节切片。func ToUpper(s []byte) []byte
:将切片s
中所有的ASCII字母转换为大写字母,并返回一个新的字节切片。
此外,还有许多其他的函数可以在bytes
包中找到。可以通过在Go文档中查找bytes
包来获得完整的函数列表。
bytes
包在Go语言中常用于处理二进制数据,例如读取和写入文件,解析网络协议等。以下是bytes
包的一些常见代码用法:
- 创建和操作字节切片
package main
import (
"bytes"
"fmt"
)
func main() {
// 创建一个空字节切片
var b []byte
// 在字节切片末尾添加新元素
b = append(b, []byte("Hello, ")...)
b = append(b, []byte("world!")...)
// 将字节切片转换为字符串并打印
fmt.Println(string(b)) // Output: Hello, world!
// 查找子切片在切片中的位置
fmt.Println(bytes.Index(b, []byte("world"))) // Output: 7
// 替换子切片并打印结果
b = bytes.Replace(b, []byte("world"), []byte("Go"), -1)
fmt.Println(string(b)) // Output: Hello, Go!
}
- 操作字节缓冲区
package main
import (
"bytes"
"fmt"
)
func main() {
// 创建一个字节缓冲区
var buf bytes.Buffer
// 写入字符串到缓冲区
buf.WriteString("Hello, ")
buf.WriteString("world!")
// 将缓冲区中的内容转换为字节切片并打印
b := buf.Bytes()
fmt.Println(string(b)) // Output: Hello, world!
// 读取缓冲区中的内容
var s string
n, err := buf.Read([]byte(s))
if err == nil {
s = string(b[:n])
}
// 打印读取的内容
fmt.Println(s) // Output: Hello, world!
}
- 操作字节流
package main
import (
"bytes"
"fmt"
"io"
)
func main() {
// 创建一个字节缓冲区
var buf bytes.Buffer
// 写入字符串到缓冲区
buf.WriteString("Hello, world!")
// 将缓冲区中的内容读取到字节流中
r := bytes.NewReader(buf.Bytes())
// 读取字节流中的内容并打印
b := make([]byte, 8)
for {
n, err := r.Read(b)
if err == io.EOF {
break
}
fmt.Print(string(b[:n]))
}
// Output: Hello, world!
}
这些代码示例展示了bytes
包在处理字节切片、字节缓冲区和字节流方面的常见用法。这些功能可以帮助您完成许多常见的二进制数据操作。
16.源码包学习计划之context包
Go语言的context包提供了一些用于管理goroutine之间数据传递、请求取消、超时等相关的函数。
以下是一些常用的context包函数:
-
context.Background()
:创建一个空的Context,它不包含任何值。 -
context.WithValue(parent Context, key, val interface{}) Context
:返回一个父Context的副本,并将key/value对存储在其中。这个函数通常用于在goroutine之间传递请求特定的值,如跟踪ID或用户身份。 -
context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
:返回一个父Context的副本,并返回一个CancelFunc函数。一旦调用了cancel函数,任何使用此context的goroutine都将立即收到一个取消信号。 -
context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
:返回一个父Context的副本,并返回一个CancelFunc函数。如果在超时时间内没有调用cancel函数,那么任何使用此context的goroutine都将立即收到一个取消信号。 -
context.WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
:返回一个父Context的副本,并返回一个CancelFunc函数。如果在截止日期之前没有调用cancel函数,那么任何使用此context的goroutine都将立即收到一个取消信号。 -
context.TODO()
:创建一个空的Context,它不包含任何值。但与Background函数不同的是,使用此函数创建的Context应该是临时的,并且在将来可能会用实际值替换掉。
这些函数是context包中最常用的函数。使用它们可以更轻松地管理goroutine之间的通信和取消。
以下是一些常见的使用context包的代码用例:
- 使用WithValue在goroutine之间传递数据:
ctx := context.Background()
ctx = context.WithValue(ctx, "userID", 123)
go func(ctx context.Context) {
userID := ctx.Value("userID").(int)
// do something with userID
}(ctx)
- 使用WithCancel在goroutine之间传递取消信号:
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
// receive cancel signal
return
default:
// do something
}
}
}(ctx)
// call cancel function to send cancel signal
cancel()
- 使用WithTimeout控制goroutine超时:
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 10)
defer cancel() // ensure cancel is called to avoid context leak
go func(ctx context.Context) {
select {
case <-ctx.Done():
// receive timeout signal
return
default:
// do something
}
}(ctx)
- 使用WithDeadline控制goroutine执行截止时间:
deadline := time.Now().Add(time.Second * 30)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel() // ensure cancel is called to avoid context leak
go func(ctx context.Context) {
select {
case <-ctx.Done():
// receive deadline signal
return
default:
// do something
}
}(ctx)
这些示例演示了如何使用context包来在goroutine之间传递数据、控制超时和取消,以及控制goroutine执行截止时间。注意,对于所有使用context的代码,都需要注意避免上下文泄漏和及时调用取消函数,以确保正确的goroutine管理和资源释放。
17.源码包学习计划之errors包
Go语言的errors包提供了一个用于错误处理的接口和一些常用的函数。
以下是errors包中常用的函数:
-
errors.New(text string) error
:创建一个新的error实例,该实例包含给定的错误信息。 -
errors.Is(err, target error) bool
:检查错误链中是否包含给定的目标错误,返回true或false。 -
errors.As(err error, target interface{}) bool
:将给定的目标对象与错误链中的错误进行比较,并将匹配的错误的引用赋给目标对象。如果找到匹配的错误,返回true,否则返回false。 -
fmt.Errorf(format string, a ...interface{}) error
:创建一个格式化的error实例,使用类似于fmt.Printf的格式字符串和参数列表。
这些函数提供了一个方便的方法来创建和处理错误,比如创建一个自定义错误、检查一个错误是否包含某个特定的错误、将错误转换为特定类型的错误等。
需要注意的是,Go语言中约定错误信息字符串以小写字母开头,并且不以标点符号结尾。使用errors包时需要遵循这个约定,以确保代码的一致性和可读性。
以下是一些常见的使用errors包的代码用例:
- 创建一个自定义错误:
import "errors"
func MyFunc() error {
if someErrorCondition {
return errors.New("my custom error message")
}
// do something
return nil
}
- 检查一个错误是否包含特定的错误:
if errors.Is(err, io.EOF) {
// handle EOF error
} else if errors.Is(err, syscall.Errno(13)) {
// handle "permission denied" error
} else {
// handle other errors
}
- 将错误转换为特定类型的错误:
type MyError struct {
message string
}
func (e *MyError) Error() string {
return e.message
}
func MyFunc() error {
if someErrorCondition {
return &MyError{"my custom error message"}
}
// do something
return nil
}
func main() {
err := MyFunc()
var myErr *MyError
if errors.As(err, &myErr) {
// handle MyError
} else {
// handle other errors
}
}
这些示例演示了如何使用errors包来创建自定义错误、检查特定的错误和将错误转换为特定类型的错误。注意,在处理错误时,可以使用多种方法,但需要选择最适合当前情况的方法。使用errors包时需要注意遵循约定,以确保代码的一致性和可读性。
18.源码包学习计划之flag包
Go语言的flag包提供了一种解析命令行参数的简单方法。flag包定义了一个名为FlagSet的类型,其中包含了一些常用的函数来解析命令行参数。常用的flag包函数包括:
-
flag.BoolVar(p *bool, name string, value bool, usage string)
:定义一个bool类型的命令行标志,并将结果存储到指定的变量中。 -
flag.StringVar(p *string, name string, value string, usage string)
:定义一个字符串类型的命令行标志,并将结果存储到指定的变量中。 -
flag.IntVar(p *int, name string, value int, usage string)
:定义一个int类型的命令行标志,并将结果存储到指定的变量中。 -
flag.Float64Var(p *float64, name string, value float64, usage string)
:定义一个float64类型的命令行标志,并将结果存储到指定的变量中。 -
flag.Parse()
:解析命令行参数。 -
flag.Usage()
:输出命令行使用说明。
这些函数提供了一种方便的方法来定义和解析命令行参数。需要注意的是,在使用flag包时,应该先定义命令行标志,然后调用flag.Parse()来解析命令行参数。
以下是一些常见的使用flag包的代码用例:
- 定义命令行标志并解析:
import "flag"
func main() {
var name string
var age int
var isMale bool
flag.StringVar(&name, "name", "unknown", "your name")
flag.IntVar(&age, "age", 0, "your age")
flag.BoolVar(&isMale, "male", false, "your gender")
flag.Parse()
// use name, age, and isMale variables
}
这个例子定义了三个命令行标志,分别是名字(字符串类型)、年龄(整数类型)和性别(布尔类型),然后调用flag.Parse()解析命令行参数。
- 输出命令行使用说明:
import "flag"
import "fmt"
import "os"
func main() {
var name string
var age int
var isMale bool
flag.StringVar(&name, "name", "unknown", "your name")
flag.IntVar(&age, "age", 0, "your age")
flag.BoolVar(&isMale, "male", false, "your gender")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Options:\n")
flag.PrintDefaults()
}
flag.Parse()
// use name, age, and isMale variables
}
这个例子定义了三个命令行标志,并重写了flag.Usage函数来输出命令行使用说明。当用户在命令行上输入-h或–help选项时,flag.Usage函数将被调用。
需要注意的是,在使用flag包时,应该先定义命令行标志,然后调用flag.Parse()来解析命令行参数。另外,还应该注意命令行标志的类型、默认值和说明。
19.源码包学习计划之fmt包
Go语言中的fmt包提供了很多用于格式化输入和输出的函数。以下是fmt包中常用的一些函数:
Print
:输出字符串,不添加任何格式Printf
:按照格式化字符串的格式输出字符串Println
:输出字符串并换行Sprint
:将输入的参数格式化为字符串并返回Sprintf
:将输入的参数按照格式化字符串的格式格式化为字符串并返回Sprintln
:将输入的参数格式化为字符串并添加换行符后返回Fprint
:将字符串输出到指定的io.Writer接口中Fprintf
:将字符串按照格式化字符串的格式输出到指定的io.Writer接口中Fprintln
:将字符串输出到指定的io.Writer接口中并添加换行符Scan
:从标准输入中读取字符串并存入参数中Scanf
:从标准输入中读取字符串并按照格式化字符串的格式存入参数中Scanln
:从标准输入中读取字符串并以空格为分隔符存入参数中,并在读取完最后一个参数后停止。
以上是fmt包中一些常用的函数,还有一些其他的函数,如Fscan/Fscanf/Fscanln等等,可以在Go官方文档中查看具体用法。
以下是fmt包常见的代码用法:
- 格式化输出
可以使用fmt.Printf函数进行格式化输出,例如:
name := "Tom"
age := 25
fmt.Printf("My name is %s, and I'm %d years old.\n", name, age)
输出结果为:My name is Tom, and I'm 25 years old.
- 输出变量的值
可以使用fmt.Println函数输出变量的值,例如:
count := 10
fmt.Println("The count is", count)
输出结果为:The count is 10
- 格式化输入
可以使用fmt.Scanf函数进行格式化输入,例如:
var name string
var age int
fmt.Print("Enter your name: ")
fmt.Scanf("%s", &name)
fmt.Print("Enter your age: ")
fmt.Scanf("%d", &age)
fmt.Printf("Your name is %s, and you're %d years old.\n", name, age)
输入"Tom"和"25"后,输出结果为:Your name is Tom, and you're 25 years old.
- 将内容写入文件
可以使用fmt.Fprintf函数将内容写入文件,例如:
f, err := os.Create("example.txt")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
fmt.Fprintf(f, "This is an example file.")
以上代码将字符串"This is an example file."写入example.txt文件中。
这些只是fmt包中一些常见的代码用法,还有很多其他的用法,具体可以查看Go官方文档。
20.源码包学习计划之net/http包
Go语言中的net/http包提供了用于构建HTTP客户端和服务器的函数和类型。以下是net/http包中一些常用的函数:
- HTTP客户端函数
- Get():发送一个HTTP GET请求,并返回响应结果。
- Post():发送一个HTTP POST请求,并返回响应结果。
- PostForm():发送一个HTTP POST请求,表单数据编码为URL查询参数,并返回响应结果。
- Do():发送一个HTTP请求,并返回响应结果。
- Head():发送一个HTTP HEAD请求,并返回响应结果的头部信息。
- HTTP服务器函数
- ListenAndServe():监听HTTP网络地址并启动HTTP服务,使用默认的HTTP请求多路复用器处理HTTP请求。
- Handle():注册一个HTTP处理函数,将URL模式和处理函数关联。
- HandleFunc():注册一个HTTP处理函数,将URL模式和处理函数关联。
- HTTP请求处理函数
- ServeHTTP():HTTP请求处理函数的接口,处理HTTP请求并返回响应结果。
除了以上列出的函数之外,net/http包还提供了许多其他的函数和类型,如支持Cookie、TLS、重定向、代理等功能的函数和类型,以及一些用于处理HTTP请求和响应的类型。具体可以查看Go官方文档。
以下是net/http包常见的代码用法:
- 发送HTTP GET请求
可以使用http.Get函数发送HTTP GET请求,例如:
resp, err := http.Get("https://example.com")
if err != nil {
// 处理错误
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// 处理错误
}
fmt.Println(string(body))
以上代码将发送HTTP GET请求到https://example.com,并将响应的内容输出到控制台。
- 发送HTTP POST请求
可以使用http.Post函数发送HTTP POST请求,例如:
values := url.Values{}
values.Set("name", "Tom")
values.Set("age", "25")
resp, err := http.PostForm("https://example.com/post", values)
if err != nil {
// 处理错误
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// 处理错误
}
fmt.Println(string(body))
以上代码将发送HTTP POST请求到https://example.com/post,并将响应的内容输出到控制台。
- 启动HTTP服务器
可以使用http.ListenAndServe函数启动HTTP服务器,例如:
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
以上代码将注册一个HTTP处理函数handler,将URL模式"/“和处理函数关联,然后监听端口8080并启动HTTP服务器。当客户端访问http://localhost:8080/时,服务器将发送响应"Hello, World!”。
这些只是net/http包中一些常见的代码用法,还有很多其他的用法,具体可以查看Go官方文档。
21.源码包学习计划之testing包
Go语言中的testing包提供了一些函数和类型,用于编写测试程序。以下是testing包中一些常用的函数:
- 测试函数
- func TestXxx(t *testing.T):测试函数的命名必须以Test开头,且函数签名必须是func TestXxx(t *testing.T),其中Xxx是任意的字符串。测试函数中通过t参数进行测试,例如t.Errorf()用于打印错误信息。
- 断言函数
- func (c *T) Errorf(format string, args …interface{}):格式化输出错误信息,类似于fmt.Printf()函数。
- func (c *T) Fail():标记测试函数失败。
- func (c *T) FailNow():标记测试函数失败并立即停止执行。
- func (c *T) Log(args …interface{}):输出日志信息,类似于fmt.Print()函数。
- func (c *T) Fatal(args …interface{}):标记测试函数失败并立即停止执行。
- 子测试
- func (c *T) Run(name string, f func(t *T)) bool:运行一个子测试,并返回子测试是否成功。name参数是子测试的名称,f参数是子测试的函数。
除了以上列出的函数之外,testing包还提供了一些其他的函数和类型,如Benchmark函数用于基准测试、Example函数用于文档测试等。具体可以查看Go官方文档。
以下是testing包常见的代码用法:
- 编写测试函数
测试函数必须以Test开头,并且接受一个*testing.T类型的参数,例如:
func TestAdd(t *testing.T) {
if Add(2, 3) != 5 {
t.Errorf("Add(2, 3) should be 5")
}
}
以上代码测试了一个Add函数,如果Add(2, 3)的结果不是5,则输出错误信息。
- 运行测试程序
在Go语言中,测试程序是普通的Go程序,只是它们包含了一些测试函数。可以使用go test命令来运行测试程序,例如:
go test
以上命令将运行当前目录下的所有测试程序。也可以指定一个包名或测试文件名,例如:
go test package_name
go test file_name_test.go
还可以使用一些命令行参数来控制测试的行为,例如:
- -v:输出详细的测试信息;
- -run pattern:只运行名称匹配pattern的测试函数;
- -bench pattern:运行基准测试;
- -cover:输出测试覆盖率信息等。
- 子测试
使用testing.T类型的Run方法可以创建一个子测试,并在其中运行测试代码,例如:
func TestAdd(t *testing.T) {
t.Run("positive numbers", func(t *testing.T) {
if Add(2, 3) != 5 {
t.Errorf("Add(2, 3) should be 5")
}
})
t.Run("negative numbers", func(t *testing.T) {
if Add(-2, -3) != -5 {
t.Errorf("Add(-2, -3) should be -5")
}
})
}
以上代码创建了两个子测试,一个测试正数相加,一个测试负数相加。如果一个子测试失败,整个测试函数也将失败。
以上是testing包的一些常见代码用法,当然还有很多其他的用法,具体可以查看Go官方文档。
22.源码包学习计划之encoding/json包
Go语言标准库中的 encoding/json
包提供了许多函数和类型,用于将 JSON 数据编码为 Go 中的值或将 Go 中的值解码为 JSON 数据。以下是该包中常用的一些函数:
Marshal(v interface{}) ([]byte, error)
:将 Go 中的值v
编码为 JSON 字节切片。MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
:将 Go 中的值v
编码为具有指定前缀和缩进的 JSON 字节切片。Unmarshal(data []byte, v interface{}) error
:将 JSON 字节切片data
解码为 Go 中的值v
。NewEncoder(w io.Writer) *Encoder
:创建一个新的 JSON 编码器,它将 JSON 数据写入指定的io.Writer
中。NewDecoder(r io.Reader) *Decoder
:创建一个新的 JSON 解码器,它从指定的io.Reader
中读取 JSON 数据并将其解码为 Go 中的值。Valid(data []byte) bool
:检查给定的 JSON 字节切片是否为有效的 JSON 数据。
除了上述常用的函数外,encoding/json
包还提供了一些其他函数和类型,例如 RawMessage
类型和 HTMLEscape
函数等。请参阅官方文档以了解更多信息:https://golang.org/pkg/encoding/json/
下面是 encoding/json
包的一些常见代码用法:
- 将 Go 对象编码为 JSON 格式的字符串:
import "encoding/json"
type Person struct {
Name string
Age int
}
func main() {
p := Person{"Alice", 25}
b, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(string(b))
}
上述代码将 Person
结构体对象 p
编码为 JSON 格式的字符串,结果为 {"Name":"Alice","Age":25}
。
- 将 JSON 格式的字符串解码为 Go 对象:
import "encoding/json"
type Person struct {
Name string
Age int
}
func main() {
str := `{"Name":"Alice","Age":25}`
var p Person
err := json.Unmarshal([]byte(str), &p)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)
}
上述代码将 JSON 格式的字符串 str
解码为 Person
结构体对象 p
,并输出其中的字段值。
- 使用
RawMessage
类型将任意 JSON 数据解码为 Go 对象:
import "encoding/json"
type Message struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
func main() {
str := `{"type":"foo","data":{"key":"value"}}`
var msg Message
err := json.Unmarshal([]byte(str), &msg)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Printf("Type: %s, Data: %s\n", msg.Type, msg.Data)
}
上述代码使用 RawMessage
类型将任意的 JSON 数据解码为 Message
结构体对象 msg
。RawMessage
类型是一个字节切片,它可以存储任意的 JSON 数据,而不需要提前定义数据结构。在上面的例子中,Data
字段的类型为 RawMessage
,它将 JSON 数据存储为字节切片,以便稍后进一步处理。
这些是 encoding/json
包的一些常见代码用法,但是在实际应用中可能会有更多的用法和细节需要处理。建议查看官方文档以了解更多信息:https://golang.org/pkg/encoding/json/