天天看點

golang int64轉string_Golang快速入門:從菜鳥變大佬

最近寫了不少Go代碼,但是寫着寫着,還是容易忘,尤其是再寫點Python代碼後。是以找了一篇不錯的Golang基礎教程,翻譯一下,時常看看。

原文連結: 「Learning Go — from zero to hero」 by Milap Neupane

開始

Go是由各種 包 組成的。main包是程式的入口,由它告訴編譯器,這是一個可執行程式,而不是共享包。main包定義如下:

package main           

工作區

Go的工作區是由環境變量

GOPATH

決定的。

你可以在工作區裡随心所欲地寫代碼,Go會在

GOPATH

或者

GOROOT

目錄下搜尋包。注:

GOROOT

是Go的安裝路徑。

設定

GOPATH

為你想要的目錄:

# export 環境變量
export GOPATH=~/workspace
# 進入工作區目錄
cd ~/workspace           

在工作區目錄裡建立

mian.go

檔案。

package main

import (
 "fmt"
)

func main(){
  fmt.Println("Hello World!")
}           

我們使用

import

關鍵字來引入一個包。

func main

是執行代碼的入口,fmt是Go的内置包,主要用來格式化輸入/輸出。而Println是fnt中的一個列印函數。

想要運作Go程式,有兩種方法。

方法一

大家都知道,Go是一門編譯型語言,是以在執行之前,我們需要先編譯它。

> go build main.go           

這個指令會生成二進制可執行檔案 main,然後我們再運作它。

> ./main 
# Hello World!           

方法二

一個

go run

指令就可以搞定。

go run main.go
# Hello World!           

注意:你可以在這個網站執行本文中的代碼。

變量

Go中的變量都是顯式聲明的。Go是靜态語言,是以聲明變量時,就會去檢查變量的類型。

變量聲明有以下三種方式。

# 1) a的預設值為0
var a int

# 2) 聲明并初始化a,a自動指派為int
var a = 1

# 3) 簡寫聲明
message := "hello world"           

還可以在一行聲明多個變量

var b, c int = 2, 3           

資料類型

數字,字元串 和 布爾型

Go 支援的數字存儲類型有很多,比如

int

,

int8

,

int16

,

int32

,

int64

,

uint

,

uint8

,

uint16

,

uint32

,

uint64

,

uintptr

等等。

字元串類型存儲一個位元組序列。使用

string

關鍵字來聲明。

布爾型使用

bool

聲明。

Go還支援複數類型資料類型,可以使用

complex64

complex128

進行聲明。

var a bool = true
var b int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)           

數組, 分片 和 映射Map

數組是包含同一資料類型的元素序列,在聲明時确定數組長度,是以不能随意擴充。

數組的聲明方式如下:

var a [5]int           

多元數組的聲明方式如下:

var multiD [2][3]int           

Go中的數組有一定限制,比如不能修改數組長度、不能添加元素、不能擷取子數組。這時候,更适合使用

slice[分片]

這一類型。

分片用于存儲一組元素,允許随時擴充其長度。分片的聲明類似數組,隻是去掉了長度聲明。

var b []int           

這行代碼會建立一個 0容量、0長度的分片。也可以使用以下代碼 設定分片的容量和長度。

// 初始化一個長度為5,容量為10的分片
numbers := make([]int,5,10)           

實際上,分片是對數組的抽象。分片使用數組作為底層結構。一個分片由三部分組成:容量、長度和指向底層數組的指針。

golang int64轉string_Golang快速入門:從菜鳥變大佬

使用

append

或者

copy

方法可以擴大分片的容量。

append

方法在分片的末尾追加元素,必要時會擴大分片容量。

numbers = append(numbers, 1, 2, 3, 4)           

還可以使用

copy

方法來擴大容量。

// 建立一個更大容量的分片
number2 := make([]int, 15)
// 把原分片複制到新分片
copy(number2, number)           

如何建立一個分片的子分片呢?參考以下代碼。

// 建立一個長度為4的分片
number2 = []int{1,2,3,4}
fmt.Println(numbers) // -> [1 2 3 4]
// 建立子分片
slice1 := number2[2:]
fmt.Println(slice1) // -> [3 4]
slice2 := number2[:3]
fmt.Println(slice2) // -> [1 2 3]
slice3 := number2[1:4]
fmt.Println(slice3) // -> [2 3 4]           

Map也是Go的一種資料類型,用于記錄鍵值間的映射關系。使用以下代碼建立一個map。

var m map[string]int

// 新增 鍵/值
m['clearity'] = 2
m['simplicity'] = 3
// 列印值
fmt.Println(m['clearity']) // -> 2
fmt.Println(m['simplicity']) // -> 3           

這裡,m是一個鍵為string,值為int的map變量。

類型轉換

接下來看一下如何進行簡單的類型轉換。

a := 1.1
b := int(a)
fmt.Println(b)
//-> 1           

并非所有的資料類型都能轉換成其他類型。注意:確定資料類型與轉換類型互相相容。

條件語句

if else

參考以下代碼中的if-else語句進行條件判斷。注意:花括号與條件語句要在同一行。

if num := 9; num < 0 {
 fmt.Println(num, "is negative")
} else if num < 10 {
 fmt.Println(num, "has 1 digit")
} else {
 fmt.Println(num, "has multiple digits")
}           

switch case

switch-case用于組織多個條件語句,詳看以下代碼

i := 2
switch i {
case 1:
 fmt.Println("one")
case 2:
 fmt.Println("two")
default:
 fmt.Println("none")
}           

循環

Go中用于循環的關鍵字隻有一個

for

i := 0
sum := 0
for i < 10 {
 sum += 1
  i++
}
fmt.Println(sum)           

以上代碼類似于C語言中的

while

循環。另一種循環方式如下:

sum := 0
for i := 0; i < 10; i++ {
  sum += i
}
fmt.Println(sum)           

Go中的死循環

for {
}           

指針

Go提供了指針,用于存儲值的位址。指針使用

*

來聲明。

var ap *int           

這裡的ap變量即指向整型的指針。使用

&

運算符擷取變量位址,

*

運算符用來擷取指針所指向的值。

a := 12
ap = &a

fmt.Println(*ap)
// => 12           

以下兩種情況,通常優先選用指針。

  • 把結構體作為參數傳遞時。因為值傳遞會耗費更多記憶體。
  • 聲明某類型的方法時。傳遞指針後,方法/函數可以直接修改指針所指向的值。

比如:

func increment(i *int) {
  *i++
}
func main() {
  i := 10
  increment(&i)
  fmt.Println(i)
}
//=> 11           

函數

main

包中的

main

函數是go程式執行的入口,除此以外,我們還可以定義其他函數。

func add(a int, b int) int {
 c := a + b
 return c
}
func main() {
 fmt.Println(add(2, 1))
}
//=> 3           

如上所示,Go中使用

func

關鍵字加上函數名來定義一個函數。函數的參數需要指明資料類型,最後是傳回的資料類型。

函數的傳回值也可以在函數中提前定義:

func add(a int, b int) (c int) {
  c = a + b
  return
}
func main() {
  fmt.Println(add(2, 1))
}
//=> 3           

這裡c被定義為傳回值,是以調用

return

語句時,c會被自動傳回。

你也可以一次傳回多個變量:

func add(a int, b int) (int, string) {
  c := a + b
  return c, "successfully added"
}
func main() {
  sum, message := add(2, 1)
  fmt.Println(message)
  fmt.Println(sum)
}           

方法、結構體和接口

Go 不是完全面向對象的語言,但是有了 方法、結構體和接口,它也可以達到面向對象的效果。

Struct 結構體

結構體包含不同類型的字段,可用來對資料進行分組。例如,如果我們要對Person類型的資料進行分組,那麼可以定義一個人的各種屬性,包括姓名,年齡,性别等。

type person struct {
  name string
  age int
  gender string
}           

有了Person類型後,現在來建立一個 Person對象:

//方法 1: 指定參數和值
p = person{name: "Bob", age: 42, gender: "Male"}

//方法 2: 僅指定值
person{"Bob", 42, "Male"}           

可以使用

.

來擷取一個對象的參數。

p.name
//=> Bob
p.age
//=> 42
p.gender
//=> Male           

也可以通過結構體的指針對象來擷取參數。

pp = &person{name: "Bob", age: 42, gender: "Male"}
pp.name
//=> Bob           

方法

方法是一種帶有接收器的函數。接收器可以是一個值或指針。我們可以把剛剛建立的Person類型作為接收器來建立方法:

package main
import "fmt"

// 定義結構體
type person struct {
  name   string
  age    int
  gender string
}

// 定義方法
func (p *person) describe() {
  fmt.Printf("%v is %v years old.", p.name, p.age)
}
func (p *person) setAge(age int) {
  p.age = age
}

func (p person) setName(name string) {
  p.name = name
}

func main() {
  pp := &person{name: "Bob", age: 42, gender: "Male"}
  
  // 使用 . 來調用方法 
  pp.describe()
  // => Bob is 42 years old
  pp.setAge(45)
  fmt.Println(pp.age)
  //=> 45
  pp.setName("Hari")
  fmt.Println(pp.name)
  //=> Bob
}           

注意,此處的接收器是一個指針,方法中對指針進行的任何修改,都可以反映在接收器

pp

上。這樣可以避免複制帶來的記憶體消耗。

注意:上面示例中,

age

被修改了,而

name

不變。因為隻有

setAge

傳入的是指針類型,可以對接收器進行修改。

接口

在Go中,接口是方法的集合。接口可以對一個類型的屬性進行分組,比如:

type animal interface {
  description() string
}           

animal

是一個接口。通過實作

animal

接口,我們來建立兩種不同類型的動物。

package main

import (
  "fmt"
)

type animal interface {
  description() string
}

type cat struct {
  Type  string
  Sound string
}

type snake struct {
  Type      string
  Poisonous bool
}

func (s snake) description() string {
  return fmt.Sprintf("Poisonous: %v", s.Poisonous)
}

func (c cat) description() string {
  return fmt.Sprintf("Sound: %v", c.Sound)
}

func main() {
  var a animal
  a = snake{Poisonous: true}
  fmt.Println(a.description())
  a = cat{Sound: "Meow!!!"}
  fmt.Println(a.description())
}

//=> Poisonous: true
//=> Sound: Meow!!!           

在main函數中,我們建立了一個類型為animal的變量a。然後,給動物指定蛇和貓的類型,并列印

a.description

在Go中,所有的代碼都寫在包裡面。

main

包是程式執行的入口,Go自帶了很多内置包,最有名的就是剛剛用過的

fmt

包。

“Go packages in the main mechanism for programming in the large that go provides and they make possible to divvy up a large project into smaller pieces.”

— Robert Griesemer

安裝一個包

go get <package-url-github>
// 舉個栗子
go get github.com/satori/go.uuid           

包預設安裝在

GOPATH

環境變量設定的工作區中。可以使用

cd $GOPATH/pkg

指令進入目錄,檢視已安裝的包。

自定義包

首先建立一個

custom_package

檔案夾

> mkdir custom_package
> cd custom_package           

假設要建立一個

person

包,首先在

custom_package

目錄下建立一個

person

檔案夾。

> mkdir person
> cd person           

然後建立一個

person.go

檔案

package person
func Description(name string) string {
  return "The person name is: " + name
}
func secretName(name string) string {
  return "Do not share"
}           

現在需要安裝這個包,以便引入并使用它。

> go install           
注意:如果以上指令報錯,确認一下

GO111MODULE

環境變量是否設定正确, 參考連結。

然後回到

custom_package

目錄下,建立一個

main.go

檔案。

package main
import(
  "custom_package/person"
  "fmt"
)
func main(){ 
  p := person.Description("Milap")
  fmt.Println(p)
}
// => The person name is: Milap           

現在,就可以引入包,并調用

Description

方法了。注意,

secretName

方法是小寫字母開頭的私有方法,是以不能被外部調用。

包的文檔

Go内置了對封包檔的支援。運作以下指令生成文檔:

go doc person Description           

這将為

person

包生成

Description

函數的文檔。請使用以下指令運作Web伺服器,檢視文檔:

godoc -http=":8080"           

打開這個連結http://localhost:8080/pkg/,就能看到文檔了。

Go中的一些内置包

fmt

fmt

包實作了格式化I/O功能。我們已經使用過這個包列印内容到标準輸出流了。

json

另外一個很有用的包是

json

,用來編碼/解碼

Json

資料。

// 編碼
package main

import (
  "fmt"
  "encoding/json"
)

func main(){
  mapA := map[string]int{"apple": 5, "lettuce": 7}
  mapB, _ := json.Marshal(mapA)
  fmt.Println(string(mapB))
}
// 解碼
package main

import (
  "fmt"
  "encoding/json"
)

type response struct {
  PageNumber int `json:"page"`
  Fruits []string `json:"fruits"`
}

func main(){
  str := `{"page": 1, "fruits": ["apple", "peach"]}`
  res := response{}
  json.Unmarshal([]byte(str), &res)
  fmt.Println(res.PageNumber)
}
//=> 1           

使用

Unmarshal

解碼json位元組時,第一個參數是json位元組,第二個是期望解碼後的結構體指針。注意:

json:"page"

負責把

page

映射到結構體中的

PageNumber

字段上。

錯誤處理

報錯是程式中的意外産物。假如我們正在使用

API

調用一個外部服務。這個

API

調用可能成功,也可能失敗。比如,可以使用以下方法,處理報錯:

package main

import (
  "fmt"
  "net/http"
)

func main(){
  resp, err := http.Get("http://example.com/")
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(resp)
}           

傳回自定義錯誤

在寫函數時,我們可能會遇到需要報錯的情景,這時可以傳回一個自定義的

error

對象。

func Increment(n int) (int, error) {
  if n < 0 {
    // return error object
    return nil, errors.New("math: cannot process negative number")
  }
  return (n + 1), nil
}
func main() {
  num := 5
 
  if inc, err := Increment(num); err != nil {
    fmt.Printf("Failed Number: %v, error message: %v", num, err)
  }else {
    fmt.Printf("Incremented Number: %v", inc)
  }
}           

大部分的内置包或者外部包,都有自己的報錯處理機制。是以我們使用的任何函數可能報錯,這些報錯都不應該被忽略,應該像上面示例中,在調用函數的地方,優雅地處理報錯。

Panic

當程式在運作過程中,突然遇到了未處理的報錯,就會導緻

panic

。在Go中,更推薦使用

error

對象,而不是

panic

來處理異常。發生

panic

後,程式會停止運作,但會運作

defer

語句代碼。

//Go
package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}           

Defer

Defer

語句總是在函數最後執行。

在上面的栗子中,我們觸發了

panic

,但是

defer

語句依然會在最後執行。

Defer

适用于 需要在函數最後執行某些操作的場景,比如關閉檔案。

并發

Go在設計時考慮了并發性。 Go中的并發可以通過輕量級線程

Go routines

來實作。

Go routine

Go routine

是一個函數,它可以與另一個函數并行或并發執行。 建立

Go routine

非常簡單,隻需在函數前面添加關鍵字

go

,就可以使其并行執行。 同時,它很輕量級,是以可以建立上千個

routine

package main
import (
  "fmt"
  "time"
)
func main() {
  go c()
  fmt.Println("I am main")
  time.Sleep(time.Second * 2)
}
func c() {
  time.Sleep(time.Second * 2)
  fmt.Println("I am concurrent")
}
//=> I am main
//=> I am concurrent           

上面的示例中,c函數是一個

Go routine

,與main函數中的線程并行。有時我們想在多個線程之間共享資源。 Go傾向于不與另一個線程共享變量,因為這會增加死鎖和資源等待的可能。但是仙人自有妙招,就是接下來講到的

go channel

Channels

我們可以使用

channel

在兩個

routine

之間傳遞資料。建立

channel

時,需要指定其接收的資料類型。

c := make(chan string)           

通過上面建立的

channel

,我們可以發送/接收

string

類型的資料。

package main

import "fmt"

func main(){
  c := make(chan string)
  go func(){ c <- "hello" }()
  msg := <-c
  fmt.Println(msg)
}
//=>"hello"           

接收方

channel

會一直等待發送方發資料到

channel

單向channel

在某些場景下,我們希望

Go routine

隻接收資料但不發送資料,反之亦然。 這時,我們可以建立一個單向

channel

package main

import (
 "fmt"
)

func main() {
 ch := make(chan string)
 
 go sc(ch)
 fmt.Println(<-ch)
}

// sc函數:隻能發送資料給 channel,不能接收資料
func sc(ch chan<- string) {
 ch <- "hello"
}           

使用

select

語句在

Go routine

中處理多個

channel

一個函數可能正在等待多個通道。這時,我們可以使用

select

語句。

package main

import (
 "fmt"
 "time"
)

func main() {
 c1 := make(chan string)
 c2 := make(chan string)
 go speed1(c1)
 go speed2(c2)
 fmt.Println("The first to arrive is:")
 select {
 case s1 := <-c1:
  fmt.Println(s1)
 case s2 := <-c2:
  fmt.Println(s2)
 }
}

func speed1(ch chan string) {
 time.Sleep(2 * time.Second)
 ch <- "speed 1"
}

func speed2(ch chan string) {
 time.Sleep(1 * time.Second)
 ch <- "speed 2"
}
// => The first to arrive is:
// => speed 2           

Buffered channel

在Go中,你還可以使用緩沖區

channel

,如果緩沖區已滿,發送到該

channel

的消息将被阻塞。

package main

import "fmt"

func main(){
  ch := make(chan string, 2)
  ch <- "hello"
  ch <- "world"
  ch <- "!" // extra message in buffer
  fmt.Println(<-ch)
}

// => fatal error: all goroutines are asleep - deadlock!           

最後唠唠嗑

為什麼

Golang

能夠成功呢?

Simplicity… — Rob-pike

因為簡單...

好了,本文終于結束了!你從菜鳥變成大佬了嗎?開個玩笑,希望看完能有所收獲。

作者: wwwn

出處:部落格園

原文連結: https://www. cnblogs.com/wwwn/p/1280 4506.html

歡迎關注:測試開發Guide,持續更新測試開發幹貨、面試資料。