SherlockGy's Blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Git使用上手

发表于 2019-11-24 | 分类于 版本控制 > Git
本文字数: 7.1k | 阅读时长 ≈ 6 分钟

Git使用上手

一、起步

1.1 安装

Windows、Linux请自行前往官网,按照说明完成

这里说一下Mac版本,分两种情况:

  1. 使用Apple提供的git发行版:这种情况使用的是Xcode内的Command Line Tools
  2. 使用homebrew安装,强烈建议.因为Xcode内的git工具不太完整,缺少gitk、HTML格式的文档等

1.2 最小化配置

配置user.name和user.email

1
2
git config --global user.name 'your_name'
git config --global user.email 'your_email@domain.com'

1.2.1 config的三个作用域

1
2
3
git config --local  # 只针对某仓库
git config --global # 对当前用户的所有仓库有效
git config --system # 对该系统所有登陆的用户有效

显示config的配置,加--list

1
2
3
git config --list --local
git config --list --global
git config --list --system

1.3 使用本地HTML文档

1
2
# 例如查看git log命令的使用文档
git help log --web

二、建一个Git仓库

2.1 初始化

此时存在两种场景:

  1. 项目代码已经存在,要把项目加入Git版本控制
  2. 新建一个项目并使用Git来管理

针对第一种情况,我们使用如下命令:

1
2
cd 项目代码所在的文件夹
git init

针对第二种情况,则是使用如下命令:

1
2
3
cd 某个文件夹
git init your_project # git会在当前目录下创建和项目名称同名的文件夹
cd your_project
阅读全文 »

Go语言实用库02——文本编码

发表于 2019-10-21 | 更新于 2019-10-27 | 分类于 程序人生 > Golang
本文字数: 1.4k | 阅读时长 ≈ 1 分钟

编码转换

假设有一文本,编码为GBK,文本内容为:

1
2
你好,中国
我爱你,中国!

由于Go默认按照utf-8的码表去解析,所以会导致乱码

在Go官方提供的库里面,有一个库可以解决这个问题,但是不包含在标准库内,需要使用go get golang.org/x/text下载

提醒:下载之前请一定配置代理,可参考这篇文章

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"io/ioutil"
"os"
)

const path = "/Users/sherlockgy/Desktop/test.txt"

func main() {
file, e := os.Open(path)
if e != nil {
panic("打开文件错误!")
}

// 编码转换
newReader := transform.NewReader(file, simplifiedchinese.GBK.NewDecoder())

newBytes, e := ioutil.ReadAll(newReader)

if e != nil {
panic("读取内容错误!")
}

fmt.Printf("%s\n", newBytes)
}

编码猜测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const path = "/Users/sherlockgy/Desktop/test.txt"

func main() {
file, e := os.Open(path)
if e != nil {
panic("打开文件错误!")
}

// 编码猜测
encoding := determineEncoding(file)

// 编码转换
newReader := transform.NewReader(file, encoding.NewDecoder())

newBytes, e := ioutil.ReadAll(newReader)

if e != nil {
panic("读取内容错误!")
}

fmt.Printf("%s\n", newBytes)
}

// 猜测编码类型
func determineEncoding(r io.Reader) encoding.Encoding {
bytes, err := bufio.NewReader(r).Peek(1024)
if err != nil {
panic(err)
}
encoding, name, certain := charset.DetermineEncoding(bytes, "")
fmt.Printf("编码类型为:%s\n是否确定:%v\n", name, certain)
return encoding
}
阅读全文 »

Go语言实用库01——正则表达式

发表于 2019-10-21 | 更新于 2019-10-26 | 分类于 程序人生 > Golang
本文字数: 1.8k | 阅读时长 ≈ 2 分钟

Go语言中的正则表达式

一、正则表达式匹配

1.1 匹配单个子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const str = "my email is sherlockjgy@gmail.com"

func main() {
// 正则表达式规则声明,解析正则表达式
// 返回Regexp对象,用于后续操作
compile, err := regexp.Compile(`[A-Za-z0-9]+@[A-Za-z0-9]+\.[A-Za-z0-9]+`)
if err != nil {
panic("你的正则表达式存在语法错误!")
}
// 从目标字符串当中查找是否存在符合正则表达式规则的字符子串
// 返回true or false
isMatch := compile.MatchString(str)
fmt.Printf("目标字符串中是否存在符合规则的子串:%v\n", isMatch) // true

// 从目标字符串当中查找并返回符合正则表达式规则的字符子串
// 若不存在,则返回空串
matchString := compile.FindString(str)
fmt.Println(matchString) // sherlockjgy@gmail.com
}

提醒:建议将正则表达式规则书写在【`】引号(键盘1左边的按键)中,这样Go语言不会解析表达式中的转义字符,否则每个斜杠都需要双斜杠代替

1.2 匹配多个子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const str = `my email are sherlockjgy@gmail.com
and shelockgy@live.com`

func main() {
// 正则表达式规则声明,解析正则表达式
// 返回Regexp对象,用于后续操作
compile, err := regexp.Compile(`[A-Za-z0-9]+@[A-Za-z0-9]+\.[A-Za-z0-9]+`)
if err != nil {
panic("你的正则表达式存在语法错误!")
}

// 从目标字符串当中查找并返回【多个】符合正则表达式规则的字符子串
// 第二个参数代表需要查询多少个符合要求的子串,-1代表查询所有
matchString := compile.FindAllString(str, -1)
fmt.Println(matchString, len(matchString)) // [sherlockjgy@gmail.com shelockgy@live.com] 2
}

1.3 字符串匹配并分组

在正则表达式中运用小括号,可以对被匹配成功对子串进行分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const str = `my email are sherlockjgy@gmail.com
and shelockgy@live.com
and helloworld@test.com.cn`

func main() {
// 正则表达式规则声明,解析正则表达式
// 返回Regexp对象,用于后续操作
compile, err := regexp.Compile(`([A-Za-z0-9]+)@([A-Za-z0-9]+)([.A-Za-z0-9]+)`)
if err != nil {
panic("你的正则表达式存在语法错误!")
}

// 返回一个二维数组
matchString := compile.FindAllStringSubmatch(str, -1)
for _, value := range matchString {
fmt.Println(value)
}
}

输出:

1
2
3
[sherlockjgy@gmail.com sherlockjgy gmail .com]
[shelockgy@live.com shelockgy live .com]
[helloworld@test.com.cn helloworld test .com.cn]
阅读全文 »

Go语言从入门到实战(二)

发表于 2019-09-28 | 分类于 程序人生 > Golang
本文字数: 7.2k | 阅读时长 ≈ 7 分钟

Go语言从入门到实战(二)

一、变量与常量

1.1 变量

我们先来看一下Go语言当中的标准变量声明方法:

1
var name string = "tom"

其中,var是声明变量的关键字,而name就是我们的变量名。需要特别注意的是,作为类型的string被放在了变量名的后面。这样设计主要是为了引导程序员使用类型自动推导,从而省略变量类型:

1
2
// 使用类型推导
var name = "tom"

拓展:这种类型后置的设计在现代编程语言当中已经非常常见了,如Scala、Kotlin、Swift、Rust等

而当我们需要一次性声明多个变量的时候,则可以使用代码块来做:

1
2
3
4
5
var (
name string
age int
address string
)

当我们的变量是局部变量的时候,我们还可以使用简短声明的方法来声明变量:

1
2
3
func main() {
name := "tom"
}

在这里,我们有几个注意点需要强调:

  1. 简短声明仅可使用于局部,全局变量必须使用var关键字声明
  2. 局部变量一经声明必须被使用,否则将会编译错误;如果需要丢弃该变量,则使用下划线_命名变量
  3. 全局变量是包级别的变量,而不是以单个文件作为作用域;全局变量可以声明但不使用
  4. 局部变量如果是不同类型(包括int32与int64也不是相同类型),无论如何都不能直接运算,而需要显式类型转换

1.2 常量

Go语言的常量使用const关键字进行声明:

1
const name string = "tom"

同样,变量类型是可以省略的:

1
const name = "tom"

当我们需要一次性声明多个常量的时候,同样可以使用代码块来做:

1
2
3
4
const (
name = "tom"
age = 11
)

关于常量,我们也有几点需要强调的:

  1. 常量声明必须使用关键字的方式,不能使用简短声明
  2. 常量可以声明后不使用
  3. 常量如果是以省略变量类型的方式声明的,整型和浮点型变量之间可以直接运算

1.3 基于常量的枚举类型

Go并不提供专门的枚举类型关键字,如其他语言当中常见的enum。而是通过常量的关键字const + iota的方式,实现枚举

我们先来看一段示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
const (
a = 9 // iota值为0,虽然没有被使用,iota依然存在
b = iota // iota值为1,所以b为1
c // iota值为2,c继续使用b的表达式,所以c为2
d = 10 // iota值为3,虽然没有被使用,iota依然存在
e = iota // iota值为4,所以e为4
f = "abc" // iota值为5,f为"abc"
g // iota值为6,g继续使用f的表达式,所以g为"abc"
h = iota // iota值为7,所以h为7
)
const i = iota // 新的const,iota重制为0,所以i为0

fmt.Printf("a = %v, b = %v, c = %v, ", a, b, c)
fmt.Printf("d = %v, e = %v, f = %v, ", d, e, f)
fmt.Printf("g = %v, h = %v, i = %v", g, h, i)
}

输出

1
a = 9, b = 1, c = 2, d = 10, e = 4, f = abc, g = abc, h = 7, i = 0

通过上述例子,可以总结为:

  1. iota不管是否被使用,在同一个const代码块内,都会从0开始不断自增;如果遇到新的const,则重制为0
  2. 如果枚举项没有声明表达式,则继承上一个枚举项的表达式

二、Go语言的数据类型

2.1 布尔与字符串类型

类型 描述
bool 布尔类型(true、flase)
string 字符串

2.2 无符号整数类型

类型 描述
uint 长度由系统架构类型决定
uint8 长度为8位,即1字节(0~255)
uint16 长度为16位,即2字节(0 到 65535)
uint32 长度为32位,即4字节(0 到 4294967295)
uint64 长度为64位,即8字节(0 到 18446744073709551615)
uintptr 长度为8位,即1字节(0~255)

2.3 有符号整数类型

类型 描述
int 长度由系统架构类型决定
int8 长度为8位,即1字节(-128 到 127)
int16 长度为16位,即2字节(-32768 到 32767)
int32 长度为32位,即4字节(-2147483648 到 2147483647)
int64 长度为64位,即8字节(-9223372036854775808 到 9223372036854775807)

注意:虽然int类型和uint类型长度没有指定,但是不代表它们可以像Python这种语言一样不限制int的大小。例如在64位操作系统上,int和uint的最大值就是int64和uint64的最大值

我们也可以在代码中轻松获得每个数值类型的最大最小值,例如:

1
2
3
4
func main() {
fmt.Println(math.MaxInt8)
fmt.Println(math.MinInt8)
}

那如果我们需要操作的数字的大小,int64已经无法满足了,要怎么办呢?

我们可以使用math/big包下的函数进行处理:

1
2
3
4
func main() {
a, _ := new(big.Int).SetString("999999999999999999999999999999999999", 10)
fmt.Println(a)
}

关于这一点,此处就不再多说了,毕竟这不是目前的重点。如果需要更多内容,可以直接查找相关API文档

2.4 浮点数类型

类型 描述
float32 长度为4个字节,IEEE-754 32位浮点型数
float64 长度为8个字节,IEEE-754 64位浮点型数

不同于整型当中的int类型,浮点数类型要么是float32要么就是float64,不存在float这个类型

在使用简短声明的时候,编译器会根据当前计算机的架构自动进行推断,从而决定使用float32还是float64

2.4.1 浮点数与字符串类型的转换

浮点数转换为字符串:

1
2
3
4
5
6
7
func main() {
var f64 float64 = 1.12
str := strconv.FormatFloat(f64, 'E', -1, 64)
str2 := fmt.Sprintf("%v", f64)
fmt.Println(str) // 1.12E+00
fmt.Println(str2) // 1.12
}

字符串转换为浮点数:

1
2
3
4
5
func main() {
s := "3.1415926"
float, _ := strconv.ParseFloat(s, 64)
fmt.Println(float)
}

2.5 复数类型

类型 描述
complex64 32 位复数(实部与虚部分别32位)
complex128 64 位实数和虚数(实部与虚部分别64位)

2.6 别名类型

2.6.1 byte和rune

类型 描述
byte 字节类型,uint8类型的别名
rune 字符类型,int32类型的别名

因为Go语言的rune和其他语言的char还是有较大差异,所以我们在这里针对rune类型,多做一些介绍

我们先看一下Go语言源码中,对于rune类型的注释:

1
2
3
4
// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
// 符文是int32的别名,在所有方面都等同于int32。按照惯例,用于区分字符值和整数值。
type rune = int32

因此,rune其实并不是一个独立的类型,它只是int32类型的另一个称呼罢了。所以,rune类型可以和int32类型直接做计算,而不需要任何转换。

如果我们直接将rune类型的值打印出来,那么它就是一个数字:

1
2
3
4
5
func main() {
c := 'a'
fmt.Println(c) // 97
fmt.Printf("%c\n", c) // a
}

对于rune类型的类型转换,下面给出两个示例:

例1:

将rune类型转换为字符串:

1
2
3
4
5
6
7
8
9
func main() {
c := 'a'
// 方法1
fmt.Println(string(c))

// 方法2
str := fmt.Sprintf("%c", c)
fmt.Println(str)
}

例2:

将rune类型所代表的数字,转换为字符串:

这个问题其实可以等价为,将整型转换为字符串。在其他语言当中,如Python,我们是直接使用str()函数进行转换的,但是因为Go语言的rune类型的存在,如果直接使用string()函数,就会得到该数字对应的字符,而不是数字本身的字符串格式。这显然不是我们想要的。

正确的做法应为:

1
2
3
4
5
6
7
8
func main() {
i := 97
// 方法1
fmt.Println(strconv.Itoa(i)) // 97

// 方法2 可以同时进行进制转换
fmt.Println(strconv.FormatInt(int64(i), 10)) // 97
}

2.6.2 类型定义与类型别名

这两个概念我们需要重点区分:类型定义和类型别名

什么是类型定义?举个例子:

1
2
3
4
5
6
type myInt int

func main() {
var i myInt = 100
fmt.Println(i)
}

在上面的例子当中,myInt这个类型就是我们新定义的。那为什么说是新定义的呢?因为,它和int类型其实并不是一个类型。他只是与int类型有共同的实现、共同的能力罢了。

我们知道Go是一个强类型语言,且不存在类型隐式转换。只要不是同一个类型,就不能直接运算,如下面的代码是不能编译成功的:

1
2
3
4
5
6
7
type myInt int

func main() {
var i myInt = 100
var j = 100 // 默认为int类型
fmt.Println(i + j) // invalid operation: i + j (mismatched types myInt and int)
}

但是如果我们直接与字面量运算,还是没有问题的:

1
2
3
4
5
6
7
type myInt int

func main() {
var i myInt = 100
var j = i + 100
fmt.Println(j) // 200 并且此时的j也是myInt类型
}

我们可以通过使用类型定义这个特性来拓展原有类型的能力:

1
2
3
4
5
6
7
8
9
10
11
12
type myInt int

// 我们给新的整数类型myInt赋予了自动翻倍的能力
func (t myInt) double() int {
return int(t * 2)
}

func main() {
i := 100
newNum := myInt(i).double()
fmt.Println(newNum) // 200
}

而类型别名像我们上述提到type和rune时介绍的一样,并不是一个新的类型,只是叫法不同。这个特性是在Go1.9版本时引入的。

不同于类型定义,我们无法对别名类型的能力进行拓展,因为本来就是同一种类型

三、条件与循环语句

3.1 if条件语句

示例:

1
2
3
4
5
6
7
8
9
10
11
func main() {
var a = 8

if a > 10 {
fmt.Println("大于10")
} else if a > 5 {
fmt.Println("a大于5")
} else {
fmt.Println("a小于5")
}
}

注意:Go语言的if语句,其条件不需要放在括号中

3.2 switch语句

3.2.1 基础switch

示例:

1
2
3
4
5
6
7
8
9
10
11
func main() {
var a = 5
switch a {
case 0, 2, 4, 6, 8:
fmt.Println("10以内的偶数")
case 1, 3, 5, 7, 9:
fmt.Println("10以内的奇数")
default:
fmt.Println("不是10以内的自然数")
}
}

输出:

1
10以内的奇数

这里应该能看出Go和其他主流编程语言的诸多不同,我们这里一一列举:

  1. Go语言的switch语句可以用来匹配任何类型,包括结构体
  2. 每个case都可以匹配多个值
  3. 每个case都默认自带break,如果需要实现switch穿透,则在需要穿透的case结尾加上fallthrough

除此之外,switch还可以作为if-else if结构的简写版本:

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
var a = 8

switch {
case a > 10:
fmt.Println("大于10")
case a > 5:
fmt.Println("a大于5")
default:
fmt.Println("a小于5")
}
}

3.2.2 type switch

type switch主要用于对变量类型进行判断的场景,使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
var a interface{} = "123"

switch a.(type) {
case int:
fmt.Println("int")
case string:
fmt.Println("string")
default:
fmt.Println("other")
}
}

这里需要说明的是,type switch只能对interface{}类型使用,interface{}类型是一个接口,Go当中所有类型都实现了该接口,所以type switch其实就是判断一个interface{}的实现到底是什么类型

提到了type switch,就不能不提type assertion。type assertion是一个类型断言,他能帮助我们先校验类型,后执行操作。

如下面这个s就可以顺利作为参数传递给println()

1
2
3
4
func main() {
var s interface{} = "123"
fmt.Println(s.(string))
}

3.3 循环语句

Go只有for循,但是存在多种不同的使用方式。比如其他语言当中的while循环,在Go语言中也是通过for来实现

3.3.1 经典for循环

示例:

1
2
3
4
5
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}

其中注意点和if是一样的,即不需要使用括号来包围条件

3.3.2 for循环变体

Go中既然没有while循环,那么怎么实现while循环的效果呢?其实只要把for当作while就可以了:

1
2
3
4
5
6
7
func main() {
i := 0
for i < 10 {
fmt.Println(i)
i++
}
}

for还可以轻松实现无限循环,相比其他语言的while(true)更加简洁:

1
2
3
4
5
func main() {
for {
fmt.Println("forever")
}
}

3.3.3 for…range

当我们需要遍历一个集合或者字符串的时候,使用经典for循环显然是太啰嗦了,我们可以使用for...range语句来简化语法,但是要注意,在某些场景下,for...range不仅仅是帮助我们简化了语法,还多做了一些人性化的事情,后面会详细讲到的。

经典for循环遍历数组:

1
2


Go语言从入门到实战(一)

发表于 2019-09-24 | 更新于 2019-09-25 | 分类于 程序人生 > Golang
本文字数: 5.4k | 阅读时长 ≈ 5 分钟

Go语言从入门到实战(一)

一、为什么要学习Go语言

在开始我们的学习旅程之前,我们先来了解一下Go语言,通过建立一个宏观的印象,来熟悉一下Go这门语言,消除陌生的感觉。

首先要强调的是,Go是一门通用语言,这也就意味着,不管是Java、C#、Python还是PHP,这些语言可以做到的事情,理论上Go都可以做;Go可以做到的事情,理论上这些语言也都可以做到。

因此,我们在选择一门技术的时候,都是选择去看他最擅长的方面。

但是先别急,我们不妨先去探究一下,Go为什么会被发明?或者说,Go是为了解决什么样的问题而诞生的呢?当我们搞清楚这个问题,Go语言擅长做什么这个问题,也就不言自明了。

2007年前后,软件开发迎来了新挑战,主要包括:

  • 摩尔定律逐渐失效,多核硬件架构的广泛应用
  • 超大规模分布式计算集群
  • Web模式导致的前所未有的开发规模和更新速度

而为了解决这些问题,Google的三位大牛合作创造了Go语言:Rob Pike、Ken Thompson、Robert Griesemer。

  • Rob Pike:Unix早期开发者、UTF-8创始人、Plan9操作系统创始人

  • Ken Thompson:Unix创始人、B语言创始人、UTF-8创始人、参与设计正则表达式、1983年图灵奖获得者

  • Robert Griesemer:Google V8 JS Engine开发者、Java Hot Spot开发者

所以我们学习Go语言,其最主要的应用场景就是上面所提到的:多核架构下,超大规模集群的项目的开发。

而天生目标明确的Go语言,毫无疑问的具备鲜明的特点,让我们来一一了解。

二、Go语言的特点

2.1 简约

Go语言所倡导的思想是“Less is more”,他的创造者们希望能用尽量简单的方法,去完成我们的工作,但是Go语言的简约不等于其简单,他的威力丝毫没有因为其简约的设计而弱于其他的编程语言。

Go语言设计的简约性可以体现在以下几个方面:

首先,就是关键字的数量:

C Go C++ 11
37 25 84

我们都知道C是一门简单而强大的系统级语言,而Go甚至做到了关键字的数量比他还要少;而C++是与Go语言的理念截然相反的,C++几乎吸收了所有他能吸收的特性,导致了他虽然功能强大,但是过于复杂。

2.2 性能

Go语言的特点不仅仅在于简约的设计,更少不了的是他强大的执行速度。

大家都知道Python也是一门上手迅速的编程语言,深受很多程序员的喜爱,然而他的问题就在于作为一门解释性语言性能较弱,因此,Python常用于一些对性能要求不高的地方,如充当“胶水语言”。

而Go作为一门编译型语言,其拥有超越Java的执行速度;又因为他的简约的设计,他还拥有接近Python的开发速度。同时具备这两个特性,使得Go语言充满了吸引力。

2.3 组合的编程思想

组合是一种比起传统面向对象编程语言当中的继承更加灵活的编程思想。

组合是has-a的关系,而继承是is-a的关系。这种设计的优越性在于可以进一步的降低耦合,实现更小的封装从而保证功能的专注和单一。我们会在后面的学习中更加深入地感受到这种设计的优越性。

当然,没有最好的设计,只有更适合的设计。组合并不能全盘否定继承的价值。不过对于Go语言来说,组合更加接近其设计思想。

因此,我们在学习和使用Go的过程中,需要训练自己使用组合的方式来思考问题,而不是把Go的代码,写成了Go语法版本的Java

三、Go语言的Killer Application

对于一门技术来说,最为重要的除了其即将能创造的价值以外,还有就是已经创造的价值。对一门编程语言来说,如果它曾经实现过杀手级应用,那么毫无疑问,对于该语言的推动与发展都是大有裨益的。

因为,这一方面杀手级应用证明了该语言开发复杂系统的能力,而不仅仅是一个大牛开发的玩具;另一方面,杀手级应用可以将部分应用自身的用户转化为开发该应用的技术的使用者。

杀手级应用(Killer application)是指一个极具价值的计算机程序或服务,消费者愿意为这个程序或服务购买特定硬件、软件产品或服务。——中文维基

Go语言就拥有数个杀手级应用:

用于云计算的两个关键应用是Go语言开发:

  • Docker
  • Kubernetes

用于区块链的杀手级应用和Go兼容最好:

  • Ethereum
  • Hyperledger

以及不得不提的:Go语言自身的编译器也是使用Go语言实现。这样的方式被称为:自举

四、Go语言的安装和准备工作

4.1 下载Go安装程序

因为Google退出中国大陆的原因,我们正常无法直接从官方网站下载Go安装程序,推荐前往地址:Go语言中文网下载。

如果是Mac OS的用户,可以使用home brew安装。

4.2 官网与文档

Golang的官网是https://golang.org,不过因为上面提到的原因,国内不能正常访问,那么替代方案有这么几种:

  • 方案一:Google CN提供的镜像网站:该网站最大的好处就是原版英文,可以获得等同于原官网的体验

  • 方案二:中文版官网:该网站已经部分汉化了,可以帮助阅读英文有困难的同学们

  • 方案三:使用Golang起一个本地服务:安装Golang后,在命令行里输入godoc -http=:8080,并在浏览器访问http://localhost:8080/即可访问本地官网(英文),8080是端口号,可以更改,这种方式优点在于不依赖网络

  • 方案四:https://tip.golang.org/是golang.org的完全限定域名,由Google提供,目前中国大陆可以正常访问

提醒:针对上面的【方案三】,需要补充说明一点:新版的Go安装程序已经不在包含godoc,必须自行安装godoc(中国大陆需要代理)。

4.3 Go环境配置

在此之前,我们先学习两个命令:

  1. 查看当前Go语言版本:go version
  2. 查看当前Go语言环境设置:go env

好,那我们开始吧:

配置环境变量GOPATH是至关重要的一个环节。所谓GOPATH,就是我们存放代码、编译程序的工作目录。

可以自己随意选择一个目录作为“GoPath”,然后将其配置到环境变量当中(具体配置方法可以参考【4.4设置代理】)

GoPath路径下的目录结构应该是这样的:

1
2
3
4
GOPATH
|-src(必要,请自行创建)
|-pkg
|-bin

其中,我们的代码就存放在src目录下,包括我们从网络上拉取的第三方包,也会存放在这里。

建议:将你的GoPath路径/bin【追加】到环境变量PATH里(请自行解决),以确保可以调用Go语言开发的工具

提示:从版本1.11开始,我们并不一定要将自己的代码写在GoPath目录下,具体请参见后面介绍“Go Mod”的文章

4.4 设置代理

Go为我们提供了非常好用的工具go get,可以直接从网络拉取依赖包(例如:github),但是由于下载速度并不理想,且部分存放在Google服务器上的包文件无法正常获取(中国大陆),我们还是通过配置代理来解决吧。

推荐方法:

如果你的Go版本大于等于1.13,可以直接使用以下命令配置代理:

1
$ go env -w GOPROXY=https://goproxy.cn,direct

通用方法:

如果上述推荐方法不适用,或者没有效果,那么我们可以手工配置:

  • Windows:添加系统环境变量,GOPROXY,值为https://goproxy.cn;如果你的版本大于等于1.13,值则为https://goproxy.cn,direct
  • Mac:在目录~/.bash_profile文件下添加:export GOPROXY=https://goproxy.cn,保存并退出;如果你的版本大于等于1.13,则为export GOPROXY=https://goproxy.cn,direct
  • Mac(补充):如果你的shell不是bash而是zsh,那么则是在该文件下操作:~/.zshrc。或者,依然在~/.bash_profile文件下操作,但是必须将source ~/.bash_profile配置添加入~/.zshrc
  • Mac(提醒):你可以执行source ~/.bash_profile或~/.zshrc使环境变量配置立刻生效

至此,你的go get命令可以正常工作了。

4.5 开发工具

Go语言可以使用的开发工具还是比较丰富的,这里稍微列举几种:

  1. GoLand:Jetbrains家的专业IDE,和大名鼎鼎的Java IDE工具“intellij idea”同门。缺点:收费
  2. liteide:由中国人自己开发的Go语言IDE工具,免费。下载说明在此,建议下载二进制文件,而不是手动编译
  3. Eclipse + Goclipse:经典IDE Eclipse通过安装插件支持Go语言开发,免费
  4. VsCode、Atom、Sublime等 + 插件:代码编辑器(Editor)相比IDE更加轻量,同样免费
  5. Vim、Emacs + 插件

五、Go快速上手

在第一篇文章的最后,我们一起来快速上手一下Go代码的编写吧。

5.1 Hello World

  1. 在你的GoPath目录下的src目录内,创建项目目录(名称自取)。

  2. 在项目目录下,创建文件hello.go(名称自取)

1
2
3
4
5
6
7
8
9
10
11
12
13
// 声明包
package main

// 导入包
import (
"fmt"
)

// 主函数
func main() {
// 向控制台输出文本内容
fmt.Println("Hello World")
}

运行代码:

在.go文件所在目录下,执行命令:go run hello.go

控制台输出:

1
Hello World

5.2 解释说明

第1行:

Go语言的单行注释,不参与程序执行

多行注释则为:

1
2
3
/*
fmt.Println("Hello World")
*/

第2行:

Go和其他很多语言一样,使用包来管理和组织代码,其中规则如下:

  1. 同一个目录下,所有.go文件的包名必须一致
  2. main包下,必须有且只能有一个main函数
  3. 目录的名称不一定非要和包名一致
  4. main方法所在的文件的文件名不要求是main.go

第5、6、7行:

导入依赖包,其中规则如下:

  1. 当需要导入的包仅有一个时,可以简写成一行:import "fmt"
  2. 包可以设置别名,如import f "fmt"
  3. 导入的包,一般必须使用,否则编译不通过
  4. 如果不使用导入的包,且想要通过编译,需要将下划线_作为包别名:import _ "fmt"

第10行:

main函数是整个程序的入口,具体规则如下:

  1. 该示例中,main函数声明为唯一写法。即:没有参数列表、没有返回值
  2. main包下必须有一个main函数,并且只能有一个main函数
  3. Go语言中,所有函数声明均是使用func关键字
  4. Go语言中,函数体使用花括号包围,且左花括号不能另起一行

第12行:

函数的调用,具体规则如下:

  1. 函数&表达式结尾均不需要添加分号
  2. 一般情况下,调用包函数必须写上包名.。如果需要省略这个部分,可以使用点.作为包别名:import . "fmt"
  3. Go语言中,函数名&变量名的首字母大小写均代表包外可见性。大写代表可见,反之不可见

5.3 程序退出码

我们可以用程序退出码来表示程序的执行结果,如:返回0代表执行成功,其他则代表出现异常

由于Go语言main函数不支持返回值,所以我们需要给os包下的Exit()函数传值实现

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"os"
)

func main() {
fmt.Println("Hello World")
// 设置程序退出码
os.Exit(0)
}

注意:这里的退出码范围为0~255,如果传递-1,则会当作255

5.4 传递命令行参数

实用的命令行工具都需要传递参数给我们的程序,由于Go的main函数没有参数列表,所以需要使用os包下的Args()函数去接收命令行参数

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"
"os"
)

func main() {
// 获取并打印命令行参数
fmt.Println(os.Args[1])
}

此处Args()函数返回的是一个字符串切片,关于字符串切片我们后面会学习到。这里我们只需要明白,[0]代表取出列表中的第一个元素,而[1]就是取出第二个元素

Args()函数返回的第一个元素(即下标0)永远是程序本身,而第二个元素(下标1)就是我们传入的第一个参数,以此类推…

执行上面的代码:

1
$ go run Hello.go haha

控制台输出:

1
haha
1…345…23
SherlockGy

SherlockGy

Keep thinking different...

111 日志
38 分类
62 标签
RSS
GitHub E-Mail
友情链接
  • GoLang-飞雪无情的博客
  • Java-酷壳
  • Python-静觅丨崔庆才的个人博客
  • 掘金
  • GoLang中国
  • GoLang中文社区
  • V2EX
  • Microsoft Docs
  • Go语言圣经
© 2020 SherlockGy | 191k | 2:false
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Pisces v6.3.0