聊聊Golang中的幾種常用基本資料型別

2022-06-30 14:00:53
本篇文章帶大家瞭解一下golang 的幾種常用的基本資料型別,如整型,浮點型,字元,字串,布林型等,並介紹了一些常用的型別轉換操作。

Golang 是一種強型別語言,變數使用時需要進行強制型別定義,一旦某一個變數被定義,如果不經強制轉換,那麼它永遠就是該型別。

變數宣告及初始化

Golang 推薦使用駝峰式命名,如 QuoteRuneToASCII 和 parseRequestLine 等。

定義變數的幾種形式:

// 第一種,先宣告型別,再進行初始化賦值
// 如果沒有初始化,則變數預設為零值。
var a int
a = 3

// 第二種, 根據賦值自動適配型別
b := 3   // 這種寫法只能在函數體中出現
         // 效果等價於 var b = 3, 但後者可以在函數體之外的地方使用

// 第三種,宣告型別的同時進行賦值
var c int = 3

// 第四種,使用 new 建立變數,然後返回變數的地址,型別為 *type
p := new(int)
*p = 3

// 與 python 相同,交換兩個變數的值,可以使用 a, b = b, a 
// 但與 python 不同的是兩個變數的型別必須是相同的。

注意:

  • Golang 中不允許出現未使用的變數,否則會報錯 a declared and not used。但是全域性變數是允許宣告但不使用。
  • Golang 的封裝性由變數名第一個字母決定,大寫字母開頭的變數是可匯出的變數,可以在包外使用,小寫字母或者下劃線開頭的變數只能在本包內使用。
  • 「:=」 是一個變數宣告語句,而 「=‘ 是一個變數賦值操作,一行中簡短宣告多個變數時,":=" 語句中必須至少要宣告一個新的變數。

基本資料型別

獲取元素型別

// 方法一,建立 reflect 變數
import "reflect"

var a int
typeOfA := reflect.TypeOf(a)
fmt.Println(typeOfA)
// 或者直接 fmt.Println(reflect.TypeOf(a))

// 方法二,使用 fmt 檢視
import "fmt"
var a int
fmt.Printf("%T", a)   // 注意不能用 Println

整型

數位表示型別的位元長度,不同長度的型別是獨立的,混合計算會報錯

int 的長度由硬體決定:

資料型別計算機架構位元寬度位元組寬度
int32位元324
int64位元648
uint32位元324
uint64位元648

也可以指定 int 的固定長度型別:

資料型別位元寬度
int88
int1616
int32 (rune, 可以用來表示一個 Unicode 字元)32
int6464
uint8 (byte)8
uint1616
uint3232
uint6464

浮點型別

Golang 支援兩種浮點型數值:float32 和 float64。可以使用 math 包中的常數獲取浮點型別的邊界值

const (
    MaxFloat32             = 3.40282346638528859811704183484516925440e+38  // 2**127 * (2**24 - 1) / 2**23
    SmallestNonzeroFloat32 = 1.401298464324817070923729583289916131280e-45 // 1 / 2**(127 - 1 + 23)

    MaxFloat64             = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
    SmallestNonzeroFloat64 = 4.940656458412465441765687928682213723651e-324 // 1 / 2**(1023 - 1 + 52)
)

注意:浮點型別有精度限制,計算結果不準確,不推薦使用
需要進行浮點計算時,可以使用放大 100 倍的整數計算,計算完成再將結果縮小 100 倍

複數

Golang 自帶了複數型別,分別為 complex64, complex128,定義時需要制定實部和虛部,如:

var c1, c2 complex64
c1 = 1 + 2i
c2 = 2 + 3i
fmt.Println(c1 + c2) // out: 3 + 5i

布林

bool 型別,有 true 和 false 兩種型別,要注意的是,和 python 不同,Golang 不會自動對變數識別 true or false,因此 if 1 這種方式是會報錯的

錯誤

error,error 實際上是一種介面型別,後續會單獨介紹,可以先參考官網檔案 error document

字元

rune, byte 可用於表示單個字元,字串,rune 陣列,byte 陣列之間可以相互轉換,讓我們從原始碼看看 byte 和 rune 的區別:

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8


// 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.
type rune = int32

可以看出,byte 為 1 位元組的 uint8 數值,rune 為 4 位元組的 int32 數值,通過下面的程式碼,可以看出一箇中文字元佔了三個位元組,在 []byte{} 中長度為 3,string 和 []byte{} 的長度相等,string 實際上就是由 byte 陣列實現的。

a := "測試T"
aRune := []rune(a)
aByte := []byte(a)
fmt.Println(a, aRune, aByte)
fmt.Println(len(a), len(aRune), len(aByte))
  
// output:
// 測試T [27979 35797 84] [230 181 139 232 175 149 84]
// 7 3 7

字串

string 型別,與 python 不同,go 的字串只能用 「」 或者 原始字串引號 ``(重音符號)

// 普通字串
s1 := "hello\nworld"    // 裡面的 \n 會被跳脫為回車

// 原始字串,相當於 python 中在字串前面加 r
s2 := `hello\nworld`    // 所有內容都是字元,不會被跳脫

字串是不可修改的,對字串進行修改操作時,會建立一個新的字串賦值給當前字串

s := "hello"
s += " world" // 這樣不會報錯,會把 "hello world" 整個重新分配給 s

s[0] = "a"    // 這樣寫就會報錯

因此,如果涉及到大批次的字串拼接或者修改的操作,效率會很低且耗費資源,推薦使用 bytes 包的 Buffer 來進行操作

import "bytes"

bs := bytes.NewBufferString("")
bs.WriteString("hello ")
bs.WriteString("world")
s := bs.String()  // out: hello world

更多內容可檢視官網檔案 bytes document

字串常用操作

獲取字串長度

字串本質上是字元陣列,所以可以使用 len(s) 獲取長度。
要注意的是對使用者名稱等可能包含中文或其他字元的字串進行判斷時,使用 utf8 長度判斷而不是 len() 判斷,否則識別出來的長度是不正確的

fmt.Println(len("測試"))							// 6
fmt.Println(utf8.RuneCountInString("測試"))			// 2
fmt.Println(utf8.RuneCountInString("混合測試,fight!"))  // 11

數值和字串的相互轉換

  • int 轉換為 string

int 轉換為 string 不能直接 string(i),這樣轉完會變成空值

s := strconv.Itoa(i)
  • int64 轉換為 string

var i64 int64s := strconv.FormatInt(i64,10)  // 第二個引數為基數(2~36),如十進位制二進位制等
  • string 轉換為 int 型

使用非數值的字串轉換為數值時會發生錯誤,比如 「abc」 轉為數位或者使用空字串,因此需要接收 error。

i, err := strconv.Atoi(s)
if err != nil {
	return err
}
  • string 轉換為 int64

//第二個引數為基數(2~36),
//第三個引數位大小表示期望轉換的結果型別,其值可以為0, 8, 16, 32和64,
//分別對應 int, int8, int16, int32和int64
i64, err := strconv.ParseInt(s, 10, 64)

string 和 slice 相互轉換

  • slice 轉換為 string

import "strings"

sli := []string{"111","2222","333","4444"}
ss := strings.Join(s,"--")  // 輸出為 111--2222--333--4444
  • string 轉換為 slice

ss := "a,b,c"
sli := strings.Split("a,b,c", ",")

字串替換

使用 strings 包的 Replace 函數進行字串替換

// Replace
// 如果最後一個引數為正整數,表示將前 n 個 old 替換為 new
// 如果最後一個引數為負數,一般為-1,表示全域性替換,相當於 ReplaceAll
func Replace(s, old, new string, n int) string
// 例:
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
// output: moo moo moo
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", 2))
// output: moo moo oink

// ReplaceAll
func ReplaceAll(s, old, new string) string

指標

如果用「var x int」宣告語句宣告一個x變數,那麼 &x 表示式(取x變數的記憶體地址)將產生一個指向該整數變數的指標,指標對應的資料型別是 *int ,指標被稱之為「指向int型別的指標」。同時 *p 表示式對應p指標指向的變數的值。

x := 1
p := &x     // p 為指向 x 的指標   
/* 等價於:
var p *int
p = &x
*/
fmt.Println(*p)   // 輸出 1
*p = 2     // 修改 *p 指向物件的值
fmt.Println(x)   // 輸出 2

任何型別的指標的零值都是 nil。如果 p != nil 測試為真,那麼 p 是指向某個有效變數。指標之間也是可以進行相等測試的,只有當它們指向同一個變數或全部是nil時才相等。

自定義型別

type 型別名字 底層型別

這種操作的意義在於賦予相同基本型別(比如 float64)的變數不同的含義,比如華氏溫度和攝氏溫度,這兩種溫度被定義成不同的資料型別,可以避免混用不同的溫度單位,產生錯誤的計算結果。

// Package tempconv performs Celsius and Fahrenheit temperature computations.
package tempconv
import "fmt"

type Celsius float64 // 攝氏溫度
type Fahrenheit float64 // 華氏溫度

const (
    AbsoluteZeroC Celsius = -273.15 // 絕對零度
    FreezingC Celsius = 0 // 結冰點溫度
    BoilingC Celsius = 100 // 沸水溫度
)

func CToF(c Celsius) Fahrenheit { 
    return Fahrenheit(c*9/5 + 32) 
}

func FToC(f Fahrenheit) Celsius { 
    return Celsius((f - 32) * 5 / 9) 
}

常數

go 使用 const 建立常數,它們在編譯時被建立,只能是數位、字串或布林
值。

// 常數用的是 = 而不是 :=
const x = 42

可以使用 iota 生成列舉值,第一個iota 表示為0,當iota 再次在新的一行使用時,它的值累加 1。第一行之外的 = iota 可以省略,只要出現了一次 iota, 當前 const 結構中後面每一行 iota的值加1。

package main

import "fmt"

func main() {
    const (
        a = iota  // 0
        b		 // 1
        c		 // 2
        d = "ha"  // "ha", iota += 1, iota = 3
        e		 // "ha", iota += 1, iota = 4
        f = 100   // 100, iota += 1, iota = 5
        g		 // 100, iota += 1, iota = 6
        h = iota  // 7
        i		 // 8
    )
    
    fmt.Println(a,b,c,d,e,f,g,h,i)
    // 結果為 0 1 2 ha ha 100 100 7 8
    
    // 另一個有趣的範例
    const (
        x = 1 << iota  // 1 << 0
        y = 3 << iota  // 3 << 1
        z			  // 3 << 2
        q			  // 3 << 3
    )
    
    fmt.Println(x,y,z,q)
    // 結果為 1 6 12 24
}

格式化輸出

使用 fmt 包進行格式化輸出,可參考連結
格式化輸出方式

強制型別轉換

type_name(expression)
// 如 
f := float32(3)
i := int64(2)

注意:將長型別強制轉換為短型別時會發生截斷

更多程式設計相關知識,請存取:!!

以上就是聊聊Golang中的幾種常用基本資料型別的詳細內容,更多請關注TW511.COM其它相關文章!