一、文件的概念
-
文件是数据源的一种,比如日常使用的 word文件 txt文件 excel文件。。。
-
文件最主要的作用就是保存数据,可以保存图片、音频、视频等二进制数据,也可以保存结构化的文本数据,比如 json csv xml等
二、输入流和输出流
-
文件在程序中是以流的形式来操作的
-
输入流:从文件中读取数据的流(读文件)
-
输出流:向文件中写入数据的流(写文件)
三、文件的打开和关闭
- 使用 file, err := os.Open(文件路径) 打开文件, 会返回一个文件对象和一个错误对象
-
使用 defer file.Close() 来确保文件在使用完毕后被正确关闭,释放资源
-
file 是 *File 类型的对象,包含了文件的相关信息和方法
package main
import(
"fmt"
"os"
)
func main() {
// 打开文件
file, err := os.Open("/Users/zyq/go/1.txt")
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
fmt.Println("文件打开成功", file, file.Name()) // file 是 *File 类型的对象,包含了文件的相关信息和方法
}
四、文件的读写
1、读取文件
-
os.ReadFile(文件路径) 方法:直接读取文件的全部内容,返回一个字节切片和一个错误对象
-
os.ReadAll(file) 方法: 一次性读取文件的全部内容,返回一个字节切片和一个错误对象
-
逐行读取文件的内容,使用 bufio.NewScanner(file) 创建一个扫描器对象,然后使用 scanner.Scan() 方法逐行读取文件内容,使用 scanner.Text() 方法获取
-
带缓存区的读取文件内容,使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,然后使用 reader.ReadString('\r\n') 方法逐行读取文件内容,直到遇到换行符
-
使用 io.EOF 来判断文件是否读取到末尾
package main
import(
"fmt"
"os"
"io/ioutil"
"bufio"
)
func main() {
// 打开文件
file, err := os.Open("/Users/zyq/go/1.txt")
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
// 方法一:直接读取文件的全部内容
content, err := os.ReadFile("/Users/zyq/go/1.txt")
if err != nil {
fmt.Println("读取文件失败", err)
return
}
fmt.Println("文件内容:", string(content))
// 方法二:使用 os.ReadAll(file) 方法一次性读取文件的全部内容
content1, err := io.ReadAll(file)
if err != nil {
fmt.Println("读取文件失败", err)
return
}
fmt.Println("文件内容:", string(content1))
// 方法三:逐行读取文件的内容,使用 bufio.NewScanner(file) 创建一个扫描器对象,然后使用 scanner.Scan() 方法逐行读取文件内容,使用 scanner.Text() 方法获取
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println("文件内容:", line)
}
if err := scanner.Err(); err != nil {
fmt.Println("读取文件失败", err)
return
}
// 方法四:带缓存区的读取文件内容,使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,然后使用 reader.ReadString('\n') 方法逐行读取文件内容,直到遇到换行符
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n') // 以换行符为分隔符读取文件内容
if err != nil {
if err == io.EOF {
fmt.Println("文件读取完毕")
break
}
fmt.Println("读取文件失败", err)
return
}
fmt.Println("文件内容:", line)
}
}
2、写入文件
-
file, err := os.OpenFile(文件路径, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 方法: 打开一个文件用于写入,如果文件不存在则创建,如果文件存在则清空内容,返回一个文件对象和一个错误对象;然后使用 file.WriteString(内容) 方法向文件中写入字符串,返回写入的字节数和一个错误对象
-
使用 os.WriteFile(文件路径, []byte(内容), 0644) 方法直接将内容写入文件,如果文件不存在则创建,如果文件存在则覆盖内容,返回一个错误对象
-
使用 writer := bufio.NewWriter(file) 创建一个带缓存区的写入器对象,然后使用 writer.WriteString(内容) 方法向文件中写入字符串,最后使用 writer.Flush() 方法将缓存区中的内容写入文件
package main
import(
"fmt"
"os"
"io"
"bufio"
)
func main() {
// 1、打开文件用于写入,如果文件不存在则创建,如果文件存在则清空内容
// os.OpenFile + os.O_WRONLY + os.O_CREATE + os.O_TRUNC
// file.WriteString
file, err := os.OpenFile("/Users/zyq/go/1.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
// 写入 5 句“hello”
for i := 0; i < 5; i++ {
_, err := file.WriteString("hello\n")
if err != nil {
fmt.Println("写入文件失败", err)
return
}
}
// 2、打开文件用于写入,如果文件不存在则创建,如果文件存在则追加内容
// os.OpenFile + os.O_WRONLY + os.O_CREATE + os.O_APPEND
// bufio.NewWriter + writer.WriteString + writer.Flush
file, err = os.OpenFile("/Users/zyq/go/1.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
// 追加写入 5 句“world”
writer := bufio.NewWriter(file)
for i := 0; i < 5; i++ {
_, err := writer.WriteString("world\n")
if err != nil {
fmt.Println("写入文件失败", err)
return
}
}
// 将缓存区中的内容写入文件
err = writer.Flush()
if err != nil {
fmt.Println("刷新文件失败", err)
return
}
// 3、打开文件用于读写,将原来的内容显示出来,然后再写入新的内容
// os.OpenFile + os.O_RDWR + os.O_APPEND
// bufio.NewReader + reader.ReadString + reader.Flush
file, err = os.OpenFile("/Users/zyq/go/1.txt", os.O_RDWR|os.O_APPEND, 0644)
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
// 读取文件内容
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
fmt.Println("文件读取完毕")
break
}
fmt.Println("读取文件失败", err)
return
}
fmt.Println("文件内容:", line)
}
// 写入新的内容
_, err = file.WriteString("hello world\n")
if err != nil {
fmt.Println("写入文件失败", err)
return
}
// 4、使用 os.WriteFile(文件路径, []byte(内容), 0644) 方法直接将内容写入文件,如果文件不存在则创建,如果文件存在则覆盖内容
content1 := "hello world\n"
err = os.WriteFile("/Users/zyq/go/1.txt", []byte(content1), 0644)
if err != nil {
fmt.Println("写入文件失败", err)
return
}
}
五、判断文件或目录是否存在
- 使用 os.Stat(文件路径) 方法获取文件或目录的信息,如果返回的错误对象不为 nil 且错误类型为 os.IsNotExist(err),则说明文件或目录不存在
package main
import(
"fmt"
"os"
)
func main() {
// 判断文件是否存在
_, err := os.Stat("/Users/zyq/go/1.txt")
if os.IsNotExist(err) {
fmt.Println("文件不存在")
} else {
fmt.Println("文件存在")
}
// 判断目录是否存在
_, err = os.Stat("/Users/zyq/go")
if os.IsNotExist(err) {
fmt.Println("目录不存在")
} else {
fmt.Println("目录存在")
}
}
六、文件的拷贝
- 使用 io.Copy(dst, src) 方法将 src 文件的内容复制到 dst 文件中,dst 和 src 都是 *File 类型的对象
package main
import(
"fmt"
"os"
"io"
)
func main() {
// 打开源文件
srcFile, err := os.Open("/Users/zyq/go/1.txt")
// 处理错误
if err != nil {
fmt.Println("打开源文件失败", err)
return
}
// 关闭源文件
defer srcFile.Close()
// 创建目标文件
dstFile, err := os.Create("/Users/zyq/go/2.txt")
// 处理错误
if err != nil {
fmt.Println("创建目标文件失败", err)
return
}
// 关闭目标文件
defer dstFile.Close()
// 复制文件内容
_, err = io.Copy(dstFile, srcFile)
if err != nil {
fmt.Println("复制文件失败", err)
return
}
fmt.Println("文件复制成功")
}
七、文件的移动和重命名
-
使用 os.Rename(oldPath, newPath) 方法将 oldPath 路径的文件或目录移动到 newPath 路径,如果 newPath 已经存在则会被覆盖
-
移动文件和重命名文件本质上是一样的操作,都是使用 os.Rename() 方法来实现的,只不过移动文件是改变了文件的路径,而重命名文件是改变了文件的名称
package main
import(
"fmt"
"os"
)
func main() {
// 移动文件
err := os.Rename("/Users/zyq/go/1.txt", "/Users/zyq/go/2.txt")
if err != nil {
fmt.Println("移动文件失败", err)
return
}
fmt.Println("文件移动成功")
// 重命名文件
err = os.Rename("/Users/zyq/go/2.txt", "/Users/zyq/go/3.txt")
if err != nil {
fmt.Println("重命名文件失败", err)
return
}
fmt.Println("文件重命名成功")
}
八、文件的删除
- 使用 os.Remove(文件路径) 方法删除指定路径的文件或目录,如果路径是一个目录,则该目录必须为空才能被删除
package main
import(
"fmt"
"os"
)
func main() {
// 删除文件
err := os.Remove("/Users/zyq/go/1.txt")
if err != nil {
fmt.Println("删除文件失败", err)
return
}
fmt.Println("文件删除成功")
// 删除目录
err = os.Remove("/Users/zyq/go/test")
if err != nil {
fmt.Println("删除目录失败", err)
return
}
fmt.Println("目录删除成功")
}
九、文件的权限和模式
-
文件的权限和模式是用一个三位八进制数来表示的,每一位八进制数代表了文件所有者、文件所属组和其他用户的权限
-
每一位八进制数的值是由读权限(4)、写权限(2)和执行权限(1)相加得到的,比如 7 代表读、写和执行权限,6 代表读和写权限,5 代表读和执行权限,4 代表读权限,3 代表写和执行权限,2 代表写权限,1 代表执行权限,0 代表没有权限
-
使用 os.Chmod(文件路径, 权限) 方法来改变文件的权限,其中权限是一个 os.FileMode 类型的值,可以使用 os.FileMode(权限) 来将一个八进制数转换为 os.FileMode 类型的值
package main
import(
"fmt"
"os"
)
func main() {
// 改变文件权限为 755,即文件所有者具有读、写和执行权限,文件所属组和其他用户具有读和执行权限
err := os.Chmod("/Users/zyq/go/1.txt", os.FileMode(0755))
if err != nil {
fmt.Println("改变文件权限失败", err)
return
}
fmt.Println("文件权限改变成功")
}
十、文件的属性和信息
- 使用 os.Stat(文件路径) 方法获取文件或目录的信息,返回一个 os.FileInfo 类型的对象和一个错误对象,os.FileInfo 对象包含了文件的名称、大小、权限、修改时间等信息
package main
import(
"fmt"
"os"
)
func main() {
// 获取文件信息
fileInfo, err := os.Stat("/Users/zyq/go/1.txt")
if err != nil {
fmt.Println("获取文件信息失败", err)
return
}
fmt.Println("文件名称:", fileInfo.Name())
fmt.Println("文件大小:", fileInfo.Size())
fmt.Println("文件权限:", fileInfo.Mode())
fmt.Println("文件修改时间:", fileInfo.ModTime())
}
十一、统计文件中的英文、数字、空格和其他字符数量
- 通过读取文件的内容,然后遍历每个字符,使用 unicode 包中的函数来判断字符的类型,分别统计英文、数字、空格和其他字符的数量
package main
import(
"fmt"
"os"
"io"
"unicode"
)
func main() {
// 打开文件
file, err := os.Open("/Users/zyq/go/1.txt")
// 处理错误
if err != nil {
fmt.Println("打开文件失败", err)
return
}
// 关闭文件
defer file.Close()
// 统计字符数量: 使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,
// 然后使用 reader.ReadRune() 方法逐个读取文件中的字符,直到遇到文件末尾,
// 使用 unicode.IsLetter()、unicode.IsDigit()、unicode.IsSpace() 来判断字符的类型,
// 并分别统计英文、数字、空格和其他字符的数量
var letterCount1, digitCount1, spaceCount1, otherCount1 int
reader := bufio.NewReader(file)
for {
char, _, err := reader.ReadRune()
if err != nil {
if err == io.EOF {
fmt.Println("文件读取完毕")
break
}
fmt.Println("读取文件失败", err)
return
}
if unicode.IsLetter(char) {
letterCount1++
} else if unicode.IsDigit(char) {
digitCount1++
} else if unicode.IsSpace(char) {
spaceCount1++
} else {
otherCount1++
}
}
fmt.Printf("英文字符数量:%d\n数字字符数量:%d\n空格字符数量:%d\n其他字符数量:%d\n", letterCount1, digitCount1, spaceCount1, otherCount1)
// 另一种方法:使用 reader.ReadString('\n') 方法逐行读取文件内容,然后遍历每行的每个字符来统计数量
var letterCount, digitCount, spaceCount, otherCount int
for {
c, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
fmt.Println("文件读取完毕")
break
}
fmt.Println("读取文件失败", err)
return
}
str := []rune(c)
for _, char := range str {
switch {
case char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z':
letterCount++
case char >= '0' && char <= '9':
digitCount++
case char == ' ' || char == '\t' || char == '\n':
spaceCount++
default:
otherCount++
}
}
}
}
十二、文件的路径和目录操作
- 使用 path/filepath 包来处理文件路径和目录操作,常用的方法有 filepath.Join() 来连接路径,filepath.Dir() 来获取目录,filepath.Base() 来获取文件名,filepath.Ext() 来获取文件扩展名,filepath.Walk() 来遍历目录等
package main
import(
"fmt"
"path/filepath"
)
func main() {
// 连接路径
path := filepath.Join("/Users/zyq/go", "1.txt")
fmt.Println("连接后的路径:", path) // 输出:/Users/zyq/go/1.txt
// 获取目录
dir := filepath.Dir("/Users/zyq/go/1.txt")
fmt.Println("文件所在目录:", dir) // 输出:/Users/zyq/go
// 获取文件名
fileName := filepath.Base("/Users/zyq/go/1.txt")
fmt.Println("文件名:", fileName) // 输出:1.txt
// 获取文件扩展名
ext := filepath.Ext("/Users/zyq/go/1.txt")
fmt.Println("文件扩展名:", ext) // 输出:.txt
// 遍历目录
err := filepath.Walk("/Users/zyq/go", func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Println("访问路径失败", err)
return err
}
fmt.Println("访问路径:", path)
return nil
})
if err != nil {
fmt.Println("遍历目录失败", err)
return
}
}