目录
一、结构体
1 - 结构体变量定义和初始化
- 结构体类型:
- 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐,数据不便于管理
- 结构体是一种聚合的数据类型,它是由一系列具有相同类型或不同类型的数据构成的数据集合。每个数据称为结构体的成员
- 普通变量定义和初始化
- 顺序初始化:依次将结构体内部所欲成员初始化
- 指定成员初始化:
man := Person{name:"rose", age:18}
----未初始化的成员变量,取该数据类型对应的默认值
type Person struct {
name string
sex byte
age int
}
func main() {
// 1. 顺序初始化, 必须全部初始化完整
var man Person = Person{"andy", 'm', 18}
fmt.Println("man:", man)
// 2. 部分初始化
man2 := Person{sex: 'f', age: 19}
fmt.Println("man2:", man2)
}
2 - 结构体赋值、比较和传参
- 普通变量的赋值和使用:使用“.”索引成员变量
var man3 Person
man3.name = "mike"
man3.sex = 'm'
man3.age = 99
fmt.Println("man3:", man3)
man3.age = 1073
fmt.Println("man3:", man3)
- 结构体变量的比较:
- 比较:只能使用 == 和 != 不能 > < >= <=…
- 相同结构体类型(成员变量的类型、个数、顺序一致)变量之间可以直接赋值
// 结构体比较
var p1 Person = Person{"andy", 'm', 18}
var p2 Person = Person{"andy", 'm', 18}
var p3 Person = Person{"andy", 'm', 181}
fmt.Println("p1 == p2 ?", p1 == p2) // true
fmt.Println("p1 == p3 ?", p1 == p3) // false
// 相同类型结构体赋值
var tmp Person
fmt.Println("tmp", tmp)
tmp = p3
fmt.Println("tmp", tmp)
- 结构体地址:结构体变量的地址 == 结构体首个元素的地址
- unsafe.Sizeof(变量名):此种类型的变量所占用的内存空间大小
- 结构体传参:将结构体变量的值拷贝一份,传递。 —— 几乎不用。 内存消耗大,效率低
type Person struct {
name string
sex byte
age int
}
func test(man Person) {
fmt.Println("test temp size:", unsafe.Sizeof(man)) // test temp size: 32
man.name = "name1"
man.age = 33
fmt.Println("test: man", man) // test: man {name1 0 33}
}
func main() {
// 函数内部使用结构体传参
var temp Person
fmt.Println("main temp size:", unsafe.Sizeof(temp)) // main temp size: 32
test(temp) // 值传递,将实参的值拷贝一份给形参
fmt.Println("temp", temp) // temp { 0 0}
// 结构体变量的地址 = 结构体首个元素的地址
fmt.Printf("&tmp = %p\n", &temp)
fmt.Printf("&temp.name = %p\n", &(temp.name))
}
3 - 结构体指针
- 结构体指针变量定义和初始化
- 顺序初始化:依次将结构体内部所欲成员初始化
var man *Person = &Person{"andy", 'm', 20}
- 使用指针索引到已有的对象地址:
var p2 *Person2 p2 = &tmp
new(Person)
:p := new(Person) p.name = “name” p.age = 10
- 顺序初始化:依次将结构体内部所欲成员初始化
- 指针索引成员变变量:使用“.”索引成员变量
- 结构体指针地址:结构体指针变量的值 == 结构体首个元素的地址
- 结构体指针传参
- unSafe.Sizeof(指针) : 不管何种类型的指针,在 64位操作系统下,大小一致。均为 8 字节!!!
- 结构体变量地址值传递(传引用)。 —— 使用频率非常高!!!
package main
import (
"fmt"
"unsafe"
)
type Person2 struct {
name string
sex byte
age int
}
func test2(p *Person2) {
fmt.Println("test2:", unsafe.Sizeof(p)) // test2: 8
p.name = "Luffy"
p.age = 779
p.sex = 'm'
}
func main() {
var p1 *Person2 = &Person2{"n1", 'f', 19}
fmt.Println("p1", p1)
var tmp Person2 = Person2{"n1", 'f', 19}
var p2 *Person2
p2 = &tmp
fmt.Println("p2", p2)
p3 := new(Person2)
p3.name = "n3"
p3.age = 22
p3.sex = 'f'
fmt.Println("p3:", p3)
fmt.Printf("p3 = %p\n",p3) // p3 = 0xc000122440
fmt.Printf("&p3.name = %p\n",&p3.name) // &p3.name = 0xc000122440
fmt.Printf("p3, type= %T\n", p3) // p3, type= *main.Person2
test2(p3)
fmt.Println("p3:", p3)
fmt.Println("main:", unsafe.Sizeof(p3)) // main: 8
}
- 通过函数参数初始化结构体
type Person3 struct {
name string
age int
flg bool
intereset []string
}
// 通过函数参数初始化结构体
func initFunc(p *Person3) {
p.name = "Nami"
p.age = 18
p.flg = true
p.intereset = append(p.intereset, "shopping")
p.intereset = append(p.intereset, "sleeping")
}
func main() {
var person Person3
initFunc(&person)
fmt.Println("person:", person) // person: {Nami 18 true [shopping sleeping]}
}
- 通过函数返回值初始化结构体
- 不能返回局部变量的地址,可以返回局部变量的值
- 局部变量保存栈帧上,函数调用结束后,栈帧释放。局部变量的地址,不再受系统保护,随时可能分配给其他程序
注意:
return p
返回的是局部变量的值,return &p
才是返回局部变量的地址
type Person3 struct {
name string
age int
flg bool
intereset []string
}
// 通过函数返回值,初始化结构体
func initFunc2() *Person3 {
p := new(Person3)
p.name = "Nami"
p.age = 18
p.flg = true
p.intereset = append(p.intereset, "shopping")
p.intereset = append(p.intereset, "sleeping")
return p // 返回指针变量的值 —— heap的地址
}
func main() {
p2 := initFunc2()
fmt.Println("p2", p2) // p2 &{Nami 18 true [shopping sleeping]}
}
二、字符串处理函数
- Split:字符串按指定分割符拆分
ret := strings.Split(str, " I")
- Fields:字符串按空格拆分
ret = strings.Fields(str)
- HasSuffix:判断字符串结束标记
flg := strings.HasSuffix("test.abc", ".mp3")
- HasPrefix:判断字符串起始标记
flg := strings.HasPrefix("test.abc", "tes.")
func main() {
str := "I love my work and I love my family too"
// 字符串按指定分割符拆分
ret := strings.Split(str, " I")
for _, s := range ret {
fmt.Println(s)
}
// 字符串按空格拆分
ret = strings.Fields(str)
for _, s := range ret {
fmt.Println(s)
}
// 判断字符串结束标记
flg := strings.HasSuffix("test.abc", ".mp3")
fmt.Println(flg) // false
// 判断字符串起始标记
flg = strings.HasPrefix("test.abc", "tes.")
fmt.Println(flg) // false
}
三、文件操作强化
1 - 创建文件和打开文件
func Create(name string) (*File, error)
:根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的- 文件不存在创建, 文件存在,将文件内容清空
func main() {
f, err := os.Create("E:/Test/testFile.xyz")
if err != nil {
fmt.Println("create err:", err)
return
}
defer f.Close()
fmt.Println("successful")
}
func Open(name string) (*File, error)
:Open()是以只读权限打开文件名为name的文件,得到的文件指针file,只能用来对文件进行“读”操作- 文件不存在,打开失败
func main() {
f, err := os.Open("E:/Test/testFile.xyz")
if err != nil {
fmt.Println("open err:", err)
return
}
defer f.Close()
_, err = f.WriteString("######")
if err != nil {
fmt.Println("WriteString err:", err)
return
}
fmt.Println("successful")
}
func OpenFile(name string, flag int, perm FileMode) (*File, error)
:OpenFile()可以选择打开name文件的读写权限- 参1:name, 打开文件的路径: 绝对路径、相对路径
- 参2:打开文件权限: O_RDONLY、O_WRONLY、O_RDWR
- 参3:一般传6
func main() {
f, err := os.OpenFile("E:/Test/testFile.xyz", os.O_RDWR, 6)
if err != nil {
fmt.Println("OpenFile err:", err)
return
}
defer f.Close()
_, err = f.WriteString("######")
if err != nil {
fmt.Println("WriteString err:", err)
return
}
fmt.Println("successful")
}
2 - 写文件操作
- 按字符串写
n, err := f.WriteString("123")
:WriteString --> n个写入的字符个数 - 按位置写
off, _ := f.Seek(-5, io.SeekEnd)
:Seek -> 修改文件的读写指针位置- 参1: 偏移量。 正:向文件尾偏, 负:向文件头偏
- 参2: 偏移起始位置
- io.SeekStart: 文件起始位置
- io.SeekCurrent: 文件当前位置
- io.SeekEnd: 文件结尾位置
- 返回值:表示从文件起始位置,到当前文件读写指针位置的偏移量
- 按字节写
n, _ = f.WriteAt([]byte("1111"), off)
:writeAt -> 在文件制定偏移位置,写入 []byte , 通常搭配 Seek()- 参1: 待写入的数据
- 参2:偏移量
- 返回:实际写出的字节数
func main() {
f, err := os.OpenFile("E:/Test/testFile.xyz", os.O_RDWR, 6)
if err != nil {
fmt.Println("OpenFile err:", err)
return
}
defer f.Close()
fmt.Println("successful")
n, err := f.WriteString("helloworld\r\n")
if err != nil {
fmt.Println("WriteString err:", err)
return
}
fmt.Println("WriteString n = ", n)
off, _ := f.Seek(-5, io.SeekEnd)
fmt.Println("off:", off)
n, _ = f.WriteAt([]byte("1111"), off)
fmt.Println("WriteAt n :", n)
}
3 - 读文件操作
- 按行读:
- ①.创建一个带有缓冲区的Reader(读写器)
reader : = bufio.NewReader(打开的文件指针)
- ②.从reader的缓冲区中,读取指定长度的数据
buf, err := reader.ReadBytes( ' \n' )
- ①.创建一个带有缓冲区的Reader(读写器)
func main() {
f, err := os.OpenFile("C:/itcast/testFile.xyz", os.O_RDWR, 6)
if err != nil {
fmt.Println("OpenFile err:", err)
return
}
defer f.Close()
fmt.Println("successful")
// 创建一个带有缓冲区(用户缓冲)的 reader
reader := bufio.NewReader(f)
for {
buf, err := reader.ReadBytes('\n') // 读一行数据
if err != nil && err == io.EOF {
fmt.Println("文件读取完毕")
return
} else if err != nil {
fmt.Println("ReadBytes err:", err)
}
fmt.Print(string(buf))
}
}
- 按字节读文件:read([]byte)
- 按字节写文件:write([]byte)
- 案例:大文件拷贝
func main() {
// 打开读文件
f_r, err := os.Open("E:/Test/01-复习.avi")
if err != nil {
fmt.Println("Open err: ", err)
return
}
defer f_r.Close()
// 创建写文件
f_w, err := os.Create("E:/Test/test.avi")
if err != nil {
fmt.Println("Create err: ", err)
return
}
defer f_w.Close()
// 从读文件中获取数据,放到缓冲区中。
buf := make([]byte, 4096)
// 循环从读文件中,获取数据,“原封不动的”写到写文件中。
for {
n, err := f_r.Read(buf)
if err != nil && err == io.EOF {
fmt.Printf("读完。n = %d\n", n)
return
}
f_w.Write(buf[:n]) // 读多少,写多少
}
}
四、目录操作
1 - 目录操作函数
- 打开目录OpenFile:以只读方式打开目录
- 参1:name, 打开目录的路径: 绝对路径、相对路径
- 参2:flg,表示打开文件的读写模式。可选择 -> O_RDONLY只读模式、O_WRONLY只写模式、O_RDWR读写模式
- 参3:os.ModeDir
- 返回值: 返回一个可以读目录的文件指针
- 读目录Readdir:
func (f *File) Readdir(n int) ([]FileInfo, error)
- 参数:欲打开的目录项个数。通常传-1,表读取目录所有文件对象
- 返回值:FileInfo类型的切片。其内部保存了文件名。error中保存错误信息
- 得到 FileInfo类型切片后,我们可以range遍历切片元素,使用.Name()获取文件名。使用.Size()获取文件大小,使用.IsDir()判断文件是目录还是非目录文件
package main
import (
"fmt"
"os"
)
func main() {
// 获取用户输入的目录路径
fmt.Println("请输入待查询的目录:")
var path string
fmt.Scan(&path)
// 打开目录
f, err := os.OpenFile(path, os.O_WRONLY, os.ModeDir)
if err != nil {
fmt.Println("OpenFile err: ", err)
return
}
defer f.Close()
// 读取目录项
info, err := f.Readdir(-1) // -1: 读取目录中所有目录项
if err != nil {
fmt.Println("Readdir err: ", err)
return
}
// 变量返回的切片
for _, fileInfo := range info {
if fileInfo.IsDir() { // 是目录
fmt.Println(fileInfo.Name(), " 是一个目录")
} else {
fmt.Println(fileInfo.Name(), " 是一个文件")
}
}
}
2 - 目录文件操作案例
- 案例:从用户给出的目录中,找出所有的 .jpg 文件
func main() {
// 获取用户输入的目录路径
fmt.Println("请输入待查询的目录:")
var path string
fmt.Scan(&path)
// 打开目录
f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
if err != nil {
fmt.Println("OpenFile err: ", err)
return
}
defer f.Close()
// 读取目录项
info, err := f.Readdir(-1) // -1: 读取目录中所有目录项
if err != nil {
fmt.Println("Readdir err: ", err)
return
}
// 变量返回的切片
for _, fileInfo := range info {
if !fileInfo.IsDir() { // 文件
if strings.HasSuffix(fileInfo.Name(), ".mp3") {
fmt.Println("jpg 文件有:", fileInfo.Name())
}
}
}
}
- 案例:从用户给出的目录中,拷贝 .mp3文件到指定目录中
package main
import (
"fmt"
"io"
"os"
"strings"
)
// 拷贝mp3 文件到指定目录的操作
func cpMP32Dir(src, dst string) {
//fmt.Println("src:", src)
//fmt.Println("dst:", dst)
//打开读文件
f_r, err := os.Open(src)
if err != nil {
fmt.Println("Open err: ", err)
return
}
defer f_r.Close()
// 创建写文件
f_w, err := os.Create(dst)
if err != nil {
fmt.Println("Create err: ", err)
return
}
defer f_w.Close()
// 从读文件中获取数据,放到缓冲区中。
buf := make([]byte, 4096)
// 循环从读文件中,获取数据,“原封不动的”写到写文件中。
for {
n, err := f_r.Read(buf)
if err != nil && err == io.EOF {
fmt.Printf("读完。n = %d\n", n)
return
}
f_w.Write(buf[:n]) // 读多少,写多少
}
}
func main() {
// 获取用户输入的目录路径
fmt.Println("请输入待查询的目录:")
var path string
fmt.Scan(&path)
// 打开目录
f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
if err != nil {
fmt.Println("OpenFile err: ", err)
return
}
defer f.Close()
// 读取目录项
info, err := f.Readdir(-1) // -1: 读取目录中所有目录项
if err != nil {
fmt.Println("Readdir err: ", err)
return
}
// 变量返回的切片
for _, fileInfo := range info {
if !fileInfo.IsDir() { // 文件
if strings.HasSuffix(fileInfo.Name(), ".mp3") {
cpMP32Dir(path+"/"+fileInfo.Name(), "./"+fileInfo.Name())
}
}
}
}
- 案例:统计指定目录下,所有.txt文件中,“Love”这个单词 出现的次数
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
// 从一个文件中逐行读取内容,统计该文件共有多少个 love
func readFile(fileName string) int {
fp, err := os.Open(fileName)
if err != nil {
fmt.Println("Open err:", err)
return -1
}
defer fp.Close()
row := bufio.NewReader(fp) // 创建一个reader
var total int = 0 // 统计 Love 总数的变量
for { // 循环 按行读取文件内容,存入buf中
buf, err := row.ReadBytes('\n')
if err != nil && err == io.EOF {
break
}
// 交给 wordCount 统计每行中 love 出现的次数。累加各行 love 数
total += wordCount(string(buf[:]))
}
return total
}
// 从一行字符串中获取 love 出现的次数。将该次数返回。
func wordCount(s string) int {
arr := strings.Fields(s) // 分割字符串,存入字符数组
m := make(map[string]int) // 创建map
// 对arr中的每个单词进行循环,存入map中,统计
for i := 0; i < len(arr); i++ {
m[arr[i]]++
}
// 统计 map 中一共有多少个 "Love"
for k, v := range m {
if k == "love" {
fmt.Printf("%s : %d\n", k, v)
return m[k] // 返回 Love 出现的次数
}
}
return 0 // 没有Love, 返回 0
}
func main() {
/* // 测试从文件读一行
oneFilenum := readFile("C:/itcast/test2/test.txt")
fmt.Printf("一个文件中有:%d 个Love\n", oneFilenum)
writeString
*/
fmt.Println("请输入要找寻的目录:")
var path string
fmt.Scan(&path) // 获取用户指定的目录名
dir, _ := os.OpenFile(path, os.O_RDONLY, os.ModeDir) // 只读打开该目录
defer dir.Close()
names, _ := dir.Readdir(-1) // 读取当前目录下所有的文件名和目录名,存入names切片
var AllLove int = 0
for _, name := range names { // 遍历切片,获取文件/目录名
if !name.IsDir() {
s := name.Name() // 文件名不包含路径。
if strings.HasSuffix(s, ".txt") {
AllLove += readFile(path + s) // 拼接有路径的文件名(绝对路径)
}
}
}
fmt.Printf("目录所有文件中共有 %d 个Love\n", AllLove)
}