golang 简介
golang 简介概述开发环境搭建安装 go 编译器设置环境变量go 语言编辑器第三方库的安装和 GOPATH 的目录结构离线安装一些无法访问的库go 文档语法简介变量申明遍历数组,mapgo 的结构体go 的 interface 类型package 和 init 函数启动一个 go routine并发时的通讯 go channeldefer关键字内存管理一些注意的点代码实例xml 文件的解析和生成json 文件的解析和生成结构体上绑定方法正则表达式的匹配一行代码实现 http 文件服务器
概述
golang 是谷歌公司推出的一门编程语言,和 C 语言更接近些,它是编译型语言,同时有结合了解释型语言的优点,对于网络,并发等支持的很好。官网:https://golang.google.cn/golang 的优点:
很方便的处理各种格式化文件,xml,json 等
很方便的处理各种网络操作,例如 TCP 连接,UDP 连接,监听端口
很方便的处理 http 相关的请求,http 服务端
模板语言功能齐全,包括 http 页面和文本的模板,也就是我司的动态替换
很方便的进行并发,go {}
简化版的面对对象,通过定义一个结构体,然后在结构体上绑定方法来实现
常见的数据结构,链表,map,hash 等都有内置实现
常见的加密算法,例如 MD5,AES,RC4 等也都有内置实现
正则表达式,字符串操作,等都有内置的实现
开发环境搭建
安装 go 编译器下载地址:https://golang.google.cn/dl/选择对应的操作系统下载也可以通过 yum,apt-get 等工具安装,只是不确定是否安装的是最新版本以 linux 为例,下载完成后,将压缩包解压,将 go 文件夹放到一个位置,例如 /root/go设置环境变量go 有比较重要的两个环境变量GOROOT:go 安装的目录,例如 /root/goGOPATH: go 的项目目录,go 编译的时候会在这些路径里面查找 package上面两个环境变量设置之后,再将和添加到 PATH 中开启一个 shell,执行 go env,如果输出一个环境变量列表则表明安装成功了,另外用 go version,可以查看go的版本
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/test/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/test/go"
GORACE=""
GOROOT="/usr/lib/go-1.10.3"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.10.3/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build812137494=/tmp/go-build -gno-record-gcc-switches"go 语言编辑器目前用过的 go 语言编辑器,最好用的是 liteide ,其他的也有支持 golang 的编辑器,例如 vscode 等liteide 支持自动提示,自动格式化代码等,推荐使用 liteide下载地址:http://liteide.org/cn/https://github.com/visualfc/liteide/releases/下载之后,同样是解压到一个目录,然后运行该目录里面的 liteide 即可,为了方便可以将其添加到任务栏。打开 liteide 之后,编辑一个 hello.go ,使用快捷键即可执行编译,使用快捷键即可编译并运行,如果成功打印出了,则说明安装成功了。
packagemain
import(
"log"
)
funcmain() {
log.Printf("hello world!")
return
}第三方库的安装和 GOPATH 的目录结构目前 go 还没有库的管理,安装第三方库都是直接在网上下载的,比较常用的都是在上下载,安装库的命令是,例如当需要更新一个库时go get 命令会自动从https://github.com/astaxie/beego的地址去下载源码并安装如果没有网络怎么办呢?下面介绍 GOPATH 目录结构,之后就知道如何离线安装第三方库了GOPATH 是 go 的库目录,类似于 C 语言的 lib 目录,下面是一个 GOPATH 目录的例子
gopath
src
github.com
astaxie
beego
bin
pkg其中 bin 目录是存放一些可执行文件的,当在一个项目中执行,则会将编译好的可执行文件放到目录下;pkg 是一个编译时的中间目录,该目录下会有不同操作系统不同架构的文件夹,平时也用不到,不用去管;src 目录就是源码目录,所有的 go package 都需要有源码,而 go get 命令下载源码也是放到这个路径的。源码路径是和 url 的目录结构相对应的,例如一个第三方库,其目录结构也有三级,,所以离线安装的话,就可以直接访问https://github.com/astaxie/beego,将源码包下载下来,再将其按照对应的目录放置即可。离线安装一些无法访问的库当有些第三方库地址无法访问(有些是墙外才能访问),这时候有一个方法https://golangtc.com/download/package这个网站专门做了一个下载的页面,用来应对这种情况,详细地可以参看这个网址的页面下载下来之后按照上面的步骤离线安装即可go 文档官网有一个 go 标准库的文档,https://golang.google.cn/pkg/安装了 go 之后,本地也可以启动一个 http 服务,来查看 go 文档,和官网的是一致的,区别在于,本地安装了第三方库,也会在页面中显示出来。启动方法
godoc-http"127.0.0.1:6666"
语法简介
变量申明
variint=
i:=
var(
j:=
k:=1
)
vardataMyData
data:=&MyData{}
vardbmap[string]string
varstrs[]string遍历数组,map
varstrs[]string
forindex,val:=rangestrs{
log.Printf("index:%s, val:%s",index,val)
}
vardbmap[string]string
forkey,val:=rangedb{
log.Printf("key:%s,val:%s",k,v)
}这里需要注意的是,遍历的时候,range 关键字是会新分配内存的,例如上面的代码中 val 的值是新分配的,二不是数组或者 map 里面的内容,所以通过 val 去修改是不会生效的,想要修改必须用下标去引用。go 的结构体go 的结构体有点像是简化版的 class
typePersonstruct{
Namestring// public
Sexstring
ageint
secrectstring// private
}
func(this*Person)Eat()error{
returnnil
}
func(this*Person)Sleep() (string,error) {
return"private",nil
}
func(this*Person)Say(msgstring)error{
fmt.Sprintf(msg)
returnnil
}
typeCoderstruct{
Person
Langstring// program language
}
func(this*Coder)WriteCode()error{
returnnil
}
func(this*Coder)FixBug()error{
returnnil
}
func(this*Coder)Eat()error{
this.Person.Eat()
returnnil
}
typeTesterstruct{
Person
Toolsstring// test tools
}
func(this*Tester)Test()error{
returnnil
}go 的 interface 类型golang 官网上对于 interface 的介绍:https://golang.google.cn/ref/spec#Interface_types
An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.可以看到 interface 是一个函数集合类型,可以保存任何拥有这些函数集合的超集的类型,未赋值的 interface 为nil。
typeHumaninterface{
Eat()error
Sleep()error
Say(msgstring)error
}
typeManstruct{
Person
Moneyint
}
func(this*Man)Pay(costint)error{
this.Money-=cost
ifthis.Money
returnerrors.New("no money")
}
returnnil
}
typeWomanstruct{
Person
Bagstring
}
func(this*Woman)Shopping() (int,error) {
cost:=100
returncost,nil
}
varjackMan
varroseWoman
funcMeet(a,bHuman)error{
varerrerror
varcostint
a.Say("hello!")
b.Say("Nice to meet you too!")
a.Eat()
b.Eat()
ifv,ok:=b.(Woman);ok{
cost,err=v.Shopping()
}
ifv,ok:=a.(Man);ok{
err=v.Pay(cost)
}
a.Say("Goodbye!")
b.Say("See you!")
returnnil
}
funcmain() {
Meet(jack,rose)
}package 和 init 函数go 中所有的代码都是以 package 的形式存在,同一个程序中 main package 只有一个,而其他 package 可以有很多个,通过 import 来引用其他 package,引用方法是通过路径,而路径会在 GOPATH 里面寻找对应的代码。
packagemain
import(
"log"
"hello/hello"
"github.com/astaxie/beego"
_"github.com/go-sql-driver/mysql"
)
funcinit() {
// do something
}
funcmain() {
log.Printf("started")
hello.Print("hello")
beego.Run()
return
}所有的 package 有一个 init 函数会默认调用,而且是在 import 阶段就会调用,例如上面的代码中,在 main 函数进入之前,就会执行 init 函数,以及引用的三个库的 init 函数,最终才进入 main 函数的流程。上述代码中 mysql 的 package 前面有一个下划线,表示仅仅执行该 package 中的 init 函数,而不引用其中的方法或者类型。在目录下有一个 hello 的 package
packagehello
import(
"fmt"
)
var(
prefixstring="say:"
)
funcinit() {
Print("here is init for hello")
}
funcPrint(msgstring)error{
fmt.Sprintf(prefix+"msg")
returnnil
}这样可以对代码进行功能拆分,变成一个一个的 package ,从而实现模块化。启动一个 go routinego 的线程叫协程
gofunc() {
log.Printf("begin")
// sleep
log.Printf("end")
}()go 关键字后面跟上函数,就会启动一个 go routine 去执行。并发时的通讯 go channel在并发时,需要进行通讯,可以使用 channel 类型,中文叫信道。
packagemain
import(
"fmt"
)
funcmain() {
varfinishchanint=make(chanint)
gofunc() {
fori:=;i
fmt.Printf("%d ",i)
}
finish
}()
}这里 chan 就是一个 channel 类型,其中信道的操作是一个比较难理解的点,这里有一个关键字,,当 chan 在左边时,右边就是要往信道发送的内容。当 chan 在右边时,表示监听信道,直到有内容过来,此时左边是没内容的信道 chan 还分为无缓冲的信道,带缓冲的信道,例如上面的代码中
varfinishchanint=make(chanint)这就是一个无缓冲的信道,当发送给信道的内容没有被接受时,发送端是会阻塞的,直到内容被信道接收,例如上面的例子,如果主线程一直不处理子线程发送的 0 ,则子线程也会阻塞。当我们如下定义时
varfinishchanint=make(chanint,100)这就是一个有100个缓冲区的信道,此时往信道里面发消息的时候不会阻塞,除非信道缓冲区满了。需要注意的是,chan 也是有死锁的情况的,当所有的 runtine 都进入等待状态,则会死锁,所以写代码的时候要注意。其他 chan 相关的知识,可以自行查找资料了解更多。defer关键字defer 后面跟一个函数,通常是在函数返回之前执行,例如
funcmain() {
fd,err:=os.Open("1.txt")
iferr!=nil{
returnerr
}
deferfd.Close()
// xxxx
return
}上述代码打开了一个文件,以前 C 语言的习惯是,在文件使用完之后执行 Close,在 go 里面也可以继续这样做,但是使用了 defer 关键字之后,就可以保证资源的打开和关闭在同一个地方,提高了代码的可读性,也避免了打开了忘记关闭资源的情况。上述的关闭文件句柄函数会在执行 return 时隐式的调用。defer 也可以像 go 关键字一样写
deferfunc() {
// do something
}()内存管理go 语言和 java 一样是有垃圾回收的,通常内存分配之后不需要执行 free / delete 来手动释放,分配内存有两个关键字 new / make。new 关键字和我们常理解的是一致的,它接受一个类型参数,分配内存,然后返回该类型的指针。
vari*string
i=new(string)
*i="hello"new 出来的类型会被初始化为初始值,int 为 0,string 为 "",其他引用类型为 nilmake 关键字专门用来分配数组(也叫切片)、map、chan 这几种引用类型,返回的是这几种类型本身,不是指针。
varfinishchanint=make(chanint,100)
varstrs[]string=make([]string,)
maps:=make(map[string]string,)一些注意的点
go 语言的大括号不允许换行,必须在上一行代码的末尾,否则会报错
行尾不能有分号
import 了一个库却不使用,会报错
代码实例
xml 文件的解析和生成
packagemain
import(
"encoding/xml"
"log"
)
typeMyDatastruct{
XMLNamexml.Name`xml:"root"`
Namestring`xml:"name"`
Statusstring`xml:"status"`
}
funcEncodeXml() {
vardataMyData
data.Name="test1"
data.Status="stopped"
out,err:=xml.Marshal(data)
iferr!=nil{
log.Fatal(err)
}
log.Printf(string(out))
}
funcDecodeXml() {
vardataMyData
varxmlstrstring=`test2running`
err:=xml.Unmarshal([]byte(xmlstr),&data)
iferr!=nil{
log.Fatal(err)
}
log.Printf("%+v",data)
}
funcmain() {
EncodeXml()
DecodeXml()
}json 文件的解析和生成
packagemain
import(
"encoding/json"
"log"
)
typeMyDatastruct{
Namestring`json:"name"`
Statusstring`json:"status"`
}
funcEncodeJson() {
vardataMyData
data.Name="test3"
data.Status="running"
out,err:=json.Marshal(data)
iferr!=nil{
log.Fatal(err)
}
log.Printf(string(out))
}
funcDecodeJson() {
vardataMyData
varjsonstrstring=`{"name":"test4","status":"crashed"}`
err:=json.Unmarshal([]byte(jsonstr),&data)
iferr!=nil{
log.Fatal(err)
}
log.Printf("%+v",data)
}
funcmain() {
EncodeJson()
DecodeJson()
}结构体上绑定方法
packagemain
import(
"encoding/json"
"log"
)
typeMyDatastruct{
Namestring`json:"name"`
Statusstring`json:"status"`
}
func(this*MyData)Encode() (string,error) {
out,err:=json.Marshal(this)
iferr!=nil{
return"",err
}
returnstring(out),nil
}
func(this*MyData)Decode(strstring)error{
err:=json.Unmarshal([]byte(str),this)
iferr!=nil{
returnerr
}
returnnil
}
func(this*MyData)Debug() {
log.Printf("debug: name=%s, status=%s",this.Name,this.Status)
return
}
funcmain() {
vardataMyData
varjsonstrstring=`{"name":"test5","status":"running"}`
err:=data.Decode(jsonstr)
iferr!=nil{
log.Fatal(err)
}
data.Debug()
data.Name="test6"
data.Status="stopped"
out,err:=data.Encode()
iferr!=nil{
log.Fatal(err)
}
log.Printf(out)
}正则表达式的匹配
packagemain
import(
"log"
"regexp"
)
funcmain() {
line:="11.22.33|44.55.66"
pcrestr:="(\\d+).(\\d+).(\\d+)"
re:=regexp.MustCompile(pcrestr)
results:=re.FindAllStringSubmatch(line,-1)
for_,matched:=rangeresults{
log.Printf("%s, %s, %s\n",matched[1],matched[2],matched[3])
}
return
}一行代码实现 http 文件服务器
packagemain
import(
"log"
"net/http"
)
funcmain() {
log.Fatal(http.ListenAndServe(":8080",http.FileServer(http.Dir("/tmp/test"))))
}
领取专属 10元无门槛券
私享最新 技术干货