【教你編程】go語言簡介
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 routine
go 的線程叫協程
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
",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"))))
}
TAG:孤獨的飯桶 |