读取
- 一次性读取
go
data, err := os.ReadFile("filename.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
- 按行读取
方式1:bufio.NewScanner
go
file, err := os.Open("filename.txt")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
方式2:bufio.NewReader
go
file, err := os.Open("filename.txt")
if err != nil {
panic(err)
}
defer file.Close()
reader:= bufio.NewReader(file)
for {
line, _, err := reader.ReadLine()
if err == io.EOF {
break
}
fmt.Println(string(line))
}
区别是bufio.Scanner
还可自定义扫描的分隔符,如:
go
file, _ := os.Open("filename.txt")
scanner := bufio.NewScanner(file)
//scanner.Split(bufio.ScanLines) // 按行读,是默认读取方式
//scanner.Split(bufio.ScanWords) // 按单词读,返回文本中每个以空格分隔的单词,并删除周围的空格
//scanner.Split(bufio.ScanBytes) // 按字节读
//scanner.Split(bufio.ScanRunes) // 按 UTF-8 编码的字符读,如:中文字符
for scanner.Scan() {
println(scanner.Text())
}
- 按指定字节长度读取
可以使用bufio.Reader
的Read
方法,能读取指定长度的字节到一个切片中
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("filename.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 2) // 指定读取的字节长度
n, err := reader.Read(buf)
if err != nil {
return
}
fmt.Printf("读取的字节数: %d\n", n)
fmt.Printf("读取的内容: %s\n", buf[:n])
}
假如要读取的文件中只有内容hello
,那么将输出:
读取的字节数: 2
读取的内容: he
然后在此基础上,使用循环来读整个文件
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("filename.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 2) // 指定读取的字节长度
for {
n, err := reader.Read(buf)
if err != nil {
break
}
fmt.Printf("%s\n", buf[:n])
}
}
其输出将变为:
he
ll
o
写入
- 权限表示
在 Unix 和类 Unix 系统的权限表示中,用3个数字表示,三个数字各代表了不同用户类别的访问权限:
- 第一个数字:代表文件拥有者(owner)的权限。
- 第二个数字:代表与文件拥有者同组的用户(group)的权限。
- 第三个数字:代表其他所有用户(others)的权限。
每个数字可以是0到7之间的任意数字,分别控制读(4)、写(2)和执行(1)权限的开启和关闭。这些数字是对应的权限值的总和
- 0 没有权限
- 1 执行权限
- 2 写权限
- 4 读权限
一个权限表示是:0666,那么它其实是代表可读可写,前面的 0 是用来明确表示这是一个八进制数
- 操作标志
go
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
举几个例子:
go
os.O_CREATE|os.O_WRONLY // 不存在则创建,若存在则从头写入并覆盖原位置的内容,比如原先是hello,然后写入的是demo,那么会变成demoo
os.O_CREATE|os.O_TRUNC|os.O_WRONLY // 不存在则创建,存在则清空
os.O_APPEND|os.O_WRONLY // 追加写
常用方法:
go
os.OpenFile(name string, flag int, perm FileMode) (*File, error)
os.WriteFile(name string, data []byte, perm FileMode) error // 存在则清空后以原权限写入,不存在则以指定权限创建
os.Open(name string) (*File, error) //模式为 O_RDONLY
os.Create(name string) (*File, error) //Create 创建或清空,模式为 O_RDWR
os.Remove(name string) error //将指定的文件或目录删除
os.RemoveAll(path string) error //删除路径及其包含的任何子项
文件复制
go
io.Copy(dst Writer, src Reader) (written int64, err error)
go
package main
import (
"io"
"os"
)
// CopyFile copies a file from src to dst. If src and dst files exist, and are the same, then return error.
// If dst does not exist, it is created with mode 0666 (before umask).
func CopyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Sync()
}
func main() {
src := "source.txt"
dst := "destination.txt"
if err := CopyFile(src, dst); err != nil {
panic(err)
}
}
操作目录
- 创建目录
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建一个目录
err := os.Mkdir("example_dir", 0755)
if err != nil {
fmt.Println(err)
}
// 创建目录路径中所有不存在的目录
err = os.MkdirAll("example_dir/subdir", 0755)
if err != nil {
fmt.Println(err)
}
}
- 读取目录
go
os.ReadDir(name string) ([]DirEntry, error) // go 1.16 引入
读取指定目录下的文件和文件夹,及其大小
go
package main
import (
"fmt"
"os"
)
// convertSize 将字节大小转换为更可读的单位
func convertSize(size int64) string {
const (
KB = 1 << 10 // 1024
MB = 1 << 20 // 1024 * 1024
GB = 1 << 30 // 1024 * 1024 * 1024
)
switch {
case size >= GB:
return fmt.Sprintf("%.2f GB", float64(size)/GB)
case size >= MB:
return fmt.Sprintf("%.2f MB", float64(size)/MB)
case size >= KB:
return fmt.Sprintf("%.2f KB", float64(size)/KB)
default:
return fmt.Sprintf("%d B", size)
}
}
func main() {
entries, err := os.ReadDir(".") // 读取当前目录
if err != nil {
fmt.Println(err)
return
}
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
// 如果无法获取文件信息,则跳过该文件
fmt.Println("Error:", err)
continue
}
fmt.Printf("%s: %s\n", info.Name(), convertSize(info.Size()))
}
}
如果想读取子目录中的内容,可以添加一个循环,然后判断是否是目录,然后同样取即可,直至没有子目录
或者使用更高效的filepath.WalkDir
(go 1.16),是filepath.Walk
(go 1.0)的优化版,性能更好
go
package main
import (
"fmt"
"os"
"path/filepath"
)
// convertSize 将字节大小转换为更可读的单位
func convertSize(size int64) string {
const (
KB = 1 << 10 // 1024
MB = 1 << 20 // 1024 * 1024
GB = 1 << 30 // 1024 * 1024 * 1024
)
switch {
case size >= GB:
return fmt.Sprintf("%.2f GB", float64(size)/GB)
case size >= MB:
return fmt.Sprintf("%.2f MB", float64(size)/MB)
case size >= KB:
return fmt.Sprintf("%.2f KB", float64(size)/KB)
default:
return fmt.Sprintf("%d B", size)
}
}
func visitFile(path string, d os.DirEntry, err error) error {
if err != nil {
fmt.Printf("访问文件时遇到错误: %v\n", err)
return nil // 返回nil继续遍历
}
if !d.IsDir() {
info, err := d.Info()
if err != nil {
fmt.Printf("获取文件信息时遇到错误: %v\n", err)
return nil
}
fmt.Printf("%s: %s\n", path, convertSize(info.Size()))
}
return nil
}
func main() {
err := filepath.WalkDir(".", visitFile)
if err != nil {
fmt.Printf("遍历目录时遇到错误: %v\n", err)
}
}