4. Golang的数据类型
概述
Go 语言中数据类型分为:基本数据类型
和复合数据类型
基本数据类型有:
整型、浮点型、布尔型、字符串
复合数据类型有:
数组、切片、结构体、函数、map、通道(channel)、接口等。
4.1 整型
整型的类型有很多中,包括 int8,int16,int32,int64。我们可以根据具体的情况来进行定义
如果我们直接写 int也是可以的,它在不同的操作系统中,int的大小是不一样的
- 32位操作系统:int -> int32
- 64位操作系统:int -> int64
可以通过
unsafe.Sizeof
查看不同长度的整型,在内存里面的存储空间var num2 = 12 fmt.Println(unsafe.Sizeof(num2))
4.1.1 类型转换
通过在变量前面添加指定类型
,就可以进行强制类型转换
var a1 int16 = 10
var a2 int32 = 12
var a3 = int32(a1) + a2
fmt.Println(a3) // 22
:warning: 注意,高位转低位的时候,需要注意,会存在精度丢失,比如上述16转8位的时候,就丢失了
var n1 int16 = 130
fmt.Println(int8(n1)) // 变成 -126
4.1.2 数字字面量语法
Go1.13版本之后,引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,例如:
v := 0b00101101 // 代表二进制的101101
v:= Oo377 // 代表八进制的377
4.1.3 进制转换
格式化动词 | 描述 | 示例 | 输出 |
---|---|---|---|
%v | 默认格式输出 | fmt.Printf("%v\n", 17) | 17 |
%d | 十进制格式输出 | fmt.Printf("%d\n", 17) | 17 |
%o | 八进制格式输出 | fmt.Printf("%o\n", 17) | 21 |
%b | 二进制格式输出 | fmt.Printf("%b\n", 17) | 10001 |
%x | 小写十六进制格式输出 | fmt.Printf("%x\n", 17) | 11 |
%X | 大写十六进制格式输出 | fmt.Printf("%X\n", 17) | 11 |
代码如下:
package main
import "fmt"
func main() {
var number = 17
// 原样输出
fmt.Printf("%v\n", number) //17
// 十进制输出
fmt.Printf("%d\n", number) //17
// 以八进制输出
fmt.Printf("%o\n", number) // 21
// 以二进制输出
fmt.Printf("%b\n", number) // 10001
// 以十六进制输出
fmt.Printf("%x\n", number) // 11
}
4.2 浮点型
Go语言支持两种浮点型数:float32和float64。这两种浮点型数据格式遵循IEEE754标准:
float32的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32。float64的浮点数的最大范围约为1.8e308,可以使用一个常量定义:math.MaxFloat64
打印浮点数时,可以使用fmt包配合动词%f
,代码如下:
var pi = math.Pi
// 打印浮点类型,默认小数点6位
fmt.Printf("%f\n", pi) // 3.141593
// 打印浮点类型,打印小数点后2位
fmt.Printf("%.2f\n", pi) // 3.14
// 打印浮点类型,打印小数点后3位
fmt.Printf("%.3f\n", pi) // 3.142
4.2.1 Golang中精度丢失
的问题
几乎所有的编程语言都有精度丢失的问题,这是典型的二进制浮点数精度损失问题,在定长条件下,二进制小数和十进制小数互转可能有精度丢失
d := 1129.6
fmt.Println(d * 100) //输出112959.99999999999
解决方法,使用第三方包来解决精度损失的问题(后面会详解讲解导包)
http://github.com/shopspring/decimal
4.2.2 golang中使用shopspring/decimal来处理精度问题
在 Golang 中处理浮点数存在着精度问题,而精度问题会带来诸多的麻烦。因此,我们需要使用高精度数来解决这个问题。shopspring/decimal 库是一个专门用于处理高精度数的库,支持多种精度、四则运算、比较等操作。本文将介绍如何在 Golang 中使用 shopspring/decimal 库来处理高精度数。
原文链接:https://blog.csdn.net/tyxjolin/article/details/130127779
安装 shopspring/decimal
go get github.com/shopspring/decimal
创建 Decimal 对象
使用如下代码来创建一个 Decimal 对象:
import "github.com/shopspring/decimal"
// 创建一个 Decimal 对象
num := decimal.NewFromFloat(1.23)
在上面的代码中,我们使用 decimal.NewFromFloat
方法来创建一个 Decimal 对象,这个方法接收一个 float64 类型的参数,并返回一个 Decimal 对象。
Decimal 对象的四则运算
shopspring/decimal 库支持 Decimal 对象的加减乘除等操作,完整示例如下:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 创建两个 Decimal 对象
a := decimal.NewFromFloat(1.23)
b := decimal.NewFromFloat(4.56)
// 加法
c := a.Add(b)
fmt.Printf("%v + %v = %v\n", a, b, c) // 输出:1.23 + 4.56 = 5.79
// 减法
c = a.Sub(b)
fmt.Printf("%v - %v = %v\n", a, b, c) // 输出:1.23 - 4.56 = -3.33
// 乘法
c = a.Mul(b)
fmt.Printf("%v * %v = %v\n", a, b, c) // 输出:1.23 * 4.56 = 5.6088
// 除法
c = a.Div(b)
fmt.Printf("%v / %v = %v\n", a, b, c) // 输出:1.23 / 4.56 = 0.2697368421052632
}
在上面的代码中,我们创建了两个 Decimal 对象 a 和 b,然后使用 Add、Sub、Mul 和 Div 方法来进行加减乘除等操作。
Decimal 对象的比较
shopspring/decimal 库支持 Decimal 对象的比较操作,示例如下:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
/*
golang中使用shopspring/decimal来处理精度问题
*/
// 创建两个 Decimal 对象
a := decimal.NewFromFloat(1.23)
b := decimal.NewFromFloat(4.56)
// 加法
c := a.Add(b)
fmt.Printf("%v + %v = %v\n", a, b, c) // 输出:1.23 + 4.56 = 5.79
// 减法
c = a.Sub(b)
fmt.Printf("%v - %v = %v\n", a, b, c) // 输出:1.23 - 4.56 = -3.33
// 乘法
c = a.Mul(b)
fmt.Printf("%v * %v = %v\n", a, b, c) // 输出:1.23 * 4.56 = 5.6088
// 除法
c = a.Div(b)
fmt.Printf("%v / %v = %v\n", a, b, c) // 输出:1.23 / 4.56 = 0.2697368421052632
}
在上面的代码中,我们创建了两个 Decimal 对象 a 和 b,然后使用 Cmp 方法来比较它们的大小。
设置 Decimal 对象的精度
package main
// import "fmt"
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 创建一个 Decimal 对象,并设置精度为 4
a := decimal.NewFromFloatWithExponent(1.2345678, -4)
fmt.Println(a) // 输出:1.2346
}
在上面的代码中,我们使用 decimal.NewFromFloatWithExponent
方法来创建一个 Decimal 对象,并设置它的精度为 4。
总结
shopspring/decimal 库是一个非常方便的处理高精度数的库,它支持多种精度、四则运算、比较等操作。在 Golang 中使用 shopspring/decimal 库可以有效地避免浮点数精度问题所带来的麻烦。在实际开发中,如果需要处理高精度数,可以优先考虑使用 shopspring/decimal 库。
完整代码示例:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 创建一个 Decimal 对象
num := decimal.NewFromFloat(1.23)
fmt.Println(num) // 输出:1.23
// 创建两个 Decimal 对象
a := decimal.NewFromFloat(1.23)
b := decimal.NewFromFloat(4.56)
// 加法
c := a.Add(b)
fmt.Println(c) // 输出:5.79
// 减法
c = a.Sub(b)
fmt.Println(c) // 输出:-3.33
// 乘法
c = a.Mul(b)
fmt.Println(c) // 输出:5.6088
// 除法
c = a.Div(b)
fmt.Println(c) // 输出:0.26973684210526316
// 比较大小
if a.Cmp(b) < 0 {
fmt.Println("a < b")
} else if a.Cmp(b) == 0 {
fmt.Println("a = b")
} else {
fmt.Println("a > b")
}
// 创建一个 Decimal 对象,并设置精度为 4
a = decimal.NewFromFloatWithExponent(1.23456789, -4)
fmt.Println(a) // 输出:1.2346
}
4.3 布尔类型
布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = false。
定义:
package main
import "fmt"
/*
这段代码定义了一个布尔变量 fl,并使用一个 if-else 语句来检查 fl 的值。如果 fl 为 true,则打印 "true";如果 fl 为 false,则打印
*/
func main() {
fl := false
if fl {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
4.4 字符串类型
Go 语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64等)一样。Go语言里的字符串的内部实现使用UTF-8编码。字符串的值为双引号(”)中的内容,可以在Go语言的源码中直接添加非ASCll码字符,例如:
4.4.1 定义string类型
//1、定义string类型
var str1 string = "你好 golang"
var str2 = "你好 golang"
str3 := "你好 golang"
fmt.Printf("%v--%T\n", str1, str1) // 输出:你好 golang--string
fmt.Printf("%v--%T\n", str2, str2) // 输出:你好 golang--string
fmt.Printf("%v--%T\n", str3, str3) // 输出:你好 golang--string
如果想要定义多行字符串,可以使用反引号
var str = `第一行
第二行`
fmt.Println(str)
4.4.2 字符串转义符
转义符 | 含义 |
---|---|
\n | 换行 |
\r | 回车 |
\t | 制表符 |
\\ | 反斜杠 |
\' | 单引号 |
\" | 双引号 |
案例:
package main
import "fmt"
func main() {
// 使用换行符
fmt.Println("第一行\n第二行")
// 使用制表符
fmt.Println("姓名\t年龄\t性别")
fmt.Println("张三\t25\t男")
fmt.Println("李四\t28\t女")
// 使用反斜杠
fmt.Println("这是一个路径:C:\\Program Files\\MyApp")
// 使用单引号和双引号
fmt.Println("他说:\"你好,世界!\"")
fmt.Println(`这是单引号 ' 和双引号 " 的使用`)
}
输出结果如下:
4.4.3 字符串常见操作:
函数/方法 | 描述 |
---|---|
len(str) | 求字符串的长度 |
+ 或 fmt.Sprintf | 拼接字符串,fmt.Sprintf 用于格式化拼接 |
strings.Split | 分割字符串 |
strings.Contains | 判断字符串是否包含子串 |
strings.HasPrefix, strings.HasSuffix | 判断字符串是否具有指定的前缀或后缀 |
strings.Index(), strings.LastIndex() | 找出子串在字符串中首次出现或最后出现的位置 |
strings.Join() | 将字符串切片连接成一个字符串(join操作) |
案例:
package main
import (
"fmt"
"strings"
)
func main() {
// 1. 使用 len(str) 求字符串长度
fmt.Println("==========1==========")
str1 := "Hello golang"
length := len(str1)
fmt.Printf("The length of '%v' is %d.\n", str1, length) // 输出:The length of 'Hello golang' is 12.
fmt.Println("The length of \"", str1, "\" is", len(str1)) // 输出:The length of " Hello golang " is 12
// 2. 使用 + 或 fmt.Sprintf 拼接字符串
fmt.Println("==========2==========")
name := "zhangsan"
age := 30
// 使用 “+”
greeting := "Hello," + name + "!"
// 使用 fmt.Sprintf
formattedGreeting := fmt.Sprintf("Hello,%v! You are %d year old .", name, age)
fmt.Println(greeting) // 输出:Hello,zhangsan!
fmt.Println(formattedGreeting) // 输出:Hello,zhangsan! You are 30 year old .
// 3. 使用 strings.Split 分割字符串
fmt.Println("=========3===========")
sentence := "The-quick-brown-fox-jumps-over-the-lazy-dog"
words := strings.Split(sentence, "-")
fmt.Println(words) // 输出The quick brown fox jumps over the lazy dog
// 4. 使用 strings.Contains 判断字符串是否包含子串
fmt.Println("==========4==========")
str2 := "Hello , my name huangjing"
if strings.Contains(str2, "huangjing") {
fmt.Println("The string contains 'huangjing'")
} else {
fmt.Println("The string 'huangjing' is not found")
}
// 5. 使用 strings.HasPrefix 和 strings.HasSuffix 判断字符串前缀和后缀
fmt.Println("==========5==========")
url := "https://example.com"
// 判读是否有前缀
if strings.HasPrefix(url, "https://") {
fmt.Println("The URL has a secure prefix")
}
// 判断是否有后缀
if strings.HasSuffix(url, ".com") {
fmt.Println("The URL has a '.com' suffix.")
}
// 6. 使用 strings.Index() 和 strings.LastIndex() 找出子串的位置
fmt.Println("==========6==========")
str3 := "Hello golang world"
index := strings.Index(str3, "world")
lastIndex := strings.LastIndex(str3, "l")
fmt.Printf("The index of 'world' is %d . \n", index) // The index of 'world' is 13 .
fmt.Printf("The last index of 'l' is %d . \n", lastIndex) // The last index of 'l' is 16 .
// 7. 使用 strings.Join() 将字符串切片连接成一个字符串
fmt.Println("==========7==========")
joinedString := strings.Join(words, "++")
fmt.Println(joinedString) //输出:The++quick++brown++fox++jumps++over++the++lazy++dog
}
4.5 byte和rune类型
组成每个字符串的元素叫做 “字符”,可以通过遍历字符串元素获得字符。字符用单引号 ‘’ 包裹起来
Go语言中的字符有以下两种类型
- uint8类型:或者叫byte型,代表了ACII码的一个字符
- rune类型:代表一个UTF-8字符
当需要处理中文,日文或者其他复合字符时,则需要用到rune类型,rune类型实际上是一个int32
Go使用了特殊的rune类型来处理Unicode,让基于Unicode的文本处理更为方便,也可以使用byte型进行默认字符串处理,性能和扩展性都有照顾。
需要注意的是,在go语言中,一个汉字占用3个字节(utf-8),一个字母占用1个字节
package main
import "fmt"
func main() {
a := 'a'
// 输出的是ASCII码值,也就是说当我们直接输出byte(字符)的时候,输出的是这个字符对应的码值
fmt.Printf("%c---%v---%T\n", a, a, a) // 输出的是: 字符 ASCII码 类型
// for循环打印字符串里面的字符
// 通过len来循环的,相当于打印的是ASCII码
s := "你好 golang"
for i := 0; i < len(s); i++ {
fmt.Printf("%v(%c)\n", s[i], s[i])
}
fmt.Println("=========================")
// 通过rune打印的是 utf-8字符
for index, v := range s {
fmt.Println(index, v)
}
}
4.5.1 修改字符串
要修改字符串,需要先将其转换成[]rune 或 []byte类型,完成后在转换成string
,无论哪种转换都会重新分配内存,并复制字节数组
转换为 []byte 类型:
// 字符串转换
str1 := "big"
// 将 str1 转换为字节切片 byteStr1
byteStr1 := []byte(str1)
byteStr1[0] = 'p'
fmt.Println(string(byteStr1)) //输出:pig
转换为rune类型:
// rune 类型
str2 := "你好golang"
// 将 str2 转换为 rune 类型的切片 byteStr2
byteStr2 := []rune(str2)
// 将 byteStr2 的第一个 Unicode 码点改为 '我'
byteStr2[0] = '我'
fmt.Println(string(byteStr2)) // 输出:我好golang
建议从低位转换成高位,这样可以避免精度丢失
完整代码:
package main
import "fmt"
func main() {
// 1.转换为 []byte 类型:
// 字符串转换
str1 := "big"
// 将str1转换成字节切片 byteStr1
byteStr1 := []byte(str1)
fmt.Println(byteStr1) // [98 105 103]
// 2.转换为rune类型:
// rune 类型
str2 := "你好golang"
// 将 str2 转换为 rune 类型的切片 byteStr2
byteStr2 := []rune(str2)
fmt.Println(byteStr2) // [20320 22909 103 111 108 97 110 103]
// 将 byteStr2 的第一个 Unicode 码点改为 '我'
byteStr2[0] = '我'
fmt.Println(string(byteStr2)) // 我好golang
}
4.6 基本数据类型转换
在 Golang 中,基本数据类型之间的转换通常是显式的,需要使用类型转换操作符来完成。以下是 Golang 中常见的基本数据类型转换介绍:
4.6.1 数值类型转换
// 整型和浮点型之间转换
var aa int8 = 20
var bb int16 = 40
fmt.Println(int16(aa) + bb)
// 建议整型转换成浮点型
var cc int8 = 20
var dd float32 = 40
fmt.Println(float32(cc) + dd)
4.6.2 转换成字符串类型
第一种方式,就是通过
fmt.Sprintf()
来转换
变量类型 | 变量值 | fmt.Sprintf 格式化动词 | 转换后的字符串值 | 字符串类型 |
---|---|---|---|---|
int | 20 | %d | 20 | string |
float64 | 12.456 | %f | 12.456000 | string |
bool | true | %t | true | string |
byte | ‘a’ | %c | a | string |
package main
import "fmt"
func main() {
// 第一种方式,就是通过 `fmt.Sprintf()`来转换
// 定义各种数据类型的变量
var i int = 20
var f float64 = 12.456
var t bool = true
var b byte = 'a'
// 使用 fmt.Sprintf 转换为字符串
str1 := fmt.Sprintf("%d", i)
fmt.Printf("类型:%v-%T \n", str1, str1) // 输出:20-string
str2 := fmt.Sprintf("%f", f)
fmt.Printf("类型:%v-%T \n", str2, str2) // 输出: 12.456000-string
str3 := fmt.Sprintf("%t", t)
fmt.Printf("类型:%v-%T \n", str3, str3) // 输出: true-string
str4 := fmt.Sprintf("%c", b)
fmt.Printf("类型:%v-%T \n", str4, str4) // 输出: a-string
}
第二种方法就是通过
strconv包
里面的集中转换方法进行转换
strconv
包是 Golang 标准库中提供的用于字符串和基本数据类型之间相互转换的工具包。它包含了许多函数,可以方便地将不同类型的数据转换为字符串,或将字符串转换为其他类型的数据。以下是 strconv
包中一些常用的函数以及它们的作用:
函数 | 作用 |
---|---|
Atoi() | 将字符串转换为整数 |
Itoa() | 将整数转换为字符串 |
ParseInt() | 将字符串转换为指定位大小和进制的整数 |
ParseFloat() | 将字符串转换为浮点数 |
FormatInt() | 将整数以指定进制转换为字符串 |
FormatFloat() | 将浮点数以指定格式转换为字符串 |
AppendInt() | 将整数追加到字节切片中 |
AppendFloat() | 将浮点数追加到字节切片中 |
ParseBool() | 将字符串转换为布尔值 |
FormatBool() | 将布尔值转换为字符串 |
Quote() | 对字符串进行转义并添加引号 |
QuoteToASCII() | 对字符串进行 ASCII 转义并添加引号 |
Unquote() | 移除字符串中的引号并取消转义 |
UnquoteChar() | 移除字符串中的引号并取消转义,返回移除的字符及下一个字符的索引 |
案例:
// int类型转换str类型
var num1 int64 = 20
str5 := strconv.FormatInt(num1, 10)
fmt.Printf("转换:%v - %T \n", str5, str5) //输出: 20 - string
// float类型转换成string类型
var num2 float64 = 3.1415926
/*
参数1:要转换的值
参数2:格式化类型 'f'表示float,'b'表示二进制,‘e’表示 十进制
参数3:表示保留的小数点,-1表示不对小数点格式化
参数4:格式化的类型,传入64位 或者 32位
*/
str6 := strconv.FormatFloat(num2, 'f', -1, 64)
fmt.Printf("转换: %v-%T \n", str6, str6) // 输出:3.1415926-string
4.6.3 字符串转换成 int 和 float 类型
// 字符串转int
str7 := "10"
/*
参数1:需要转换的值
参数2:进制
参数3:32位或64位
*/
num3, _ := strconv.ParseInt(str7, 10, 64)
fmt.Printf("转换: %v-%T\n", num3, num3) // 输出:10-int64
// 转换成float类型
str8 := "3.141592654"
num4, _ := strconv.ParseFloat(str8, 10)
fmt.Printf("转换: %v-%T\n", num4, num4) // 输出:3.141592654-float64
完整代码:
package main
import (
"fmt"
"strconv"
)
func main() {
// 第一种方式,就是通过 `fmt.Sprintf()`来转换
// 定义各种数据类型的变量
var i int = 20
var f float64 = 12.456
var t bool = true
var b byte = 'a'
// 使用 fmt.Sprintf 转换为字符串
str1 := fmt.Sprintf("%d", i)
fmt.Printf("类型:%v-%T \n", str1, str1) // 输出:20-string
str2 := fmt.Sprintf("%f", f)
fmt.Printf("类型:%v-%T \n", str2, str2) // 输出: 12.456000-string
str3 := fmt.Sprintf("%t", t)
fmt.Printf("类型:%v-%T \n", str3, str3) // 输出: true-string
str4 := fmt.Sprintf("%c", b)
fmt.Printf("类型:%v-%T \n", str4, str4) // 输出: a-string
fmt.Println("====================================================")
// 第二种方法就是通过`strconv包`里面的集中转换方法进行转换
// int类型转换str类型
var num1 int64 = 20
str5 := strconv.FormatInt(num1, 10)
fmt.Printf("转换:%v - %T \n", str5, str5) //输出: 20 - string
// float类型转换成string类型
var num2 float64 = 3.1415926
/*
参数1:要转换的值
参数2:格式化类型 'f'表示float,'b'表示二进制,‘e’表示 十进制
参数3:表示保留的小数点,-1表示不对小数点格式化
参数4:格式化的类型,传入64位 或者 32位
*/
str6 := strconv.FormatFloat(num2, 'f', -1, 64)
fmt.Printf("转换: %v-%T \n", str6, str6) // 输出:3.1415926-string
fmt.Println("====================================================")
// 字符串转int
str7 := "10"
/*
参数1:需要转换的值
参数2:进制
参数3:32位或64位
*/
num3, _ := strconv.ParseInt(str7, 10, 64)
fmt.Printf("转换: %v-%T\n", num3, num3) // 输出:10-int64
// 转换成float类型
str8 := "3.141592654"
num4, _ := strconv.ParseFloat(str8, 10)
fmt.Printf("转换: %v-%T\n", num4, num4) // 输出:3.141592654-float64
}