文件 & 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)
}
写入文件
两种写入方式:
- 字节切片形式写入,存储还是字符串
- 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)
}
}