文件 & IO 操作

文件 & IO操作

文件操作

读取文件

相对路径读取

多次遍历读取

os.open:针对磁盘

相对路径:相对运行程序的路径

func main() {
	path := "user.txt"
	file, err := os.Open(path) // 返回指针和错误信息
	fmt.Println(err)           // nil
	fmt.Println(file)          // &{0xc00014e780}
	fmt.Printf("%T", file)     // *os.File

	if err != nil {
		fmt.Println(err)
	} else {
		var bytes []byte = make([]byte, 20) // 长度为20的字节切片

		for {
			n, err := file.Read(bytes) // 返回长度和错误,但是最多读取20个元素,相当于读取文件后将切片填充
			if err == io.EOF {         // 当读取完毕,返回EOF
				break // 读取完毕,退出循环
			} else if err != nil {     // 如果返回不为空,但是报其他错误
				fmt.Println(err)   // 将错误结果输出
			} else {
				fmt.Println(string(bytes[:n])) // 反之没有读完,继续读取,但一行只读取20个字符
			}
		}
		file.Close()                       // 关闭文件
	}

}
  • 一次性读取完
  • ioutil
func main() {
	file, err := os.Open("user.txt")
	if err == nil {
		bytes, err := ioutil.ReadAll(file) // 所有内容一次性读出,返回字节切片
		fmt.Println(string(bytes), err)    // 将字节切片转换为字符串
		file.Close()
	}
}
  • ioutil写入
func main() {
	err := ioutil.WriteFile("abc.txt", []byte("xxxxxxxxxxxxxx"), os.ModePerm)
	fmt.Println(err)
}

写入文件

两种写入方式:

  1. 字节切片形式写入,存储还是字符串
  2. WriteString,存储为字符串
// 写入文件,先清除再写入
func main() {
	path := "user1.txt"
	file, err := os.Create(path)           // 创建文件,返回指针和错误
	if err != nil {
		fmt.Println(err)
	} else {
		file.Write([]byte("abcdefj")) // 字节切片方式写入,最后存储还是字符串
		file.WriteString("abc123")    // 字符串方式写入,返回int和error
		file.Close()
	}
}

文件追加

func main() {
	path := "user.log"
	// os.O_APPEND: 追加写入
	// os.O_CREATE: 文件不存在就创建
	file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE, os.ModePerm)
	if err == nil {
    // 模拟将当前时间(int64转换为字符串)写入文件
		file.WriteString(strconv.FormatInt(time.Now().Unix(), 10))
		file.WriteString("\n")
		file.Close()
	}
}
  • 模拟日志信息写入文件
func main() {
	path := "user.log"
	// os.O_APPEND: 追加写入
	// os.O_CREATE: 文件不存在就创建
	file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE, os.ModePerm)
	if err == nil {
		log.SetOutput(file)         // 设置日志输出到文件
		log.Print(time.Now().Unix())
		file.Close()
	}
}

文件指针(少用)

func main() {
	file, _ := os.Open("user.txt")

	// 偏移量,相对位置
	// 文件开始 0 
	// 当前位置 1
	// 文件末尾 2 
	file.Seek(3, 0)
	bytes := make([]byte, 100)
    // 返回int64和err
	n, err := file.Read(bytes)

	fmt.Println(n, err, string(bytes[:n]))
	
	file.Close()
}

获取文件信息

func main() {
	//os.Rename("abc.txt", "ddd.txt")	        // 重命名文件
	//os.Remove("ddd.txt")				// 删除文件
	//os.Mkdir("ddd", 0644)				// 创建目录权限为0644
	//os.MkdirAll("tesst01/xxx", 0644)              // 在不存在的test01目录下创建xxx目录
	//os.RemoveAll("tesst01")		        // 目录中有文件也一起删除

	// 判断文件是否存在
	// 如果不存在,则提示文件不存在,否则关闭文件
	//file, err := os.Open("llc.txt")
	//if err != nil {
	//	if os.IsNotExist(err) {
	//		fmt.Println("文件不存在")
	//	}
	//} else {
	//	file.Close()
	//}

	for _, path := range []string{"user.log", "user.txt", "ddd"} {
		info, err := os.Stat(path)
		if err != nil {
			if os.IsNotExist(err) {
				fmt.Println("文件不存在")
			}
		} else {
			fmt.Println(strings.Repeat("*", 20))
			fmt.Println(info.Name())    // 获取文件名称
			fmt.Println(info.IsDir())   // 是否是目录
			fmt.Println(info.Size())    // 文件大小
			fmt.Println(info.ModTime()) // 修改时间

			if info.IsDir() {
				dirfile, err := os.Open(path)
				if err != nil {
					defer dirfile.Close()

					//childrens, _ := dirfile.Readdir(-1) // 获取所有信息,-1 为获取所有参数
					//for _, children := range childrens {
					//	fmt.Println(children.Name(), children.IsDir(), children.Size(), children.ModTime())
					//}

					names, _ := dirfile.Readdirnames(-1) // 获取文件夹信息
					for _, name := range names {
						fmt.Println(name)
					}

				}
			}
		}
	}

}

获取文件路径

func main() {
	fmt.Println(os.TempDir())
	fmt.Println(os.UserCacheDir())
	fmt.Println(os.UserHomeDir())         // 获取用户主目录

	path, _ := filepath.Abs(os.Args[0])
	dirpath := filepath.Dir(path)

	fmt.Println(filepath.Abs("."))        // 获取运行当前目录
	fmt.Println(filepath.Abs(os.Args[0])) // 获取运行程序绝对路径
	fmt.Println(filepath.Base(path))      // 获取运行程序名
	fmt.Println(filepath.Dir(path))       // 获取运行程序目录
	fmt.Println(filepath.Ext(path))       // 获取文件后缀

	inipath := dirpath + "/conf/ip.ini" // 路径拼接
	fmt.Println(inipath)
	fmt.Println(filepath.Join(dirpath, "config", "ip.init")) // 推荐路径拼接方式
	fmt.Println(filepath.Glob("./*.ext"))                    // 找当前目录下所有.exe结尾的文件

	filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error {
		fmt.Println(path, fileInfo.Name())               // 遍历一个目录下所有文件
		return nil
	})

}

标准输入输出

func main() {
	// os.Stdout
	fmt.Println("xxx")

	// os.Stdin
	bytes := make([]byte, 5)
	n, err := os.Stdin.Read(bytes) // 获取命令行输入
	fmt.Println(n, err, bytes)

	// 将输入信息放入buffer中打印
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	fmt.Println(scanner.Text())

	// 传递文件输出对象
	fmt.Fprintf(os.Stdout, "%", 1)
}

带缓冲的io

bufio带缓冲io读

按行读取文件,for遍历

bufio好处:

将文件读取到内存中作处理,并不会直接对函数或者方法做处理

func main() {
	file, err := os.Open("user.txt")
	if err == nil {
		defer file.Close()                       // 延迟函数,放在代码最后执行

		i := 0
		scanner := bufio.NewScanner(file)       // 创建对象
		for scanner.Scan() {                    // 将buffer中的信息一行行扫描
			fmt.Println(i, scanner.Text())	// 获取数据
			i++
		}

	}
}

/*
0 my name is tcy
1 dfsakjdfasjdfsdls
2 my name is tcymy name is
3 tcymy name is tcymy name is tcymy name is tcy
4 aaaa
5 bbbbb
6 ddddd
7 cccc

*/

按行读取文件,for遍历

func main() {
	file, err := os.Open("user.txt")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)

		for {
			// 返回字节切片,布尔类型,错误信息
			line, isPrefix, err := reader.ReadLine()
			if err != nil {
				if err != io.EOF {
					fmt.Println(err)
				}
				break
			} else {
				fmt.Println(isPrefix, err, string(line))
			}
		} 
	}
}

/*
false <nil> my name is tcy
false <nil> dfsakjdfasjdfsdls
false <nil> my name is tcymy name is
false <nil> tcymy name is tcymy name is tcymy name is tcy
false <nil> aaaa
false <nil> bbbbb
false <nil> ddddd
false <nil> cccc

*/

指定切割符号遍历

func main() {
	file, err := os.Open("user.txt")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)

		for {
			// 返回字节切片,错误信息,指定\n进行切割
			//line, err := reader.ReadSlice('\n')

			// 同样的切割,但针对字符串,而不是和上面一样使用字节切片,所以后面也可以直接打印
			line, err := reader.ReadString('\n')

			if err != nil {
				if err != io.EOF {
					fmt.Println(err)
				}
				break
			} else {
				//fmt.Println(err, string(line))
				fmt.Println(err, line)
			}
		}
	}
}

/*
<nil> my name is tcy

<nil> dfsakjdfasjdfsdls

<nil> my name is tcymy name is

<nil> tcymy name is tcymy name is tcymy name is tcy

<nil> aaaa

<nil> bbbbb

<nil> ddddd


*/

bufio带缓冲写

func main() {
	file, err := os.Create("user.txt")
	if err == nil {
		defer file.Close()

		write := bufio.NewWriter(file)
		write.WriteString("abcdef")
		write.Write([]byte("123455"))
		write.Flush() // 内存中的操作刷新到磁盘
	}
}

/*
abcdef123455
*/

持久化

CSV

特点:

csv就是excel表格文件

存储都是每行用逗号分隔

  • 存入
func main() {
	file, err := os.Create("user.csv")
	if err == nil {
		defer file.Close()

		write := csv.NewWriter(file)
		write.Write([]string{"编号", "姓名", "电话"})
		write.Write([]string{"1", "tcy", "110"})
		write.Write([]string{"2", "twg", "188"})
		write.Flush()

	}
}
  • 读取
func main() {
	file, err := os.Open("user.csv")
	if err == nil {
		defer file.Close()

		reader := csv.NewReader(file)
		for {
			line, err := reader.Read()
			if err != nil {
				if err != io.EOF {
					fmt.Println(err)
				}
				break
			} else {
				fmt.Println(line)
			}
		}
	}

}

编码解码gob

使用gob进行编码解码,是go内置的,其他语言不支持

  • 编码,存储为字符串类型映射
func main() {
	// 编码
	user := map[int]string{1: "汤采玉", 2: "汤文广", 3: "尹金秀"}

	file, err := os.Create("user03.txt")

	if err == nil {
		defer file.Close()

		encoder := gob.NewEncoder(file) // 创建对象,将新建文件传入
		encoder.Encode(user)            // 将映射信息编码存入
	}
}
  • 编码,存储为结构体类型映射
//编码
	user := map[int]User{
		1: User{1, "tcy", time.Now()},
		2: User{2, "汤文广", time.Now()},
		3: {3, "汤国华", time.Now()},
	}

	file, err := os.Create("user03.txt")
	
	if err == nil {
		defer file.Close()
	
		encoder := gob.NewEncoder(file) // 创建对象,将新建文件传入
		encoder.Encode(user)            // 将映射信息编码存入
	}
  • 解码,查看字符串类型映射
func main() {	
	// 解码查看
	users := map[int]string{}

	file, err := os.Open("user03.txt")
	if err == nil {
		defer file.Close()

		decoder := gob.NewDecoder(file)
		decoder.Decode(&users)
		fmt.Println(users)		// map[1:汤采玉 2:汤文广 3:尹金秀]
	}

}
  • 解码,查看结构体类型映射
	//解码查看
	users := map[int]User{}

	file, err := os.Open("user03.txt")
	if err == nil {
		defer file.Close()

		decoder := gob.NewDecoder(file)
		decoder.Decode(&users)
		fmt.Println(users)             // map[1:汤采玉 2:汤文广 3:尹金秀]
	}

strings/bytes

strings

场景:

当网络中传输过来的数据,放入到内存中,分批次处理,而不是直接在硬盘中处理。

针对文件,io操作上

  • string.Reader 读
func main() {
    // 创建对象
	reader := strings.NewReader("abcdefgijk")

	bytes := make([]byte, 3)

	for {
    // 每次读指定长度
		n, err := reader.Read(bytes)
		if err != nil {
			if err != io.EOF {
				fmt.Println(err)
			}
			break
		} else {
			fmt.Println(n, bytes[:n]) // 每次读取三个,每次打印三个元素
		}
	}

}
  • string.Builder 写
  • 不用每次对数据新增就新加一个变量,主要用来拼装字符串,主要是用切片做存储。可以用来提高字符串的操作速度。
// string.Builder
	var builder strings.Builder
	builder.Write([]byte("abc"))
	builder.WriteString("abcdefg!@#")
	fmt.Println(builder.String()) // abcabcdefg!@#

buffer

  • 在内存中对字符串做操作
buffer := bytes.NewBufferString("badjdfj")
	buffer.Write([]byte("123"))
	buffer.WriteString("!@#")
	fmt.Println(buffer.String())       // badjdfj123!@#

	bytes := make([]byte, 2)
	buffer.Read(bytes)
	fmt.Println(string(bytes))         // ba 因为每次就读2个字节

	line, _ := buffer.ReadString('!') // 按照!作为分隔符打印
	fmt.Println(line)                 // djdfj123!

案例

copyfile

命令行参数写入源文件和目标文件,将源文件内容拷贝至目标文件,目标文件不存在则创建。

func copyfile(src, dest string) {
	srcfile, err := os.Open(src)
	if err != nil {
		fmt.Println(err)
	} else {
		defer srcfile.Close()
		destfile, err := os.Create(dest)
		if err != nil {
			fmt.Println(err)
		} else {
			defer destfile.Close()

			bytes := make([]byte, 1024*1024*10) // 初始化字节切片,单次容量为10m

			for {
				n, err := srcfile.Read(bytes)
				if err != nil {
					if err != io.EOF {
						fmt.Println(err)
					}
					break
				}
				destfile.Write(bytes[:n])
			}
		}
	}
}

func main() {
	// 命令行传参,-s参数,默认值,描述信息,返回指针类型
	src := flag.String("s", "", "src file")
	dest := flag.String("d", "", "dest file")
	help := flag.Bool("h", false, "help")

	flag.Usage = func() {
		fmt.Println(`
Usage: copyfile -s srcfile -d destfile
Option:
			`)
		flag.PrintDefaults() // 将所有参数帮助信息输出
	}

	// 解析命令行
	flag.Parse()

	if *help || *src == "" || *dest == "" {
		flag.Usage()
	} else {
		copyfile(*src, *dest)
	}

	//fmt.Printf("%v %v %v\n", *src, *dest, *help)

}

md5计算文件或字符

func md5file(path string) string {
	file, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
	} else {
		defer file.Close()
		// 为了防止文件过大,使用批量读取文件的方式进行计算
		bytes := make([]byte, 1024*1024*10)

		hasher := md5.New()

		for {
			n, err := file.Read(bytes)
			if err != nil {
				if err != io.EOF {
					fmt.Println(err)
				}
				break
			} else {
				hasher.Write(bytes[:n])
			}
		}
		return fmt.Sprintf("%x", hasher.Sum(nil))
	}
	return ""
}

func md5string(txt string) string {
	return fmt.Sprintf("%X", md5.Sum([]byte(txt)))
}

func main() {
	// md5值的计算,或者文件的计算
	// -s :对参数字母的计算
	// -f:文件的计算
	txt := flag.String("s", "", "md5.txt")
	path := flag.String("f", "", "file path")
	help := flag.Bool("h", false, "help")

	flag.Usage = func() {
		fmt.Println(`
Usage: md5.exe [-s 123abc] [-f filepath]

Option:
`)
		flag.PrintDefaults()
	}
	flag.Parse()

	if *help || *txt == "" && *path == "" {
		flag.Usage()
	} else {
		var md5 string
		if *path != "" {
			md5 = md5file(*path)
		} else {
			md5 = md5string(*txt)
		}
		fmt.Println(md5)
	}

}
posted @ 2022-05-04 17:19  元气少女郭德纲!!  阅读(248)  评论(0编辑  收藏  举报