Go语言,又称 Golang,是由 Google 开发的一门开源编程语言,以其独特的性能优势和简洁的语法设计,正在全球范围内成为热潮。本文将为读者提供一个迅速入门 Go 语言开发的指南。
无论是初学者还是有一定开发经验的开发者,通过本文整理的示例代码,都能轻松学习和巩固 Go 语言的常见编程用法。跟随本文,探索 Go 语言的优雅之处,快速拥抱这门现代化的编程语言。
优势介绍
Go语言是一门强大而高效的编程语言,适合于构建高性能、可靠性强的应用程序。主要优点如下:
- 语法设计简单且易于阅读,使得代码更易于理解和维护
- 编译器速度很快,编译时间短,开发效率高
- 编译型语言,运行速度接近于C和C++,适合开发高性能的服务端应用
- 自动垃圾回收机制,大大减少了内存泄漏和指针错误的可能性
- 为并发而生,Go提供了一种简洁的并发模型,使得并发编程变得更加容易和高效
安装Go语言环境
在官方网站(https://go.dev/)下载并安装适用于你操作系统的安装包。安装完成后,在命令行中输入 go version,正常显示版本号,则说明安装成功。
建议使用微软的 VS Code 作为开发工具,在官网(https://code.visualstudio.com/)下载安装即可。
注意,VS Code 可以通过插件的支持变得非常强大,如果它在需要对应的插件时自动提示,我们酌情选择安装即可。
如下载软件遇到困难,可私信作者,可发软件包和示例代码的网盘地址。
编写第一个Go程序
创建Go的学习文件夹,比如 D:\go_learn
用 VS Code 打开改文件夹,创建一个名为hello.go的文件,然后输入以下代码:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
保存文件,用 VS Code 新建终端,并在命令行执行以下命令:
go run hello.go
你将在命令行中看到输出:Hello, Go!
第一个Go程序就运行成功了。
以上代码中中的 fmt 是准库中的一个包,format的缩写,用于格式化输入输出。
变量和数据类型
Go是一门静态类型语言,但也支持短变量声明,编译器会根据值自动推断变量的类型。
新建文件 variable.go ,输入代码:
package main
import "fmt"
func main() {
// 使用var声明变量并赋值
var x int = 10
var y float64 = 3.14
var z string = "Go"
fmt.Println(x, y, z)
// 简短声明,根据值自动推断类型
a := 5
b := 2.7
c := "GoGo"
fmt.Println(a, b, c)
// 声明多个变量
var (
d, e = 40, "Hello"
f, g string
)
f = "GoGoGo"
fmt.Println(d, e, f, g)
// 类型转换
m := 1.1
n := int(m)
fmt.Println(n)
str := `多行
字符串`
fmt.Println(str)
// 其他数字类型
num1 := 3 // int
num2 := 3. // float64
num3 := 3 + 4i // complex128
num4 := byte('a') // byte (alias for uint8)
fmt.Println(num1, num2, num3, num4)
// 数组,长度固定,在声明时就被确定,不可改变
numbers := [...]int{0, 10, 20, 30, 40}
fmt.Println(numbers)
// 切片,长度可变,在处理动态大小的数据集合时更为灵活
slice := []int{2, 3, 4}
fmt.Println(slice)
}
运行命令 go run variable.go,输出内容如下:
10 3.14 Go
5 2.7 GoGo
40 Hello GoGoGo
1
多行
字符串
3 3 (3+4i) 97
[0 10 20 30 40]
[2 3 4]
条件和循环
Go中的条件语句和循环结构使用方式与其他编程语言类似,有if、for、switch等,但语法更为简洁。
新建文件 condition.go ,输入代码:
package main
import "fmt"
func main() {
// if条件语句
age := 20
if age >= 18 {
fmt.Println("成年人")
} else {
fmt.Println("未成年人")
}
// for循环结构
for i := 1; i <= 5; i++ {
fmt.Println(i)
}
// 遍历数组
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("索引:%d,值:%d\n", index, value)
}
}
执行后,输出如下:
成年人
1
2
3
4
5
索引:0,值:1
索引:1,值:2
索引:2,值:3
索引:3,值:4
索引:4,值:5
星期三
温暖的天气
数学运算
新建 math.go
package main
import (
"fmt"
"math"
)
func main() {
// 加法和减法
sum := 5 + 3
difference := 8 - 3
fmt.Printf("加法和减法:\n5 + 3 = %d\n8 - 3 = %d\n\n", sum, difference)
// 乘法和除法
product := 4 * 6
quotient := 10 / 2
fmt.Printf("乘法和除法:\n4 * 6 = %d\n10 / 2 = %d\n\n", product, quotient)
// 取余数
remainder := 15 % 4
fmt.Printf("取余数:\n15 %% 4 = %d\n\n", remainder)
// 指数运算
result := math.Pow(2, 3)
fmt.Printf("指数运算:\n2^3 = %f\n\n", result)
// 绝对值
absoluteValue := math.Abs(-7)
fmt.Printf("绝对值:\n|-7| = %f\n\n", absoluteValue)
// 开方
squareRoot := math.Sqrt(25)
fmt.Printf("开方:\n√25 = %f\n", squareRoot)
}
使用两个嵌套的循环来遍历输出一个九九乘法表。
新建文件:99.go
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%2d ", j, i, i*j)
}
fmt.Println()
}
}
函数
Go的函数可以像其他值一样被传递、赋值和作为参数和返回值。
新建文件:function.go
package main
import "fmt"
// 函数定义
func add(x, y int) int {
return x + y
}
// 多返回值的函数
func swap(x, y string) (string, string) {
return y, x
}
func main() {
// 函数调用
result := add(3, 5)
fmt.Println("3 + 5 =", result)
// 多返回值的函数调用
a, b := "Hello", "World"
a, b = swap(a, b)
fmt.Println(a, b)
}
输出如下:
3 + 5 = 8
World Hello
用户输入交互
通过 bufio (buffer input/output)这个包,可以用于对数据流进行缓冲读写。
这段代码示例通过 bufio.NewReader 创建了一个用于读取输入的读取器,然后使用 reader.ReadString 获取用户输入的名字和年龄。在读取用户年龄时,进行了简单的错误处理。最后,通过 fmt.Printf 输出。
新建文件 io.go:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 创建一个用于读取标准输入的读取器
reader := bufio.NewReader(os.Stdin)
// 提示用户输入名字
fmt.Print("请输入您的名字: ")
// 读取用户输入的名字
name, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取输入时发生错误:", err)
return
}
// 移除输入中的换行符
name = strings.TrimSpace(name)
// 输出欢迎消息
fmt.Printf("欢迎,%s!\n", name)
// 提示用户输入年龄
fmt.Print("请输入您的年龄: ")
// 读取用户输入的年龄
ageStr, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取输入时发生错误:", err)
return
}
// 移除输入中的换行符
ageStr = strings.TrimSpace(ageStr)
// 输入的年龄是字符串类型,这里将年龄转换为整数
var age int
_, err = fmt.Sscanf(ageStr, "%d", &age)
if err != nil {
fmt.Println("解析年龄时发生错误:", err)
return
}
// 输出用户输入的年龄
fmt.Printf("您输入的年龄是: %d\n", age)
}
字符串的处理
日常会有许多字符串相关的处理需求,如字符串的赋值、拼接、长度获取、索引访问、切片、遍历、比较和包含判断等基本操作。
新建 string.go,这次我们引入了 strings 包。
package main
import (
"fmt"
"strings"
)
func main() {
// 字符串赋值和拼接
str1, str2 := "Hello, ", "Go!"
result := str1 + str2
fmt.Println(result) // 输出: Hello, Go!
// 字符串长度
length := len(result)
fmt.Println(length) // 输出: 10
// 字符串索引访问
firstChar := result[0]
fmt.Println(firstChar) // 输出: 72 (ASCII码 'H')
// 字符串切片
subString := result[7:10]
fmt.Println(subString) // 输出: Go!
// 遍历字符串,获取每个字符的Unicode码点
for i, char := range str1 {
fmt.Printf("字符 %c 在字符串中的位置是 %d\n", char, i)
}
// 字符串相等性比较
isEqual := (str1 == str2)
fmt.Println(isEqual) // 输出: false
// 判断字符串是否包含子串
contains := strings.Contains(result, "Go")
fmt.Println(contains) // 输出: true
}
自行运行查看结果,输出内容已在代码注释中体现。
时间的处理
时间处理也是代码中非常常用的场景,以下是一些关于时间的获取、格式化、计算等。
新建文件:time.go
package main
import (
"fmt"
"time"
)
var pl = fmt.Println
func main() {
// 获取当前时间
now := time.Now()
pl("当前时间:", now)
pl(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
// 格式化时间
formattedTime := now.Format("2006-01-02 15:04:05")
pl("格式化后的时间:", formattedTime)
// 解析字符串为时间对象
parsedTime, err := time.Parse("2006-01-02 15:04:05", "2023-01-15 12:30:00")
if err != nil {
pl("解析时间出错:", err)
return
}
pl("解析后的时间:", parsedTime)
// 时间计算
oneHourLater := now.Add(time.Hour)
pl("一小时后的时间:", oneHourLater)
// 计算时间差
timeDiff := oneHourLater.Sub(now)
pl("时间差:", timeDiff)
// 判断两个时间是否相等
isEqual := now.Equal(parsedTime)
pl("当前时间是否等于解析后的时间:", isEqual)
}
补充说明:2006-01-02 15:04:05是 Go 语言的发布日期,被选为时间格式化的基准,因其中的数字是连续递增的,比较容易记忆,且清晰无歧义。
构建应用程序 及 命令行参数
到目前为止,我们代码程序都是需要在安装了Go环境的系统下运行,如果我们写好的代码希望在其他电脑运行,需要将其构建为应用程序。接下来这个示例将介绍如何构建EXE应用程序,以及如何使用命令行参数。
新建文件:clitest.go
package main
import (
"flag"
"fmt"
)
func main() {
// 定义命令行参数
var name string
var age int
var married bool
// 绑定命令行参数到变量
flag.StringVar(&name, "name", "", "姓名")
flag.IntVar(&age, "age", 0, "年龄")
flag.BoolVar(&married, "married", false, "婚姻状况")
// 解析命令行参数
flag.Parse()
// 输出解析后的结果
fmt.Println("姓名:", name)
fmt.Println("年龄:", age)
fmt.Println("婚姻状况:", married)
}
命令行参数的使用方式如下:
go run clitest.go -name 张三 -age 30 -married=true
以Windows系统为例,使用go build命令将其打包为exe程序
go build clitest.go
我们可以看到在clitest.go文件同级,生成了新的文件:clitest.exe
这个文件可在Windows电脑直接运行,如果我们需要在运行时加上参数:
./clitest -name 张三 -age 30 -married=true
Go module 包管理
Go Module 是 Go 语言从1.11版本开始引入的依赖管理工具。
首先新建一个文件夹作为项目目录:
mkdir goquery_project && cd goquery_project
在项目目录下,运行以下命令来初始化一个新的Go模块:
go mod init example.com/goqueryproject
这将创建一个名为go.mod的文件,其中包含了模块名称及Go的版本号。
添加外部依赖goquery(GoQuery是一个用于Go语言的HTML解析和查询库)。
go get github.com/PuerkitoBio/goquery
这个命令会下载goquery包,并将其添加到go.mod文件中。效果如下:
如果未能下载,说明终端连接不上Github,需自行解决。
常用 go mod 命令总结:
#下载依赖
go mod download
# 添加或更新依赖
go get <package>
# 清理和优化依赖
go mod tidy
# 查看依赖图
go mod graph
# 验证依赖
go mod verify
# 替换依赖
go mod edit -replace=<old-module>=<new-module>
# 删除替换的依赖
go mod edit -dropreplace=<module>
# 将依赖复制到vendor目录
go mod vendor
编写爬虫
在刚刚创建的 goquery_project 项目下新建文件 goquery.go
以抓取百度首页的热搜为示例代码,这里用到了 goquery 和 http 包。
package main
import (
"fmt"
"log"
"github.com/PuerkitoBio/goquery"
"net/http"
)
func main() {
// 设置请求头,模拟浏览器访问
client := &http.Client{}
req, err := http.NewRequest("GET", "https://www.baidu.com/", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3")
// 发送GET请求
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 使用goquery解析HTML
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Fatal(err)
}
// 查找热搜条目
hotSearchItems := doc.Find(".hotsearch-item")
// 遍历并打印热搜条目
hotSearchItems.Each(func(i int, s *goquery.Selection) {
title := s.Find(".title-content-title").Text()
fmt.Printf("热搜%d: %s\n", i+1, title)
})
}
运行结果:
这个例子非常基础,实际的网络爬虫可能需要处理更复杂的情况,例如解析HTML、处理cookies和session、处理重定向、设置请求头、处理速率限制、错误重试等。在编写爬虫时,应尊重网站的robots.txt文件和使用条款,并确保你的行为是合法和合理的,避免对服务器造成过大的负担。
并发的处理
Go 语言使用了 goroutines 和 channels 来实现多任务并行处理。
以下代码导入了 time 包用于模拟耗时操作。每个 worker goroutine 并行地处理接收到的任务,并将处理结果发送回结果通道。主程序负责分配任务和收集结果。这样可以在处理大量任务或耗时操作时提高程序的执行效率。
创建 worker.go
package main
import (
"fmt"
"time"
)
// 这是一个将在goroutine中运行的函数
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("worker %d started job %d\n", id, j)
time.Sleep(time.Second) // 模拟耗时操作
fmt.Printf("worker %d finished job %d\n", id, j)
results <- j * 2 // 将处理结果发送回结果通道
}
}
func main() {
jobs := make(chan int, 100) // 创建一个工作通道,缓冲区大小为100
results := make(chan int, 100) // 创建一个结果通道,缓冲区大小为100
// 启动3个worker goroutines
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 分配10个任务
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs) // 关闭jobs通道,表示没有更多任务了
// 等待所有结果
for a := 1; a <= 10; a++ {
<-results // 从结果通道接收一个结果
}
}
并发和爬虫结合是一个较常用的场景,以下是另一个示例:
package main
import (
"fmt"
"net/http"
"sync"
)
func download(url string, result chan<- []byte, wg *sync.WaitGroup) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
fmt.Printf("Error downloading %s: %v\n", url, err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading body for %s: %v\n", url, err)
return
}
result <- body
}
func main() {
urls := []string{
"https://www.example1.com",
"https://www.example2.com",
"https://www.example3.com",
// 添加更多要下载的URL...
}
resultsChan := make(chan []byte)
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go download(url, resultsChan, &wg)
}
go func() {
wg.Wait()
close(resultsChan)
}()
for body := range resultsChan {
fmt.Printf("Received content from a website:\n%s\n", body)
}
}
结语
至此,我们已经探索了Go语言的广阔世界,这篇精心编写的Go语言学习指南旨在为你提供一个坚实的学习基础和实用的开发工具箱。
学习编程语言是一个持续的过程,而掌握 Go 语言则是你开启高性能、高并发编程之旅的关键一步。希望这篇指南能为你照亮前行的道路,激发你的创新思维,提升你的编程能力。
请记住,理论与实践相结合是掌握任何技能的不二法门。因此,不要仅仅满足于阅读,更要将其知识融入你的日常编程实践中。
感谢你选择这篇Go语言学习指南作为你的学习伴侣。只要坚持不懈,勇于探索,你必将能在Go语言的世界中创造出属于自己的精彩。别忘了收藏这篇宝贵的学习资源,它将在你的编程之旅中持续发挥价值。欢迎留言交流。
加油,愿你在 Go 语言的学习旅程中取得成功!
本文暂时没有评论,来添加一个吧(●'◡'●)