创建项目:
创建一个文件夹(名字是golearn),进入文件夹,使用命令:go mod init golearn,这命令具体的执行内容后面再讲,最明显的是这个命令创建了一个go.mod文件,这个文件用于跟踪项目依赖的包,也能用于发布项目。
这个示例项目是创建一个网页,用于邀请大家来参加一个party,首先有一个欢迎界面,然后有一个表格界面用于填写要参加party的人的信息,以及对于填写了信息的人展示欢迎界面。
创建一个go文件(名字是learn1003.go):
写入代码:
gopackage main
import "fmt"
func main() {
fmt.Println("TODO:add some features")
}
main函数是整个项目的入口函数,每个项目有且仅有一个
自定义数据类型和集合
创建一个RSVP请求数据类型:
gopackage main
import "fmt"
type Rsvp struct{
Name,Email,Phone string
WillAttend bool
}
func main() {
fmt.Println("TODO:add some features")
}
使用make创建切片:
gopackage main
import "fmt"
type Rsvp struct{
Name,Email,Phone string
WillAttend bool
}
var responses = make([]*Rsvp,0,10)
func main() {
fmt.Println("TODO:add some features")
}
make用于创建数组,切片,字典
在上面使用make创建了一个初始化大小(size)为0,初始化容量(capacity)为10的切片
虽然初始化大小为0,但是当添加新元素的时候,切片会自动扩容,
创建一个HTML模板(party.html):
html<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Let's Party!</title>
<link href=
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.1/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body class="p-2">
{{ block "body" . }} Content Goes Here {{ end }}
</body>
</html>
模板中,{{}}内容是用于插入动态数据
创建欢迎模板(welcome.html):
html{{ define "body"}}
<div class="text-center">
<h3> We're going to have an exciting party!</h3>
<h4>And YOU are invited!</h4>
<a class="btn btn-primary" href="/form">
RSVP Now
</a>
</div>
{{ end }}
创建表格模板(form.html):
html{{ define "body"}}
<div class="h5 bg-primary text-white text-center m-2 p-2">RSVP</div>
{{ if gt (len .Errors) 0}}
<ul class="text-danger mt-3">
{{ range .Errors }}
<li>{{ . }}</li>
{{ end }}
</ul>
{{ end }}
<form method="POST" class="m-2">
<div class="form-group my-1">
<label>Your name:</label>
<input name="name" class="form-control" value="{{.Name}}" />
</div>
<div class="form-group my-1">
<label>Your email:</label>
<input name="email" class="form-control" value="{{.Email}}" />
</div>
<div class="form-group my-1">
<label>Your phone number:</label>
<input name="phone" class="form-control" value="{{.Phone}}" />
</div>
<div class="form-group my-1">
<label>Will you attend?</label>
<select name="willattend" class="form-select">
<option value="true" {{if .WillAttend}}selected{{end}}>
Yes, I'll be there
</option>
<option value="false" {{if not .WillAttend}}selected{{end}}>
No, I can't come
</option>
</select>
</div>
<button class="btn btn-primary mt-3" type="submit">
Submit RSVP
</button>
</form>
{{ end }}
创建感谢页面(thanks.html:
html{{ define "body"}}
<div class="text-center">
<h1>Thank you, {{ . }}!</h1>
<div> It's great that you're coming. The drinks are already in the fridge!</div>
<div>Click <a href="/list">here</a> to see who else is coming.</div>
</div>
{{ end }}
创建遗憾界面(sorry.html):
html{{ define "body"}}
<div class="text-center">
<h1>It won't be the same without you, {{ . }}!</h1>
<div>Sorry to hear that you can't make it, but thanks for letting us know.</div>
<div>
Click <a href="/list">here</a> to see who is coming,
just in case you change your mind.
</div>
</div>
{{ end }}
此界面用于对不能来参加的人标识遗憾
创建名单界面(list.html):
html{{ define "body"}}
<div class="text-center p-2">
<h2>Here is the list of people attending the party</h2>
<table class="table table-bordered table-striped table-sm">
<thead>
<tr><th>Name</th><th>Email</th><th>Phone</th></tr>
</thead>
<tbody>
{{ range . }}
{{ if .WillAttend }}
<tr>
<td>{{ .Name }}</td>
<td>{{ .Email }}</td>
<td>{{ .Phone }}</td>
</tr>
{{ end }}
{{ end }}
</tbody>
</table>
</div>
{{ end }}
用于展示参加聚会的人的信息
加载模板:
gopackage main
import (
"fmt"
"html/template"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
# 创建了一个以字符串为key,template.Template指针为值得字典
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func main() {
loadTemplates()
}
创建http处理服务:
gopackage main
import (
"fmt"
"html/template"
"net/http"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request) {
templates["welcome"].Execute(writer, nil)
}
func listHandler(writer http.ResponseWriter, request *http.Request) {
templates["list"].Execute(writer, responses)
}
func main() {
loadTemplates()
http.HandleFunc("/", welcomeHandler)
http.HandleFunc("/list", listHandler)
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request)
上面这个函数是用来处理http请求得函数,第二个参数是外部请求的指针
第一个参数是一个接口
http.HandleFunc("/", welcomeHandler):注册http请求处理函数
添加监听服务:
gopackage main
import (
"fmt"
"html/template"
"net/http"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request) {
templates["welcome"].Execute(writer, nil)
}
func listHandler(writer http.ResponseWriter, request *http.Request) {
templates["list"].Execute(writer, responses)
}
func main() {
loadTemplates()
http.HandleFunc("/", welcomeHandler)
http.HandleFunc("/list", listHandler)
err := http.ListenAndServe(":5000", nil)
if err != nil {
fmt.Println(err)
}
}
添加对表格的处理(get请求处理)
gopackage main
import (
"fmt"
"html/template"
"net/http"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request) {
templates["welcome"].Execute(writer, nil)
}
func listHandler(writer http.ResponseWriter, request *http.Request) {
templates["list"].Execute(writer, responses)
}
type foxData struct {
*Rsvp
Errors []string
}
func formHandler(writer http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
templates["form"].Execute(writer, foxData{
Rsvp: &Rsvp{},
Errors: []string{},
})
}
}
func main() {
loadTemplates()
http.HandleFunc("/", welcomeHandler)
http.HandleFunc("/list", listHandler)
http.HandleFunc("/form", formHandler)
err := http.ListenAndServe(":5000", nil)
if err != nil {
fmt.Println(err)
}
}
添加对表格的处理(post请求处理)
gopackage main
import (
"fmt"
"html/template"
"net/http"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request) {
templates["welcome"].Execute(writer, nil)
}
func listHandler(writer http.ResponseWriter, request *http.Request) {
templates["list"].Execute(writer, responses)
}
type foxData struct {
*Rsvp
Errors []string
}
func formHandler(writer http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
templates["form"].Execute(writer, foxData{
Rsvp: &Rsvp{},
Errors: []string{},
})
} else if request.Method == http.MethodPost {
request.ParseForm()
responseData := Rsvp{
Name: request.Form["name"][0],
Email: request.Form["email"][0],
Phone: request.Form["phone"][0],
WillAttend: request.Form["willattend"][0] == "true",
}
responses = append(responses, &responseData)
if responseData.WillAttend {
templates["thanks"].Execute(writer, responseData.Name)
} else {
templates["sorry"].Execute(writer, responseData.Name)
}
}
}
func main() {
loadTemplates()
http.HandleFunc("/", welcomeHandler)
http.HandleFunc("/list", listHandler)
http.HandleFunc("/form", formHandler)
err := http.ListenAndServe(":5000", nil)
if err != nil {
fmt.Println(err)
}
}
添加数据验证
gopackage main
import (
"fmt"
"html/template"
"net/http"
)
type Rsvp struct {
Name, Email, Phone string
WillAttend bool
}
var responses = make([]*Rsvp, 0, 10)
var templates = make(map[string]*template.Template, 3)
func loadTemplates() {
templateNames := [5]string{"welcome", "form", "thanks", "sorry", "list"}
for index, name := range templateNames {
t, err := template.ParseFiles("party.html", name+".html")
if err == nil {
templates[name] = t
fmt.Println("Loaded template", index, name)
} else {
panic(err)
}
}
}
func welcomeHandler(writer http.ResponseWriter, request *http.Request) {
templates["welcome"].Execute(writer, nil)
}
func listHandler(writer http.ResponseWriter, request *http.Request) {
templates["list"].Execute(writer, responses)
}
type foxData struct {
*Rsvp
Errors []string
}
func formHandler(writer http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
templates["form"].Execute(writer, foxData{
Rsvp: &Rsvp{},
Errors: []string{},
})
} else if request.Method == http.MethodPost {
request.ParseForm()
responseData := Rsvp{
Name: request.Form["name"][0],
Email: request.Form["email"][0],
Phone: request.Form["phone"][0],
WillAttend: request.Form["willattend"][0] == "true",
}
errors := []string{}
if responseData.Name == "" {
errors = append(errors, "please enter your name!")
}
if responseData.Email == "" {
errors = append(errors, "please enter your email!")
}
if responseData.Phone == "" {
errors = append(errors, "please enter your phone number!")
}
if len(errors) > 0 {
templates["form"].Execute(writer, foxData{
Rsvp: &responseData,
Errors: errors,
})
} else {
responses = append(responses, &responseData)
if responseData.WillAttend {
templates["thanks"].Execute(writer, responseData.Name)
} else {
templates["sorry"].Execute(writer, responseData.Name)
}
}
}
}
func main() {
loadTemplates()
http.HandleFunc("/", welcomeHandler)
http.HandleFunc("/list", listHandler)
http.HandleFunc("/form", formHandler)
err := http.ListenAndServe(":5000", nil)
if err != nil {
fmt.Println(err)
}
}
gopackage main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println(rand.Int())
}
使用import导入的时候,有两个关键:
- 使用import关键字
- 包的路径,可以是本地路径,也可以是github路径
基础数据类型:
常量分为两种:指定类型和非指定类型
指定类型就是在定义时指定了常量的类型:
gopackage main
import (
"fmt"
//"math/rand"
)
func main() {
const price float32 = 275.00
const tax float32 = 27.50
const quantity int = 2
fmt.Println("Total:", quantity * (price + tax))
}
上面就指定了quantity的常量类型为int,由于go中没有自动类型转换,当int和float32混合计算的时候,类型不一致,不能混合计算将导致报错:
invalid operation: quantity * (price + tax) (mismatched types int and float32)
这个时候使用非指定类型就方便一些了:
gopackage main
import (
"fmt"
//"math/rand"
)
func main() {
const price float32 = 275.00
const tax float32 = 27.50
const quantity = 2
fmt.Println("Total:", quantity * (price + tax))
}
上面两个例子的区别就是没有指定quantity的类型,由编译器自动推断,所以第二个例子就没有报错。
iota自增常量
gopackage main
import (
"fmt"
)
const (
one = iota + 1
two
three
four
five
)
func main() {
fmt.Println("one:", one)
fmt.Println("two:", two)
fmt.Println("three:", three)
fmt.Println("four:", four)
fmt.Println("five:", five)
}
同时定义多个常量:
goconst price,tax,quantity float32 = 275.00,27.50,2.33
const price,insock = 2,true
变量和常量的区别就是变量可以修改,而常量不行:
gopackage main
import "fmt"
func main() {
var price float32 = 275.00
var tax float32 = 27.50
fmt.Println(price + tax)
price = 300
fmt.Println(price + tax)
}
同理,变量在定义的时候也可以省略变量类型,起到非指定类型的效果
gopackage main
import "fmt"
func main() {
var price = 275.00
var price2 = price
fmt.Println(price)
fmt.Println(price2)
}
注意:go内部默认将浮点值转换为float64位,所以如果将非指定类型的浮点值与float32进行计算,会报错
定义变量但不初始化赋值
gopackage main
import "fmt"
func main() {
var price float32
fmt.Println(price)
price = 275.00
fmt.Println(price)
}
D:\goProject\golearn\lession2>go run basic.go
0
275
可以看出,虽然没有初始化赋值,但是自动赋值为类型0值
在一条语句中定义多个变量:
gopackage main
import "fmt"
func main() {
var price, tax = 275.00, 27.50
fmt.Println(price + tax)
}
D:\goProject\golearn\lession2>go run basic.go
302.5
重定义变量
变量一般是不允许再次声明定义的,所以下面的会报错
gopackage main
import "fmt"
func main() {
price, tax, inStock := 275.00, 27.50, true
fmt.Println("Total:", price+tax)
fmt.Println("In stock:", inStock)
var price2, tax = 200.00, 25.00
fmt.Println("Total 2:", price2+tax)
}
因为tax再之前已经使用短变量声明定义了
但是如果后面的不是使用var声明,而是也是使用短变量声明以及同时还有另一个未声明的变量,那么就可以重新定义,但是注意,变量类型不能变
gopackage main
import "fmt"
func main() {
price, tax, inStock := 275.00, 27.50, true
fmt.Println("Total:", price+tax)
fmt.Println("In stock:", inStock)
price2, tax := 200.00, 25.00
fmt.Println("Total 2:", price2+tax)
}
可以看见,和之前的差别就是使用短变量声明法以及同时还有一个未声明变量同时赋值
示例:
govar first int = 100
var second *int = &first
*int表示这个指针是整型指针,*表示这是一个指针,&是取地址符,&first表示结果是first的内存地址;上面总结就是定义一个整型指针,指针内容是first的地址
打印的区别:
gofmt.Println("second 内容",second) #输出first的地址(类似于0xc000010088);
fmt.Println("second 内容代表的值",*second) #输出first的值(100)
指针的计算:
shell*second++ 是可以的,等同于fitst++ var third *int = second 可以将指针的值赋值给另一个指针
指针的零值
gopackage main
import "fmt"
func main() {
first := 100
var second *int
fmt.Println(second)
second = &first
fmt.Println(second)
}
D:\goProject\golearn\lession2>go run basic.go
<nil>
0xc000012098
指针的零值是nil,并且如果在此时去读取指针地址的内容,那么将会报错:
gopackage main
import "fmt"
func main() {
first := 100
var second *int
fmt.Println(*second) # 读取nil指针的值内容
second = &first
fmt.Println(second)
}
D:\goProject\golearn\lession2>go run basic.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xbcc593]
goroutine 1 [running]:
main.main()
D:/goProject/golearn/lession2/basic.go:8 +0x33
exit status 2
指针的指针也是可以允许的:
gopackage main
import "fmt"
func main() {
first := 100
second := &first
third := &second
fmt.Println(first)
fmt.Println(*second)
fmt.Println(**third)
}
D:\goProject\golearn\lession2>go run basic.go
100
100
100
那么在读取的时候就需要两个读取符来读取地址内容了。
基础的计算,比较和逻辑运算是到处都会用到的。
由于go的严格类型规则,不允许不通类型的值进行同一个运算。
基本操作的操作符类似其它语言的操作符,但是类型转换可以使用内置函数或者第三方库。
基础计算符:
go计算操作:+ - * / %:加减乘除和取余
比较操作:"==" "!=" < <= > >= :对于两个数的大小比较
逻辑操作:|| && !:或并非;逻辑计算
赋值操作:= =: 前者是赋予变量值,后者是声明和赋值一起
增减操作:-= += ++ --
位操作:& | ^ &^ << >>
计算操作符除了取余操作符只能用于整型,其它的可以用于所有数值类型(float32 float64 int int32 int64...)。
计算操作只能用于同一个类型或者能够被同一个类型表示的两个值。
golang允许数值溢出,这在其它一些语言中会报错。
示例:
gopackage main
import (
"fmt"
"math"
)
func main() {
var intVal = math.MaxInt64
var floatVal = math.MaxFloat64
fmt.Println(intVal * 2)
fmt.Println(floatVal * 2)
fmt.Println(math.IsInf((floatVal * 2), 0))
}
D:\goProject\golearn\lession2>go run basic.go
-2
+Inf # 溢出到正无穷大
true
go中的取余操作和python中的取模操作类似,但是go的取余操作可以对负整数使用。
gopackage main
import (
"fmt"
"math"
)
func main() {
posResult := 3 % 2
negResult := -3 % 2
absResult := math.Abs(float64(negResult))
fmt.Println(posResult)
fmt.Println(negResult)
fmt.Println(absResult)
}
D:\goProject\golearn\lession2>go run basic.go
1
-1
1
可以使用操作符"+"将两个string值拼接在一起:
gopackage main
import (
"fmt"
// "math"
)
func main() {
greeting := "Hello"
language := "Go"
combinedString := greeting + ", " + language
fmt.Println(combinedString)
}
D:\goProject\golearn\lession2>go run .
Hello, Go
比较运算必须在两个相同的数据类型之间比较,或者都是非指定类型变量或常量,并且可以看作更大类型。
示例:
gopackage main
import (
"fmt"
// "math"
)
func main() {
first := 100
const second = 200.00
equal := first == second
notEqual := first != second
lessThan := first < second
lessThanOrEqual := first <= second
greaterThan := first > second
greaterThanOrEqual := first >= second
fmt.Println(equal)
fmt.Println(notEqual)
fmt.Println(lessThan)
fmt.Println(lessThanOrEqual)
fmt.Println(greaterThan)
fmt.Println(greaterThanOrEqual)
}
D:\goProject\golearn\lession2>go run .
false
true
true
true
false
false
上面的示例中,因为second是非指定类型常量,并且小数点后为0,可以看作更大类型的整型,而且first也可以看作更大类型的整型,所以可以放在一起比较,如果second是200.01,那就会报错:
goD:\goProject\golearn\lession2>go run .
# lession2
.\basic.go:11:17: constant 200.01 truncated to integer
.\basic.go:12:20: constant 200.01 truncated to integer
.\basic.go:13:20: constant 200.01 truncated to integer
.\basic.go:14:27: constant 200.01 truncated to integer
.\basic.go:15:23: constant 200.01 truncated to integer
.\basic.go:16:30: constant 200.01 truncated to integer
通过比较运算可以判断两个指针是否指向同一个地址。
gopackage main
import (
"fmt"
// "math"
)
func main() {
first := 100
second := &first
third := &first
alpha := 100
beta := &alpha
fmt.Println(second == third)
fmt.Println(second == beta)
}
D:\goProject\golearn\lession2>go run .
true
false
|| 或,A || B,如果A计算结果是真,那么B就不计算了;A和B至少一个是真,结果为真,都为假,结果为假
&& 与,A && B,A和B同时为真,结果为真,如果其中一个为假,则结果为假;如果A计算结果为假,那么结果为假,B不计算。
! 非,取反,!A,若A计算结果为真,则整个计算结果取反为假,若A计算结果为假,取反,则整个计算结果为真。
示例:
gopackage main
import (
"fmt"
// "math"
)
func main() {
maxMph := 50
passengerCapacity := 4
airbags := true
familyCar := passengerCapacity > 2 && airbags
sportsCar := maxMph > 100 || passengerCapacity == 2
canCategorize := !familyCar && !sportsCar
fmt.Println(familyCar)
fmt.Println(sportsCar)
fmt.Println(canCategorize)
}
D:\goProject\golearn\lession2>go run .
true
false
false
go不允许不同的数值类型在计算中混用,也不会自动转换数值类型,除非是未定义类型变量。如果混用就会报错,错误的示例如下:
gopackage main
import (
"fmt"
// "math"
)
func main() {
kayak := 275
soccerBall := 19.50
total := kayak + soccerBall
fmt.Println(total)
}
D:\goProject\golearn\lession2>go run .
# lession2
.\basic.go:11:17: invalid operation: kayak + soccerBall (mismatched types int and float64)
如上所示,错误提示是int和float64混用。
可以通过显示类型转换手动将数值转换为想要计算的数值类型,示例如下:
gopackage main
import (
"fmt"
// "math"
)
func main() {
kayak := 275
soccerBall := 19.50
total := float64(kayak) + soccerBall
fmt.Println(total)
}
D:\goProject\golearn\lession2>go run .
294.5
上面通过使用float64(kayak)转换成float64类型。
显示转换的表达式是T(x),T是目标类型,x当前的变量名或值
显式转换只能在当前值能够被目标类型表达的时候进行,比如两个数值类型可以显式转化,字符串可以和runes之间可以转换;但是想把整型转换成布尔型就不行了。
显示转换可能会丢失精确度或者导致数值溢出,要注意。
示例:
gopackage main
import (
"fmt"
// "math"
)
func main() {
kayak := 275
soccerBall := 19.50
total := kayak + int(soccerBall)
fmt.Println(total)
fmt.Println(int8(total))
}
D:\goProject\golearn\lession2>go run .
294
38
如上所示,19.50转换成整型变成了19,而int类型转换为int8类型就数值溢出了。
将浮点值转换为整型是有风险的,不过使用math包里面的转换函数就比较安全。
注意,上述函数的参数都是float64类型,返回的都是float64类型,只是返回的小数部分是0(比如:math.Round(19.32)返回19.00),所以仍需要进行显式转换为int类型才能与int类型进行计算
gopackage main
import (
"fmt"
"math"
)
func main() {
kayak := 275
soccerBall := 19.50
total := kayak + int(math.Round(soccerBall))
fmt.Println(total)
}
D:\goProject\golearn\lession2>go run .
295
go的标准库中有strconv包用来将string类型转换为其它类型。
包中的函数如下所示:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "true"
val2 := "false"
val3 := "not true"
bool1, b1err := strconv.ParseBool(val1)
bool2, b2err := strconv.ParseBool(val2)
bool3, b3err := strconv.ParseBool(val3)
fmt.Println("Bool 1", bool1, b1err)
fmt.Println("Bool 2", bool2, b2err)
fmt.Println("Bool 3", bool3, b3err)
}
D:\goProject\golearn\lession2>go run .
Bool 1 true <nil>
Bool 2 false <nil>
Bool 3 false strconv.ParseBool: parsing "not true": invalid syntax
字符串转为整型使用的是strconv.ParseInt(str,base,size);其中str是字符串,base是进制,size是多少位能容纳下转换后的数值,但是注意,这个函数返回的类型是int64,比如500,8位二进制最大时256,所以strconv.Parse("500",0,8)就会报错,因为8位容纳不下500这个值。示例如下:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "100"
int1, int1err := strconv.ParseInt(val1, 0, 8)
if int1err == nil {
fmt.Println("Parsed value:", int1)
} else {
fmt.Println("Cannot parse", val1)
}
}
D:\goProject\golearn\lession2>go run .
Parsed value: 100
# 将字符串内容改为"500":D:\goProject\golearn\lession2>go run .
package main
import (
"fmt"
"strconv"
)
func main() {
val1 := "500"
int1, int1err := strconv.ParseInt(val1, 0, 8)
if int1err == nil {
fmt.Println("Parsed value:", int1)
} else {
fmt.Println("Cannot parse", val1, int1err)
}
}
D:\goProject\golearn\lession2>go run .
Cannot parse 500 strconv.ParseInt: parsing "500": value out of range
由于strconv.ParseInt返回的是Int64类型,但是我们不需要这么大的类型,只需要比如int8类型,那么当size设置为8的时候,如果能够转换成功,那么再使用int8(result)来再次转换是非常安全的。
下面的示例,将100视为二进制的数用来转换,转换为整数的结果是4:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "100"
int1, int1err := strconv.ParseInt(val1, 2, 8)
if int1err == nil {
smallInt := int8(int1)
fmt.Println("Parsed value 10:", smallInt)
} else {
fmt.Println("Cannot parse", val1, int1err)
}
}
D:\goProject\golearn\lession2>go run .
Parsed value 10: 4
使用前缀判断字符串内容是什么进制:
示例:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "0b1100100"
int1, int1err := strconv.ParseInt(val1, 0, 8)
if int1err == nil {
smallInt := int8(int1)
fmt.Println("Parsed value:", smallInt)
} else {
fmt.Println("Cannot parse", val1, int1err)
}
}
D:\goProject\golearn\lession2>go run .
Parsed value: 100
转换为十进制数有两种办法,一种是使用ParseInt(str,10,size),一种是Atoi(str),如下:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "100"
int1, int1err := strconv.ParseInt(val1, 10, 0)
if int1err == nil {
var intResult int = int(int1)
fmt.Println("Parsed value:", intResult)
} else {
fmt.Println("Cannot parse", val1, int1err)
}
}
package main
import (
"fmt"
"strconv"
)
func main() {
val1 := "100"
int1, int1err := strconv.Atoi(val1)
if int1err == nil {
var intResult int = int1
fmt.Println("Parsed value:", intResult)
} else {
fmt.Println("Cannot parse", val1, int1err)
}
}
其实可以把Atoi看错strconv.ParseInt(str,10,0)
主要是使用ParseFloat(str,size)函数,其中str是待转换的字符串,size是指定转换后的位数,但是注意,和ParseInt类似,ParseFLoat返回的是float64类型,如果将size指定为32,那么显式转换为float32的时候,不会有精度损失。
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "48.95"
float1, float1err := strconv.ParseFloat(val1, 64)
if float1err == nil {
fmt.Println("Parsed value:", float1)
} else {
fmt.Println("Cannot parse", val1, float1err)
}
}
D:\goProject\golearn\lession2>go run .
Parsed value: 48.95
将科学计数法的字符串转为浮点值:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := "4.895e+01"
float1, float1err := strconv.ParseFloat(val1, 64)
if float1err == nil {
fmt.Println("Parsed value:", float1)
} else {
fmt.Println("Cannot parse", val1, float1err)
}
}
D:\goProject\golearn\lession2>go run .
Parsed value: 48.95
将数值转为字符串主要使用以下几个函数:
FormatBool接收一个布尔值,返回其代表值的字符串:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val1 := true
val2 := false
str1 := strconv.FormatBool(val1)
str2 := strconv.FormatBool(val2)
fmt.Println("Formatted value 1: " + str1)
fmt.Println("Formatted value 2: " + str2)
}
D:\goProject\golearn\lession2>go run .
Formatted value 1: true
Formatted value 2: false
FormatInt接收两个参数,一个是int64类型的整数,一个是这个整数转换后的进制;
示例:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val := 275
base10String := strconv.FormatInt(int64(val), 10)
base2String := strconv.FormatInt(int64(val), 2)
fmt.Println("Base 10: " + base10String)
fmt.Println("Base 2: " + base2String)
}
D:\goProject\golearn\lession2>go run .
Base 10: 275
Base 2: 100010011
也可以使用Itoa函数来转化,Itoa函数可视为:ForamtInt(int64(var),10),示例:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val := 275
base10String := strconv.Itoa(val)
base2String := strconv.FormatInt(int64(val), 2)
fmt.Println("Base 10: " + base10String)
fmt.Println("Base 2: " + base2String)
}
D:\goProject\golearn\lession2>go run .
Base 10: 275
Base 2: 100010011
浮点数需要指定更多的参数和不同的格式,示例:
gopackage main
import (
"fmt"
"strconv"
)
func main() {
val := 49.95
Fstring := strconv.FormatFloat(val, 'f', 2, 64)
Estring := strconv.FormatFloat(val, 'e', -1, 64)
fmt.Println("Format F: " + Fstring)
fmt.Println("Format E: " + Estring)
}
D:\goProject\golearn\lession2>go run .
Format F: 49.95
Format E: 4.995e+01
FormatFloat函数有4个参数:
分类:
gopackage main
import "fmt"
func main() {
kayakPrice := 275.00
if kayakPrice > 100 {
fmt.Println("Price is greater than 100")
}
}
D:\goProject\golearn\lession2>go run .
Price is greater than 100
使用格式:
goif 条件判断 {
执行语句
}
条件判断必须在同一行中
gopackage main
import "fmt"
func main() {
kayakPrice := 275.00
if kayakPrice < 200 {
fmt.Println("Price is lower than 200")
} else if kayakPrice > 100 {
fmt.Println("Price is bigger than 100")
}
}
D:\goProject\golearn\lession2>go run .
Price is bigger than 100
package main
import "fmt"
func main() {
kayakPrice := 275.00
if kayakPrice < 200 {
fmt.Println("Price is lower than 200")
} else {
fmt.Println("Price is bigger than 200")
}
}
D:\goProject\golearn\lession2>go run .
Price is bigger than 200
if的作用域就在if的两个大括号之间,所以如果在这之间定义了变量,那么在语句结束之后,变量也就失效,所以可以在if和else if中重复定义变量
shellpackage main import "fmt" func main() { kayakPrice := 275.00 if kayakPrice > 500 { scopedVar := 500 fmt.Println("Price is greater than", scopedVar) } else if kayakPrice < 100 { scopedVar := "Price is less than 100" fmt.Println(scopedVar) } else { scopedVar := false fmt.Println("Matched: ", scopedVar) } } D:\goProject\golearn\lession2>go run . Matched: false 上面在if和else if和else中都定义了scopedVar变量,并没有报错
gopackage main
import (
"fmt"
"strconv"
)
func main() {
priceString := "275"
if kayakPrice, err := strconv.Atoi(priceString); err == nil {
fmt.Println("Price:", kayakPrice)
} else {
fmt.Println("Error:", err)
}
fmt.Println(kayakPrice)
}
D:\goProject\golearn\lession2>go run .
# lession2
.\basic.go:15:14: undefined: kayakPrice
注意,在if条件语句中定义并初始化的变量,在if外是不能使用的
但是如果是在if外定义的,在if条件语句中初始化,那么就可以在if外使用
go中只有一个循环就是for循环,没有while这些循环
for循环格式:
gofor 初始化语句;条件语句;增长语句 {
执行语句
}
示例:
for i:=1;i<10;i++{
fmt.Println("i=",i)
}
package main
import (
"fmt"
//"strconv"
)
func main() {
for i := 1; i < 10; i++ {
fmt.Println("i=", i)
}
}
D:\goProject\golearn\lession2>go run .
i= 1
i= 2
i= 3
i= 4
i= 5
i= 6
i= 7
i= 8
i= 9
当for的3个语句就是空的时候,就是无限循环,需要使用break才能退出循环
3个语句也不一定全部都有,可以只有一部分,如下:
package main
import (
"fmt"
//"strconv"
)
func main() {
counter := 0
for counter <= 3 {
fmt.Println("Counter:", counter)
counter++
// if (counter > 3) {
// break
// }
}
}
D:\goProject\golearn\lession2>go run .
Counter: 0
Counter: 1
Counter: 2
Counter: 3
这两个和其它语言的作用是一样的
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
fmt.Println("Index:", index, "Character:", string(character))
}
}
D:\goProject\golearn\lession2>go run .
Index: 0 Character: K
Index: 1 Character: a
Index: 2 Character: y
Index: 3 Character: a
Index: 4 Character: k
如果取消输出中的显式string转换:
D:\goProject\golearn\lession2>go run .
Index: 0 Character: 75
Index: 1 Character: 97
Index: 2 Character: 121
Index: 3 Character: 97
Index: 4 Character: 107
这是因为for将字符串视为一个个rune组成的列表
rune的底层是int32类型,byte底层是uint8类型,在处理英文字符的时候,两者差别不大,但是在处理中文以及特殊字符的时候就有区别了。
byte代表一个ascii字符,rune代表一个utf-8字符。
示例:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
str := "hello 世界"
fmt.Println(len(str))
str2 := []rune(str)
fmt.Println(len(str2))
for i := 0; i < len(str); i++ {
fmt.Print(string(str[i]))
}
fmt.Println("\n----------")
for _, v := range str {
fmt.Print(string(v))
}
}
D:\goProject\golearn\lession2>go run .
12
8
hello ä¸ç
----------
hello 世界
可以看见使用byte来计算长度的时候,计算的是底层字节长度,在计算ascii码的时候还可以,但是涉及中文就不行了
使用rune来计算的时候,计算的是utf-8编码的字节长度,可以很方便的计算中文等特殊字符的长度。
遍历的时候,使用for..range更好,因为for..range会隐式的unicode解码
switch提供另一种方式来控制流程,基于匹配表达式结果和一个特定的值:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
switch character {
case 'K':
fmt.Println("K at position", index)
case 'y':
fmt.Println("y at position", index)
}
}
}
D:\goProject\golearn\lession2>go run .
K at position 0
y at position 2
switch可以匹配多个值:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
switch character {
case 'K', 'k':
fmt.Println("K or k at position", index)
case 'y':
fmt.Println("y at position", index)
}
}
}
D:\goProject\golearn\lession2>go run .
K or k at position 0
y at position 2
K or k at position 4
中止switch的执行:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
switch character {
case 'K', 'k':
if character == 'k' {
fmt.Println("Lowercase k at position", index)
break
}
fmt.Println("Uppercase K at position", index)
case 'y':
fmt.Println("y at position", index)
}
}
}
D:\goProject\golearn\lession2>go run .
Uppercase K at position 0
y at position 2
Lowercase k at position 4
注意:break在打印了小写的k之后中止,退出了switch循环,但是没有退出for循环,所以让然会对下一个字符进行判断
fallthrough是可以强制执行下一个case中的语句:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
switch character {
case 'K':
fmt.Println("Uppercase character")
fallthrough
case 'p':
fmt.Println("not match but fall through")
case 'y':
fmt.Println("y at position", index)
}
}
}
D:\goProject\golearn\lession2>go run .
Uppercase character
not match but fall through
y at position 2
当所有case都没有匹配的时候,就可以使用default来接收:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
product := "Kayak"
for index, character := range product {
switch character {
case 'K', 'k':
if character == 'k' {
fmt.Println("Lowercase k at position", index)
break
}
fmt.Println("Uppercase K at position", index)
case 'y':
fmt.Println("y at position", index)
default:
fmt.Println("Character", string(character), "at position", index)
}
}
}
D:\goProject\golearn\lession2>go run .
Uppercase K at position 0
Character a at position 1
y at position 2
Character a at position 3
Lowercase k at position 4
可以在switch的条件语句中添加初始化语句,用于每次循环:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
for counter := 0; counter < 20; counter++ {
switch val := counter / 2; val {
case 2, 3, 5, 7:
fmt.Println("Prime value:", val)
default:
fmt.Println("Non-prime value:", val)
}
}
}
D:\goProject\golearn\lession2>go run .
Non-prime value: 0
Non-prime value: 0
Non-prime value: 1
Non-prime value: 1
Prime value: 2
Prime value: 2
Prime value: 3
Prime value: 3
Non-prime value: 4
Non-prime value: 4
Prime value: 5
Prime value: 5
Non-prime value: 6
Non-prime value: 6
Prime value: 7
Prime value: 7
Non-prime value: 8
Non-prime value: 8
Non-prime value: 9
Non-prime value: 9
可以将比较的语句放在case中,达到更多更灵活的使用:
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
for counter := 0; counter < 10; counter++ {
switch {
case counter == 0:
fmt.Println("Zero value")
case counter < 3:
fmt.Println(counter, "is < 3")
case counter >= 3 && counter < 7:
fmt.Println(counter, "is >= 3 && < 7")
default:
fmt.Println(counter, "is >= 7")
}
}
}
Zero value
1 is < 3
2 is < 3
3 is >= 3 && < 7
4 is >= 3 && < 7
5 is >= 3 && < 7
6 is >= 3 && < 7
7 is >= 7
8 is >= 7
9 is >= 7
其实就是case语句需要一个布尔类型的结果,如果switch中有值,那么就和case的值进行等于比较,返回case一个布尔值;如果switch中没有比较值,那么就计算case语句中表达式的布尔值,返回给case
gopackage main
import (
"fmt"
//"strconv"
)
func main() {
counter := 0
target:
fmt.Println("Counter", counter)
counter++
if counter < 5 {
goto target
}
}
D:\goProject\golearn\lession2>go run .
Counter 0
Counter 1
Counter 2
Counter 3
Counter 4
数组存储固定长度的值,切片存储可变长度的值,maps存储key-value键值对
数组是一个固定长度并且包含单一类型元素的容器,可以通过下标访问元素。是值传递的。
创建:
shellmake([]string,6,10) 创建一个字符串切片,长度为6,容量为10 或者从现有的数组创建: var arr [3]string={"123","133","5234"} sli := arr[:]
切片底层是数组,且注意,切片的第一个元素并不一定是底层数组的第一个元素。
切片有三个元素:底层数组,长度,容量;长度是切片包含的元素个数,容量是切片第一个元素到底层数组最后一个元素的个数。
为什么有容量?比如两个切片底层是一个数组,当两个切片的长度都小于容量的时候,修改A切片的内容可能会影响到B切片的数据,当切片的长度大于容量的时候,会重新分配底层数组,这时两个切片就不相关了。
一般使用for...range..来遍历切片
使用sort包的sort函数可以为切片排序
切片只能和nil比较
copy函数保证每个切片有自己独立的数组。
copy(slice dest,slice src)
注意
不会自动扩容,如果在copy的过程中,目的切片不够了,那么就会中止copy,导致数据丢失!如果目的切片没有初始化,也没有赋予初始长度容量,那么当其作为copy的目的切片时,不会赋值元素进去,且不会报错。
如果源切片长于目的切片,那么目的切片的前部分会被源切片元素覆盖,后面的保持不变。
shellfunc main() { p1 := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} arrayPtr := (*[3]string)(p1) array := *arrayPtr fmt.Println(array) }
字符串也可以当作切片取值,但是注意,取单个元素是byte,多个元素是string
shellvar price string = "$48.95" var currency string = string(price[0]) var amountString string = price[1:] amount, parseErr := strconv.ParseFloat(amountString, 64) fmt.Println("Currency:", currency) if parseErr == nil { fmt.Println("Amount:", amount) } else { fmt.Println("Parse Error:", parseErr) }
但是注意,上面的例子中金钱符号占据一个byte,但是如果有个符号占据3个byte的话,整个代码就出错了,所以需要将string转为rune数组来操作就可以了:
shellvar price []rune = []rune("€48.95") var currency string = string(price[0]) var amountString string = string(price[1:]) amount, parseErr := strconv.ParseFloat(amountString, 64) fmt.Println("Length:", len(price)) fmt.Println("Currency:", currency) if parseErr == nil { fmt.Println("Amount:", amount) } else { fmt.Println("Parse Error:", parseErr) }
所以关注存储空间长度使用byte,关注内容使用rune
使用for循环string的时候,使用rune来做循环的。
map是键值对类型,每个元素都有其单独且唯一的key对应。
创建map:
shellmake(map[string]float64,10) 创建一个以字符串为key,float64为value的map,初始长度为10 map_test:=map[string]int64{ "1":1, "2":2, }
判断key是否在map中
当key没在map中,取这个key的value会返回零值,这容易与存值混淆,解决方法:
shell# 不要使用零值判断是否在map中
value,ok:=map_test["3"]
删除key:
shelldelete(map_test,"2")
遍历map:
shellfor key,value range map_test:
使用key排序,只有先取出所有key,然后对key排序,再使用key取值:
shellproducts := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, "Hat": 0, } keys := make([]string, 0, len(products)) for key, _ := range products { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { fmt.Println("Key:", key, "Value:", products[key]) }
可变长度参数:
shellfunc printSuppliers(product string, suppliers ...string) { for _, supplier := range suppliers { fmt.Println("Product:", product, "Supplier:", supplier) } }
可变长度参数必须是最后一个参数。
当可变参数没有值的时候是nil,所有有必要对其进行判断:
shellfunc printSuppliers(product string, suppliers ...string) { if len(suppliers) == 0 { fmt.Println("Product:", product, "Supplier: (none)") } else { for _, supplier := range suppliers { fmt.Println("Product:", product, "Supplier:", supplier) } } }
可以使用切片作为可变参数的值。
go读写参数:

golang对文件的操作总体来说是增删改
有两种方式:
go[root@localhost golearn]# cat main.go
package main
import "os"
func main(){
file,_ := os.OpenFile("file.txt",os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
file.Write([]byte("append data\n"))
file.Close()
}
goos.MkdirAll(dir,perm),创建一个目录,MkdirALL类似于mkdir -p os.Create(filepath),创建一个文件,但是其目录必须存在 os.Remove(filepath),删除一个文件 os.RemoveAll(dir),删除一个目录
shell使用os.Stat()来获取文件信息,此函数返回os.FileInfo结构,此结构包含文件必要信息
示例:
shellpackage main import ( "fmt" "os" ) func main() { file,_ := os.Stat("main.go") fp := fmt.Println fp("filename:",file.Name()) fp("size by bytes:",file.Size()) fp("last modified time:",file.ModTime()) fp("is a dir:",file.IsDir()) ff := fmt.Printf ff("Permission 9 bit:%s\n",file.Mode()) ff("Permission 3 bit:%o\n",file.Mode()) ff("Permission 4 bit:%04o\n",file.Mode()) } [root@localhost golearn]# go run main.go filename: main.go size by bytes: 385 last modified time: 2024-05-30 17:11:59.914381643 +0800 CST is a dir: false Permission 9 bit:-rw-r--r-- Permission 3 bit:644 Permission 4 bit:0644
shellpackage main
import (
"fmt"
"log"
"os"
)
func main() {
files, err := os.ReadDir("/tmp")
for _, file := range files {
fmt.Println("file name:", file.Name())
fmt.Println("is dir:", file.IsDir())
fmt.Println("################")
}
if err != nil {
log.Fatal("Encounter Error:", err)
}
}
[root@localhost golearn]# go run main.go
file name: .ICE-unix
is dir: true
################
file name: .Test-unix
is dir: true
################
file name: .X1024-lock
is dir: false
################
file name: .X11-unix
is dir: true
################
file name: .XIM-unix
is dir: true
################
....
当读写文件时,有时会使用缓冲buff来优化读写过程。
bufio的Scanner接口是
读取:
gopackage main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("./file.txt") # 前面说过噻,返回一个只读的文件对象
defer file.Close()
scanner := bufio.NewScanner(file) # 使用文件对象创建一个缓存读取器
scanner.Split(bufio.ScanLines) # Split使用指定的分割函数处理文本,默认就是使用bufio.ScanLines函数,用于按照行分割,就是说读取到函数返回true的时候就截断
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
for _, line := range lines {
fmt.Println(line)
}
}
写入:
gopackage main
import (
"bufio"
"os"
)
func main() {
lines := []string{"3333", "4444", "5555"}
file, _ := os.OpenFile("./file.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) # 创建一个可读写的文件对象
defer file.Close()
writer := bufio.NewWriter(file) # 使用bufio创建一个新的写入器
for _, line := range lines {
_, _ = writer.WriteString(line+"\n") # 写入器写入字符串
}
writer.Flush() # 将写入器缓存中未写完的数据写入
}
shellpackage main import ( "fmt" "io" "net/http" "time" ) func main() { req, _ := http.NewRequest("GET", "http://preaim.shoval.cn/", nil) # 创建一个http请求 req.Header.Set("Cache-Control", "no-cache") client := &http.Client{Timeout: time.Second * 10} # 创建一个http客户端 resp, _ := client.Do(req) # 使用客户端发送请求 body, _ := io.ReadAll(resp.Body) # 获取返回内容 defer resp.Body.Close() fmt.Printf("%s\n", body) # 打印内容 }