go学习 3、基础数据类型

3、基础数据类型

  • 基础数据类型:数字、字符串、布尔型
  • 复合类型:数组、结构体
  • 引用类型:指针、切片、字典、函数、通道
  • 接口类型

3.1 整型

有符号、无符号 int8/int16/int32/int64 uint8/uint16/uint32/units 64
Unicode字符rune类型是和int32等价的类型,通常用来表示一个Unicode码点,这两个名称可以互换使用。
byte和unit8是等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
一种无符号的整数类型unitptr,没有指定具体的bit大小但是足以容纳指针。只有在底层编程时才需要。
int 和int32也是不同的类型,即使int的大小也是32bit。需要显式类型转换。
二元运算符:算术运算、逻辑运算、比较运算(优先级顺序)
%取模运算符的符号和被取模数的符号总是一致的。仅用于整数间的运算。 -5%3=-2
数据溢出

^ //位运算XOR,作为二元运算符时是按位异或,用作一元运算符时表示按位取反
&^ //位清空,按位清零
z=x &^ y//y=1,z=0;y=0,z=x

倾向于使用有符号的int类型
八进制前缀0,十六进制前缀0x

o:=0666
fmt.Printf("%d %[1]o %#[1]o\n",o)//438 666 0666

[1]:再次使用第一个操作数
#:输出时生成前缀
字符使用%c参数打印,用%q参数打印带单引号的字符

3.2 浮点数

float32/float64
%g 打印浮点数
对应表格的数据,使用%e(带指数)或%f形式打印可能更合适
math.IsNaN用来测试一个数是否是非数NaN
math.NaN返回非数NaN对应的值

3.3 复数

complex64/complex128 real()实部 imag()虚部
math/cmplx包提供了复数处理的很多函数

3.4 布尔型

true/false

3.5 字符串

字符串是一个不可改变的字节序列。但可以给一个字符串变量分配一个新字符串值。
不变性意味着如果两个字符串共享相同的底层数据的话也是安全的。
文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列

s:="left foot"
t:=s
s+=", right foot"
s[0]='L'//编译错误

3.5.1 字符串面值

将一系列字节序列包含包含在双引号,“hello,world”
Go语言源文件总是用UTF8编码,并且Go语言的文本字符串也以UTF8编码的方式处理,因此我们可以将Unicode码点也写到字符串面值中。
在一个双引号包含的字符串面值中,可以用以反斜杠\开头的转义序列插入任意的数据。

\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
单引号(只用在‘"形式的rune符号面值中)
‘’ 双引号(只用在“…”形式的字符串面值中)
\ 反斜杠
可以通过十六进制或八进制转义在字符串面值包含任意的字节。一个十六进制的转移形式\xhh,其中两个h表示十六进制数字(大写或小写都可以)。一个八进制转义形式是\ooo,包含三个八进制的o数字(0到7),但不能超过\377(对应一个字节的范围,十进制是255)。每一个单一的字节表达一个特定的值。
一个原生的字符面值形式是’…',使用反引号代替双引号。没有转义操作,全部的内容都是字面的意思,包含退格和换行。唯一的特殊处理是会删除回车以保证在所有平台上的值都是一样的。
原生字符串面值用于编写正则表达式会很方便,因为正则表达式往往会包含很多反斜杠。
const GoUsage=’Go is a tool for managing Go source code.

Usage:
	go command [arguments]
...

3.5.2 Unicode

如何有效处理这些包含了各种语言的丰富多样的文本数据呢?
Unicode,它收集了这个世界上所有的符号系统,包括重音符号或其他变音符号,制表符和回车符,还有很多神秘的符号,每个符号都分配一个唯一的Unicode码点,Unicode码点对应Go语言中的rune整数类型。(rune是int32等价类型)
我们可以将一个符文序列表示一个int32序列。编码方式:UTF-32或UCS-4,这种方式比较简单统一,但是它会浪费很多存储空间。
大数据计算机可读的文本是ASCII字符,本来每个ASCII字符只需要8bit或1字节就能表示。常用的字符也远少于65536,16bit编码方式就能表达常用字符。

3.5.3 UTF-8

UTF8是一个将Unicode码点编码为字节序列的变长编码。UTF8编码使用1到4个字节来表示每个Unicode码点,ASCII部分字符只使用1个字节,常用字符部分使用2或3个字节表示。
每个符号编码后第一个字节的高端bit位用于表示总共有多少编码个字节。
如果第一个字节的高端bit为0,则表示对应7bit的ASCII字符,ASCII字符每个字符依然是一个字节,和传统的ASCII编码兼容。
如果第一个字节的高端bit是110,则说明需要2个字节;后续的每个高端bit都以10开头
go学习 3、基础数据类型_第1张图片
变长的编码无法直接通过索引来访问第n个字符,但有优点:

  • UTF8编码比较紧凑,完全兼容ASCII码,并且可以自动同步
  • 它可以通过向前回溯最多2个字节就能确定当前字符编码的开始字节的位置
  • 前缀编码,当从左向右解码时不会有任何歧义也并不需要向前查看。
  • 没有任何字符的编码是其他字符编码的子串,或是其他编码序列的字符,因此搜索一个字符时只要搜索它的字节编码序列即可。
  • UTF8编码的顺序和Unicode码点的顺序一致,可以直接排序UTF8编码序列。
  • 没有嵌入NUL(0)字节,可以很好兼容那些使用NUL作为字符串结尾的编程语言。

Go语言的源文件采用UTF8编码,并且Go语言处理UTF8编码的文本也很出色。unicode包提供了诸多处理rune字符相关功能的函数(区分字母和数组、字母的大写和小写转换),unicode/utf8包提供了用于rune字符序列的UTF8编码和解码的功能。
Unicode转义字符让我们可以通过Unicode码点输入特殊的字符。有两种形式:\uhhh对应16bit的码点值,\Uhhhhhhhh对应32bit的码点值,其中h是一个十六进制数组,一般很少使用32bit的形式。每一个对应码点的UTF8编码。
下面的字母串面值都表示相同的值:
在这里插入图片描述
Unicode转义也可以使用在rune字符中。下面三个字符是等价的:

在这里插入图片描述
对于小于256码点值可以写在一个十六进制转义字节中,例如\x41对应字符’A’,但对于更大码点则必须使用\u或\U转义形式。
得益于UFT8编码优良的设计,诸多字符串操作都不需要解码操作。
不用解码直接测试一个字符串是否是另一个字符串的前缀:

func HasPrefix(s,prefix string) bool{
	return len(s)>=len(prefix)&&s[:len(prefix)]==prefix
}

后缀测试:

func HasSuffix(s,suffix string) bool{
	return len(s)>=len(suffix)&&s[len(s)-len(suffix):]==suffix
}

包含子串测试:

func Contains(s,substr string) bool{
	for i:=0;i<len(s);i++{
		if HasPrefix(s[i:],substr){
			return true
		}
	}
	return false
}

对于UTF8编码后文本的处理和原始的字节处理逻辑是一样的。
内存表示形式

import "unicode/utf8"

s:="Hello, 世界"
fmt.Println(len(s))//13字节
fmt.Println(utf8.RuneCountInString(s))//9个Unicode

为了处理这些真实的字符,我们需要一个UFT8解码器。

for i := 0; i < len(s); {
	//	返回字符本身,字符采用UTF8编码后的编码字节数目
    r, size := utf8.DecodeRuneInString(s[i:])
    fmt.Printf("%d\t%c\n", i, r)
    i += size
}

go学习 3、基础数据类型_第2张图片
Go语言的range循环在处理字符串时,会自动隐式解码UFT8字符串。

for i, r := range "Hello, 世界" { 
	fmt.Printf("%d\t%q\t%d\n", i, r, r)
}

统计字符串中字符的数目:

n := 0
for _, _ = range s {
	n++ 
}
n := 0
for range s {
	n++ 
}

utf8.DecodeRuneInString解码或在range循环中隐式地解码,如果遇到一个错误的UTF8编码输入,将生成Unicode字符\uFFD
UTF8字符串作为交换格式是非常方便的,但是在程序内部采用rune序列可能更方便,rune大小一致,支持数组索引和方便切割。
将[]rune类型转换应用到UTF8编码的字符串,将返回字符串编码的Unicode码点序列:

// "program" in Japanese katakana
s := "プログラム"
//%x参数用于在每个十六进制数字前插入一个空格
fmt.Printf("% x\n", s) // "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0" r := []rune(s)
fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
//如果将一个[]rune类型的Unicode字符slice或数组转为string,则对它们进行UTF8编码
fmt.Println(string(r)) // "プログラム"
//将一个整数转型为字符串:生成以只包含对应Unicode码点字符的UTF8字符串
fmt.Println(string(65)) // "A", not "65"
fmt.Println(string(0x4eac)) // "京"

3.5.4 字符串和Byte切片

  • strings包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能
  • bytes包,针对[]byte类型提供很多类似功能的函数。字符串是只读的,逐步构建字符串会导致很多分配和复制。bytes.Buffer类型将会更有效
  • strconv包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换
  • unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等类似功能,用于字符分类。每个函数有一个单一的rune类型的参数,然后返回一个布尔值。

basename函数
basename(s) 将看起来像是系统路径的前缀删除,同时将看似文件类型的后缀名部分删除

fmt.Println(basename("a/b/c.go")) // "c"
fmt.Println(basename("c.d.go"))   // "c.d"
fmt.Println(basename("abc"))      // "abc"

basename1.go

func basename(s string) string {
    // Discard last '/' and everything before.
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] == '/' {
			s = s[i+1:]
			break
		}
	}
    // Preserve everything before last '.'.
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] == '.' {
			s = s[:i]
			break
		}
	}
	return s 
}

basename2.go

func basename(s string) string {
    slash := strings.LastIndex(s, "/") // -1 if "/" not found
    s = s[slash+1:]
    if dot := strings.LastIndex(s, "."); dot >= 0 {
		s = s[:dot] 
	}
	return s 
}

一个字节slice的元 素则可以自由地修改
字符串和字节slice之间可以相互转换:

s := "abc"
//分配了一个新的字节数组用于保存字符串数据的拷贝,然后 引用这个底层的字节数组
b := []byte(s)
//构造一个字符串拷贝,以确保s2字符串是只读的
s2 := string(b)

为了避免转换中不必要的内存分配,bytes包和strings同时提供了许多实用函数。下面是 strings包中的六个函数:

func Contains(s, substr string) bool
func Count(s, sep string) int
func Fields(s string) []string
func HasPrefix(s, prefix string) bool
func Index(s, sep string) int
func Join(a []string, sep string) string

bytes包中也对应的六个函数:

func Contains(b, subslice []byte) bool
func Count(s, sep []byte) int
func Fields(s []byte) [][]byte
func HasPrefix(s, prefix []byte) bool
func Index(s, sep []byte) int
func Join(s [][]byte, sep []byte) []byte

它们之间唯一的区别是字符串类型参数被替换成了字节slice类型的参数。
bytes包还提供了Buffer类型用于字节slice的缓存。一个Buffer开始是空的,但是随着string、 byte或[]byte等类型数据的写入可以动态增长,一个bytes.Buffer变量并不需要初始化,因为零值也是有效的。

3.5.5 字符串和数字的转换

由strconv包提供字符串和数值之间的转换
将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa(“整数到ASCII”):

x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"

FormatInt和FormatUint函数可以用不同的进制来格式化数字:

fmt.Println(strconv.FormatInt(int64(x), 2)) // "1111011"

fmt.Printf函数的%b、%d、%o和%x等参数提供功能往往比strconv包的Format函数方便很 多,特别是在需要包含附加额外信息的时候:

s := fmt.Sprintf("x=%b", x) // "x=1111011"

如果要将一个字符串解析为整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无 符号整数的ParseUint函数:

x, err := strconv.Atoi("123")             // x is an int
//第三个参数是用于指定整型数的大小;例如16表示int16,0则表示int。
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits

fmt.Scanf来解析输入的字符串和数字,特别是当字符串和数字混合在一行的 时候,它可以灵活处理不完整或不规则的输入。

3.6 常量

常量表达式的值在编译器计算,而不是在运行期

 const pi = 3.14159 // approximately; math.Pi is a better approximation

批量声明多个

const (
	e = 2.71828182845904523536028747135266249775724709369995957496696763 
	pi = 3.14159265358979323846264338327950288419716939937510582097494459
)

常量可以是构成类型的一部分,例如用于指定数组类型的长度:

const IPv4Len = 4
// parseIPv4 parses an IPv4 address (d.d.d.d).
func parseIPv4(s string) IP {
    var p [IPv4Len]byte
    // ...
}

一个常量的声明也可以包含一个类型和一个值,但是如果没有显式指明类型,那么将从右边的表达式推断类型。

const noDelay time.Duration = 0
const timeout = 5 * time.Minute
fmt.Printf("%T %[1]v\n", noDelay)     // "time.Duration 0"
fmt.Printf("%T %[1]v\n", timeout)     // "time.Duration 5m0s"
fmt.Printf("%T %[1]v\n", time.Minute) // "time.Duration 1m0s"

如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的

const ( 
	a=1
	b 
	c=2 
	d
)
fmt.Println(a, b, c, d) // "1 1 2 2"

3.6.1 iota常量生成器

常量声明可以使用iota常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不 用每行都写一遍初始化表达式。在一个const声明语句中,在第一个声明的常量所在的行, iota将会被置为0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota//0
    Monday //1
    Tuesday //2
    Wednesday
    Thursday
    Friday
    Saturday
)

例子:给一个无符号整数的最低5bit的每个bit指定一个名字:

type Flags uint
const (
    FlagUp Flags = 1 << iota // is up
	FlagBroadcast// supports broadcast access capability
	FlagLoopback// is a loopback interface
	FlagPointToPoint// belongs to a point-to-point link
	FlagMulticast// supports multicast access capability
)

随着iota的递增,每个常量对应表达式1 << iota,是连续的2的幂,分别对应一个bit位置
iota局限性:它并不能用于产生1000的幂(KB、MB等),因 为Go语言并没有计算幂的运算符。

3.6.2 无类型常量

六种未明确类型的常量类型:无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串
通过延迟明确常量的具体类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接 用于更多的表达式而不需要显式的类型转换。
只有常量可以是无类型的。

你可能感兴趣的:(go语言学习,golang,学习)