6.数据类型

予早 2024-12-09 22:48:36
Categories: Tags:

分类

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint

const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Pointer
    Slice
    String
    Struct
    UnsafePointer
)
// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.

Go中数据类型可以分为两大类:

  1. 基本数据类型
    1. 数值型
      • 整数类型,byte(等价unit8)rune(等价int32)、int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64
      • 浮点类型,float(默认float64)、float32、float64
    2. 布尔型,bool
    3. 字符串,string
  2. 派生数据类型(复杂数据类型)
    1. 指针
    2. 数组
    3. 结构体
    4. 管道
    5. 函数
    6. 切片
    7. 接口
    8. map

注意:

  1. 没有单独的字符型,可以使用byte存储字符并格式化转换为字符
  2. int及uint在32位系统占4字节,在64位系统占8字节,即是一个机器字

零值

当声明该数据类型的变量时,不手动赋初值就会使用零值,也可认为是默认值

数据类型 默认值
整数类型 0
浮点类型 0.000000
布尔类型 false
字符串类型 “”

数值类型

字符处理

package main

import (
    "fmt"
)

func main() {
    // type byte = uint8
    // 需要格式化输出
    var v1 byte = 'c'
    fmt.Printf("%T, %c", v1, v1)
}

浮点类型

package main

import (
    "fmt"
)

func main() {
    var v1 float32 = 3.14
    var v2 float64 = -3.14
    var v3 float64 = 314e-2
    var v4 float64 = -314e-2
    v5 := -.14

    fmt.Println(v1, v2, v3, v4, v5)

}

布尔类型

布尔类型占一个字节

package main

import "fmt"

func main() {
    var b bool = true // 事实上goland会提示类型可以省略
    fmt.Println(b)

    //// bool is the set of boolean values, true and false.
    //type bool bool
    //
    //// true and false are the two untyped boolean values.
    //const (
    //	true  = 0 == 0 // Untyped bool.
    //	false = 0 != 0 // Untyped bool.
    //)
}

字符串

字符串一旦赋值,便不可更改

如果想要“修改”字符串,只能强转[]byte复制一份,修改后强转为string,不过byte只允许英文,使用[]rune支持中文字符

package main

import "fmt"

func main() {

    var s1 = "a string"
    fmt.Println(s1)
    fmt.Printf("%T", s1) // string

    var s2 = `
        func xxx() {
            ...
        }
...`

    fmt.Println(s2) // 反引号原样输出

    var s3 = "a " + "b" + // 这个加号必须在第一行明示编译器还没结束,与自动添加分号进行区分
        "c"
    fmt.Println(s3)
}

字符串操作

package main

import (
    "fmt"
    "strings"
)

func main() {

    //查找子串在字符串中第一次出现的位置,没有返回-1
    fmt.Println(strings.Index("Hello World", "llo"))

    //是否包含子串
    fmt.Println(strings.Contains("Hello World", "llo"))

    //统计字符串中有几个字串
    fmt.Println(strings.Count("Hello World", "llo"))

    //不区分大小写比较字符串,区分大小写比较直接使用==
    fmt.Println(strings.EqualFold("Hello World", "hello world"))

    //字符串替换,-1表示全部替换???
    fmt.Println(strings.Replace("Hello World", "ll", "ii", 3))

    //字符串分割
    fmt.Println(strings.Split("src/go-code/main", "/"))

    //大小写转换
    fmt.Println(strings.ToUpper("Hello World"))
    fmt.Println(strings.ToLower("Hello World"))

    //去除字符串两边空格
    fmt.Println(strings.TrimSpace("   Hello World   "))
    //去除字符串两边指定字符
    fmt.Println(strings.Trim("---Hello World---", "-"))
    //去除字符串左边指定字符
    fmt.Println(strings.TrimLeft("---Hello World---", "-"))
    //去除字符串右边指定字符
    fmt.Println(strings.TrimRight("---Hello World---", "-"))

    //判断字符串前缀
    fmt.Println(strings.HasPrefix("https://xxx.com", "http"))
    //判断字符串后缀
    fmt.Println(strings.HasSuffix("main.go", ".go"))
}

数组

  1. Go中数组是值类型
  2. 数组元素类型相同,但元素个数不同则两者类型不同,即数组长度是数组类型的一部分
  3. 数组声明后长度固定,不可动态变化

一维数组

package main

import "fmt"

func main() {
    // 声明数组变量,随后逐一赋值
    var a [3]float64
    a[0] = 1.0
    a[1] = .3
    a[2] = 2

    // 声明变量,随后赋值
    var b [3]float64 = [3]float64{1, 2, 3}
    fmt.Printf("%T\n", b)

    // 可以省略类型,由编译器自动推断
    var c = [3]float64{1, 2, 3}
    fmt.Printf("%T\n", c)

    // 可以使用不定长度,由编译器计算
    var d = [...]float64{1, 2, 3}
    fmt.Printf("%T\n", d)

    // 数组支持按序号赋值
    var e = [...]float64{2: 3, 0: 1, 1: 2}
    fmt.Printf("%T\n", e)
    fmt.Println(e)
}
package main

import "fmt"

func change(array *[3]float64) { //必须传入长度,长度不一样也是不一样的类型
    // 对数组进行遍历,这里带不带*都可,不带*Go会自动处理
    for i, v := range *array {
        fmt.Println(i, v)
        // 单纯赋值不会改变其值
        // v = 1
        // v += 1

        // 必须修改数组元素才行,两者均可
        (*array)[i] += 1
        array[i] += 1
    }
}

func main() {
    // 由于数组是值类型,所以想要改变数组需要将其引用传递进去
    var a = [3]float64{1, 2, 3}
    change(&a)
    for _, v := range a {
        fmt.Println(v)
    }
}

数组遍历

package main

import "fmt"

func main() {
    var a = [...]float64{1, 2, 3}

    for i := 0; i < len(a); i++ {
        fmt.Println(a[i])
    }

    for _, value := range a {
        fmt.Printf("%.2f\n", value)
    }
}

二维数组

package main

import (
    "fmt"
)

func main() {
    // 二维数组与一维数组声明方式同理
    var arr = [3][3]int{{1, 1}, {1: 1}}
    fmt.Println(arr)

    // 遍历方式同理
    for index1, value1 := range arr {
        for index2, value2 := range value1 {
            fmt.Println(index1, index2, value2)
        }
    }
}

指针

  1. 可以通过指针改变指向的值
  2. 指针变量接受地址
package main

import "fmt"

func main() {
    // 定义一个变量 int32
    i := 1
    // 使用 & 取地址,p是一个指针类型,当然这里类型可以省略
    var p *int = &i
    // 打印指针值
    fmt.Println(p)
    fmt.Printf("%+v", p)
}

数据类型转换

Go中没有隐式类型转换,不同类型间必须显式类型转换(强制类型转换)

package main

import "fmt"

func main() {

    // 常量与变量不匹配会编译失败
    // 基本数据类型和引用数据类型的类型转换是不一样的,基本数据类型复制

    var n1 int8 = 256
    fmt.Println(n1)
    // cannot use 256 (untyped int constant) as int8 value in variable declaration (overflows)
}

数字间转换

package main

import (
    "fmt"
)

func main() {

    // 整数高低精度互转
    var n1 int8 = 1
    var n2 int32 = 1000
    n1 = int8(n2) // 可以显式转换,但会丢失精度
    n2 = int32(1)
    fmt.Println("整数高低精度互转", n1, n2)

    // 浮点高低精度互转
    var n3 float32 = 1
    var n4 float64 = 2
    n3 = float32(n4)
    n4 = float64(5)
    fmt.Println("浮点高低精度互转", n3, n4)

    // 整数与浮点互转
    var n5 int8 = 1
    var n6 float32 = .6
    n5 = int8(n6)
    n6 = float32(1) // 直接截断
    fmt.Println("整数与浮点互转", n5, n6)

}

字符串与其他类型相互转换

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 字符串与(整数类型、浮点数类型、布尔类型)互转,两种方式,fmt和strconv

    // 字符串与整数类型互转
    var n1 int64 = 66             // int64 -> string
    res1 := fmt.Sprintf("%v", n1) // sprintf
    fmt.Printf("%T, %v\n", res1, res1)
    res1 = strconv.FormatInt(n1, 10) // format
    fmt.Printf("%T, %v\n", res1, res1)

    var n2 string = "66"                    // string -> int64
    res2, _ := strconv.ParseInt(n2, 10, 64) // parse
    fmt.Printf("%T, %v\n", res2, res2)

    // 字符串与浮点类型互转
    var n3 float64 = 0.03         // float64 -> string
    res3 := fmt.Sprintf("%v", n3) // sprintf
    fmt.Printf("%T, %v\n", res3, res3)
    res3 = strconv.FormatFloat(n3, 'f', 9, 64)   // format
    fmt.Printf("%T, %v, %q\n", res3, res3, res3) // 注意这里

    var n4 string = "0.06"                // string -> float64
    res4, _ := strconv.ParseFloat(n4, 64) // parse
    fmt.Printf("%T, %v\n", res4, res4)

    // 字符串与布尔值互转
    var n5 bool = true            // bool -> string
    res5 := fmt.Sprintf("%v", n5) // sprintf
    fmt.Printf("%T, %v\n", res5, res5)
    res5 = strconv.FormatBool(n5) // format
    fmt.Printf("%T, %v\n", res5, res5)

    var n6 string = "false"
    res6, _ := strconv.ParseBool(n6) // parse
    fmt.Printf("%T, %v\n", res6, res6)

    n7, _ := strconv.Atoi("123456789")
    fmt.Printf("%T, %v\n", n7, n7)

    n8 := strconv.Itoa(123456789)
    fmt.Printf("%T, %v\n", n8, n8)

    var n9 string = "shw3gsdf54dg"
    res9 := []byte(n9)
    fmt.Printf("%T, %v\n", res9, res9)

    var n10 = [...]int{115, 104, 119, 51, 103, 115, 100, 102, 53, 52, 100, 103}
    fmt.Println(n10)

}
package main

import "fmt"

func main() {
    // 字符串 byte
    var b = []byte("Hello World")
    fmt.Printf("%T\n", b)
    var str = string([]byte{9})
    fmt.Printf("%T\n", str)
}

转换失败时

package main

import (
    "fmt"
    "strconv"
)

func main() {
    s1 := "Hello World"
    n1, err := strconv.ParseInt(s1, 10, 64)

    fmt.Printf("%+v\n%+v", n1, err)
    // 遇到错误自动处理为默认值,然后通知一下
    // 0
    // strconv.ParseInt: parsing "Hello World": invalid syntax
}

map,映射

自动扩容

var m map[string]string

var m map[string]map[string]string,多重map

key的类型:

  1. bool、数字、string、指针、channel
  2. 只包含上述类型的接口、结构体、数组
  3. slice、map、func不可以
package main

import "fmt"

func main() {

    // 定义时直接写明常量值
    var singleMap map[string]string = map[string]string{"1": "1", "2": "2", "3": "3"}
    singleMap["A"] = "A"
    singleMap["B"] = "B"
    singleMap["C"] = "C"
    fmt.Println(singleMap)

    // 使用make
    singleMap = make(map[string]string, 10)
    singleMap["0"] = "0"
    delete(singleMap, "0") // 内置函数,专门用于删除 key-value
    value, flag := singleMap["0"]
    fmt.Printf("%q %#v", value, flag)

    // 多重map
    var multiMap map[string]map[string]string
    fmt.Println(multiMap)
}
package main

import "fmt"

func main() {
    m := map[string]string{"1": "1", "2": "2", "3": "3"}
    for key, value := range m {
        fmt.Println(key, value)
        //fmt.Printf(key, value)
        //1%!(EXTRA string=1)2%!(EXTRA string=2)3%!(EXTRA string=3)
        //体现Go错误自己处理的思想
    }
}

slice,切片

切片是从堆开辟的“数组”,如果切片从数组创建,切片容量=数组元素数量-切片起始元素之前的元素数量(不包括起始元素)

[]int int切片

[]map[string]string map切片

创建切片

package main

import (
    "fmt"
)

func main() {

    // 1.从数组创建切片
    var arr1 = [...]int{1, 1, 1, 1, 1, 1, 1, 2}
    slice := arr1[1:3]
    fmt.Println(slice, len(slice), cap(slice)) // 从数组创建切片,切片容量是切片在数组中起始位置到末尾所有元素的数量
    fmt.Printf("%T\n", slice)

    // 2.make创建切片
    var arr2 = make([]int, 3, 10)
    fmt.Println(arr2, len(arr2), cap(arr2))
    fmt.Printf("%T\n", arr2)

    // 二维数组的切片
    var arr3 = [3][3]int{{1, 1}, {1: 1}}
    fmt.Printf("%T", arr3[1:2])

    // slice内存
    // 起始地址(底层数组地址)、长度、容量 描述slice
    // 起始地址指向的堆内存 存储数据
}

切片可以简写

arr[0:end] arr[:end]
arr[start:len(arr)] arr[start:]
arr[0:len(arr)] arr[start:end] arr[:]

从数组创建的切片是对数据的复制

package main

import "fmt"

func main() {

    var arr1 = [...]int{1, 1, 1}
    var arr2 = arr1[:]
    var arr3 = arr2
    fmt.Printf("%T %T %T\n", arr1, arr2, arr3) // arr1 是数组,arr2和arr3是切片

    arr1[0] = 0
    fmt.Println(arr1, arr2, arr3)
    fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别

    arr2 = append(arr2, 9)
    fmt.Println(arr1, arr2, arr3)
    fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别

    arr1[0] = 8
    fmt.Println(arr1, arr2, arr3)
    fmt.Printf("%p %p %p\n", &arr1, &arr2, &arr3) // 注意切片和数组输出地址的区别
}

切片可以再切片

切片合并、切片拷贝

package main

import "fmt"

func main() {

    // 切片合并
    var arr1 = []int{1, 1, 1}
    var arr2 = []int{2, 2, 2}
    var arr3 = append(arr1, arr2...) //注意这里是把arr2作为可变参数传进去的
    fmt.Printf("%v %T %d\n", arr3, arr3, cap(arr3))

    // 注意arr3是一个全新的切片,append后会生成一个全新的切片
    arr1[0] = 0
    arr2[1] = 0
    arr3[2] = 0
    fmt.Println(arr1, arr2, arr3)

    // 切片拷贝
    arr4 := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
    arr5 := make([]int, 3)
    copy(arr5, arr4) // copy就是用于切片拷贝的,只能用于切片类型
    fmt.Println(arr4, arr5)

}

数组与切片的地址比较

package main

import "fmt"

func main() {
    //关于数组和切片的地址,注意切片arr5和&arr5都格式为%p的不同
    arr4 := [...]int{1, 1, 1}
    arr5 := arr4[:]
    fmt.Printf("%p %p %p %p %p %p\n", arr4, &arr4, &(arr4[0]), arr5, &arr5, &(arr5[0]))
    fmt.Println(arr4, &arr4, &arr4[0], arr5, &arr5, &arr5[0])
    //%!p([3]int=[1 1 1]) 0xc00000e198 0xc00000e198 0xc00000e198 0xc000008078 0xc00000e198
    //[1 1 1] &[1 1 1] 0xc00000e198 [1 1 1] &[1 1 1] 0xc00000e198
}

bytes

reader

buffer

类型断言

类型断言

package main

import "fmt"

type name struct{}

func main() {
    var a any
    a = name{}

    var b name
    var f bool
    b, f = a.(name)
    fmt.Println(b, f)

    if b, f := a.(name); f {
        fmt.Println(b, f)
    }

}
package main

import (
    "fmt"
)

func TypeJudge(item any) {
    //type switch
    //Go特有的断言类型的switch语句,比较的是类型
    switch item.(type) {
    case bool:
        fmt.Println("bool")
    case float64:
        fmt.Println("float64")
    case nil:
        fmt.Println("nil")
    case string:
        fmt.Println("string")
    case *string:
        fmt.Println("*string")
    }
}

func main() {
    s := ""
    TypeJudge(&s)
}

接口类型(interface)

空接口interface{}是没有任何方法的接口,所有类型均满足空接口,则空接口可以存储任何类型的值

var any interface{}
any = "a string"
any = 123
any = true

基于空接口实现泛型编程

func Println(v interface{}) {
    fmt.Println(v)
}

处理未知类型的类

data := []byte(`{"name":"John","age":30}`)
var result map[string]interface{}
json.Unmarshal(data, &result)

自定义类型与类型别名

自定义类型可以绑定方法

package main

import "fmt"

// 自定义类型 myInt
type myInt int

// 类型别名 yourInt
type yourInt = int

func main() {
    var n1 myInt = 1
    fmt.Printf("%T %v\n", n1, n1) // main.myInt 1

    var n2 yourInt = 1
    fmt.Printf("%T %v\n", n2, n2) // int 1
}