嘘~ 正在从服务器偷取页面 . . . 可尝试再次刷新 . . .

Go语言中的数据类型


4. Golang的数据类型

概述

Go 语言中数据类型分为:基本数据类型复合数据类型基本数据类型有:

整型、浮点型、布尔型、字符串

复合数据类型有:

数组、切片、结构体、函数、map、通道(channel)、接口等。

image-20240719104317351

image-20240719104411707

4.1 整型

整型的类型有很多中,包括 int8,int16,int32,int64。我们可以根据具体的情况来进行定义

如果我们直接写 int也是可以的,它在不同的操作系统中,int的大小是不一样的

  • 32位操作系统:int -> int32
  • 64位操作系统:int -> int64

image-20200719084018801

可以通过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

image-20240511105506362

创建 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(`这是单引号 ' 和双引号 " 的使用`)

}

输出结果如下:

image-20240511153716429

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 格式化动词转换后的字符串值字符串类型
int20%d20string
float6412.456%f12.456000string
booltrue%ttruestring
byte‘a’%castring
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
}

文章作者: 無以菱
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 無以菱 !
评论
  目录