目录操作
创建目录
package main
import ( "fmt" "os" )
func main() { err := os.Mkdir("testdir", 0755) if err != nil { fmt.Println("创建目录失败:", err) return } fmt.Println("目录创建成功") err = os.MkdirAll("testdir/subdir1/subdir2", 0755) if err != nil { fmt.Println("创建多级目录失败:", err) return } fmt.Println("多级目录创建成功") }
|
删除目录
package main
import ( "fmt" "os" )
func main() { err := os.Remove("testdir") if err != nil { fmt.Println("删除目录失败:", err) } else { fmt.Println("目录删除成功") } err = os.RemoveAll("testdir") if err != nil { fmt.Println("递归删除目录失败:", err) } else { fmt.Println("目录递归删除成功") } }
|
判断目录是否存在
package main
import ( "fmt" "os" )
func isDirExists(path string) bool { info, err := os.Stat(path) if os.IsNotExist(err) { return false } return info.IsDir() }
func main() { path := "testdir" if isDirExists(path) { fmt.Printf("目录 %s 存在\n", path) } else { fmt.Printf("目录 %s 不存在\n", path) } }
|
文件操作
创建文件
package main
import ( "fmt" "os" )
func main() { file, err := os.Create("test.txt") if err != nil { fmt.Println("创建文件失败:", err) return } defer file.Close() fmt.Println("文件创建成功") }
|
写入文件
package main
import ( "fmt" "os" )
func main() { content := []byte("Hello, Go!\n这是一行中文文本。") err := os.WriteFile("test.txt", content, 0644) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("文件写入成功") file, err := os.OpenFile("test2.txt", os.O_WRONLY|os.O_CREATE, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() _, err = file.WriteString("第一行内容\n") if err != nil { fmt.Println("写入文件失败:", err) return } _, err = file.Write([]byte("第二行内容\n")) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("逐步写入文件成功") }
|
读取文件
package main
import ( "fmt" "os" "io" )
func main() { content, err := os.ReadFile("test.txt") if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("文件内容:\n%s\n", string(content)) file, err := os.Open("test.txt") if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() fmt.Println("逐步读取文件内容:") buffer := make([]byte, 16) for { n, err := file.Read(buffer) if err != nil && err != io.EOF { fmt.Println("读取文件失败:", err) return } if n == 0 { break } fmt.Print(string(buffer[:n])) } }
|
文件信息与属性
package main
import ( "fmt" "os" "time" )
func main() { fileInfo, err := os.Stat("test.txt") if err != nil { fmt.Println("获取文件信息失败:", err) return } fmt.Printf("文件名: %s\n", fileInfo.Name()) fmt.Printf("文件大小: %d 字节\n", fileInfo.Size()) fmt.Printf("权限: %s\n", fileInfo.Mode()) fmt.Printf("最后修改时间: %s\n", fileInfo.ModTime().Format(time.RFC3339)) fmt.Printf("是否是目录: %t\n", fileInfo.IsDir()) }
|
重命名和移动文件
package main
import ( "fmt" "os" )
func main() { err := os.Rename("test.txt", "renamed.txt") if err != nil { fmt.Println("重命名文件失败:", err) return } fmt.Println("文件重命名成功") os.MkdirAll("subdir", 0755) err = os.Rename("renamed.txt", "subdir/moved.txt") if err != nil { fmt.Println("移动文件失败:", err) return } fmt.Println("文件移动成功") }
|
删除文件
package main
import ( "fmt" "os" )
func main() { err := os.Remove("test.txt") if err != nil { fmt.Println("删除文件失败:", err) return } fmt.Println("文件删除成功") }
|
遍历文件夹
使用os.ReadDir (Go 1.16+)
package main
import ( "fmt" "os" "path/filepath" )
func main() { entries, err := os.ReadDir(".") if err != nil { fmt.Println("读取目录失败:", err) return } fmt.Println("当前目录内容:") for _, entry := range entries { info, err := entry.Info() if err != nil { fmt.Printf("获取文件信息失败: %v\n", err) continue } fileType := "文件" if entry.IsDir() { fileType = "目录" } fmt.Printf("%s: %s (%d bytes)\n", entry.Name(), fileType, info.Size()) } }
|
使用filepath.Walk
package main
import ( "fmt" "os" "path/filepath" )
func main() { fmt.Println("遍历当前目录及其子目录:") err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Printf("访问路径 %s 时出错: %v\n", path, err) return nil } fileType := "文件" if info.IsDir() { fileType = "目录" } if info.IsDir() && info.Name() == ".git" { fmt.Printf("跳过目录: %s\n", path) return filepath.SkipDir } relPath, _ := filepath.Rel(".", path) fmt.Printf("%s: %s (%d bytes)\n", relPath, fileType, info.Size()) return nil }) if err != nil { fmt.Printf("遍历目录失败: %v\n", err) } }
|
使用filepath.WalkDir (Go 1.16+)
package main
import ( "fmt" "os" "path/filepath" )
func main() { fmt.Println("使用WalkDir遍历目录:") err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error { if err != nil { fmt.Printf("访问路径 %s 时出错: %v\n", path, err) return nil } fileType := "文件" if d.IsDir() { fileType = "目录" } info, err := d.Info() if err != nil { fmt.Printf("获取文件信息失败: %v\n", err) return nil } relPath, _ := filepath.Rel(".", path) fmt.Printf("%s: %s (%d bytes)\n", relPath, fileType, info.Size()) return nil }) if err != nil { fmt.Printf("遍历目录失败: %v\n", err) } }
|
高级文件操作
检查文件是否存在
package main
import ( "fmt" "os" )
func fileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() }
func main() { filename := "test.txt" if fileExists(filename) { fmt.Printf("文件 %s 存在\n", filename) } else { fmt.Printf("文件 %s 不存在\n", filename) } }
|
复制文件
package main
import ( "fmt" "io" "os" )
func copyFile(src, dst string) error { source, err := os.Open(src) if err != nil { return err } defer source.Close() destination, err := os.Create(dst) if err != nil { return err } defer destination.Close() _, err = io.Copy(destination, source) return err }
func main() { err := copyFile("source.txt", "destination.txt") if err != nil { fmt.Println("复制文件失败:", err) return } fmt.Println("文件复制成功") }
|
处理文件路径
package main
import ( "fmt" "path/filepath" )
func main() { path := filepath.Join("dir", "subdir", "file.txt") fmt.Println("拼接后的路径:", path) dir := filepath.Dir(path) fmt.Println("目录部分:", dir) filename := filepath.Base(path) fmt.Println("文件名部分:", filename) ext := filepath.Ext(path) fmt.Println("文件扩展名:", ext) rel, err := filepath.Rel("/usr/local", "/usr/local/bin/go") if err != nil { fmt.Println("获取相对路径失败:", err) } else { fmt.Println("相对路径:", rel) } cleanPath := filepath.Clean("./dir/.././file.txt") fmt.Println("清理后的路径:", cleanPath) }
|
综合示例:统计目录大小
package main
import ( "fmt" "os" "path/filepath" )
func dirSize(path string) (int64, error) { var size int64 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { size += info.Size() } return nil }) return size, err }
func main() { if len(os.Args) < 2 { fmt.Println("请指定要统计的目录") return } dir := os.Args[1] size, err := dirSize(dir) if err != nil { fmt.Printf("统计目录大小失败: %v\n", err) return } var sizeStr string switch { case size > 1<<30: sizeStr = fmt.Sprintf("%.2f GB", float64(size)/(1<<30)) case size > 1<<20: sizeStr = fmt.Sprintf("%.2f MB", float64(size)/(1<<20)) case size > 1<<10: sizeStr = fmt.Sprintf("%.2f KB", float64(size)/(1<<10)) default: sizeStr = fmt.Sprintf("%d B", size) } fmt.Printf("目录 %s 的大小: %s\n", dir, sizeStr) }
|
Go语言中的文件读写模式详解
Go语言提供了灵活的文件操作方式,支持多种读写模式。下面将详细说明各种文件读写模式,包括覆盖、追加等方式,并提供详细的代码示例。
文件打开模式
在Go中,我们使用os.OpenFile
函数来打开文件,并通过组合不同的标志(flags)来指定文件打开模式:
file, err := os.OpenFile(filename, flags, permission)
|
常用的文件打开标志包括:
os.O_RDONLY
: 只读模式
os.O_WRONLY
: 只写模式
os.O_RDWR
: 读写模式
os.O_CREATE
: 如果文件不存在则创建
os.O_APPEND
: 追加模式
os.O_TRUNC
: 如果文件存在则清空
os.O_EXCL
: 与O_CREATE一起使用,文件必须不存在
这些模式可以通过按位或(|)组合使用。
各种读写模式示例
只读模式
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_RDONLY, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() data := make([]byte, 1024) count, err := file.Read(data) if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("读取了 %d 字节: %s\n", count, string(data[:count])) }
|
覆盖写入模式(创建新文件或清空已有文件)
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() content := "这是新内容,会覆盖文件原有内容。\n" _, err = file.WriteString(content) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("文件覆盖写入成功") }
|
追加写入模式
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() content := "这是在文件末尾追加的内容。\n" _, err = file.WriteString(content) if err != nil { fmt.Println("追加内容失败:", err) return } fmt.Println("文件追加写入成功") }
|
读写模式(不清空)
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() data := make([]byte, 1024) count, err := file.Read(data) if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("原文件内容: %s\n", string(data[:count])) _, err = file.Seek(0, 0) if err != nil { fmt.Println("重置文件指针失败:", err) return } newContent := "这是从开头写入的新内容。\n" _, err = file.WriteString(newContent) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("文件读写操作成功") }
|
读写模式并清空
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() content := "这是清空后写入的新内容。\n" _, err = file.WriteString(content) if err != nil { fmt.Println("写入文件失败:", err) return } _, err = file.Seek(0, 0) if err != nil { fmt.Println("重置文件指针失败:", err) return } data := make([]byte, 1024) count, err := file.Read(data) if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("文件内容: %s\n", string(data[:count])) fmt.Println("文件清空后读写操作成功") }
|
追加读写模式
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() data := make([]byte, 1024) count, err := file.Read(data) if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("原文件内容: %s\n", string(data[:count])) appendContent := "这是在文件末尾追加的内容。\n" _, err = file.WriteString(appendContent) if err != nil { fmt.Println("追加内容失败:", err) return } fmt.Println("文件追加读写操作成功") }
|
互斥创建模式
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) if err != nil { if os.IsExist(err) { fmt.Println("文件已存在,创建失败") } else { fmt.Println("创建文件失败:", err) } return } defer file.Close() content := "这是在新创建的文件中写入的内容。\n" _, err = file.WriteString(content) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("文件创建并写入成功") }
|
使用os包便捷函数
除了使用os.OpenFile
,Go还提供了一些便捷函数:
使用os.WriteFile覆盖写入
package main
import ( "fmt" "os" )
func main() { content := "这是使用os.WriteFile写入的内容,会覆盖原有内容。\n" err := os.WriteFile("example.txt", []byte(content), 0644) if err != nil { fmt.Println("写入文件失败:", err) return } fmt.Println("文件覆盖写入成功") }
|
使用os.ReadFile读取文件
package main
import ( "fmt" "os" )
func main() { data, err := os.ReadFile("example.txt") if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Printf("文件内容: %s\n", string(data)) }
|
综合示例:日志文件操作
package main
import ( "fmt" "os" "time" )
func logMessage(message string) error { file, err := os.OpenFile("app.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return err } defer file.Close() timestamp := time.Now().Format("2006-01-02 15:04:05") logEntry := fmt.Sprintf("[%s] %s\n", timestamp, message) _, err = file.WriteString(logEntry) return err }
func viewLog() error { data, err := os.ReadFile("app.log") if err != nil { return err } fmt.Println("=== 应用日志 ===") fmt.Println(string(data)) return nil }
func clearLog() error { file, err := os.OpenFile("app.log", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } defer file.Close() _, err = file.WriteString("=== 日志已清空 ===\n") return err }
func main() { logMessage("应用启动") logMessage("执行操作1") logMessage("执行操作2") viewLog() clearLog() logMessage("清空后重新开始记录") viewLog() }
|
Go语言提供了多种文件读写模式,可以根据需求灵活选择:
- 只读模式 (
os.O_RDONLY
):仅读取文件内容
- 覆盖写入模式 (
os.O_WRONLY|os.O_CREATE|os.O_TRUNC
):创建新文件或清空已有文件后写入
- 追加模式 (
os.O_WRONLY|os.O_CREATE|os.O_APPEND
):在文件末尾追加内容
- 读写模式 (
os.O_RDWR|os.O_CREATE
):可读可写,保留原有内容
- 读写清空模式 (
os.O_RDWR|os.O_CREATE|os.O_TRUNC
):清空文件后可读可写
- 追加读写模式 (
os.O_RDWR|os.O_CREATE|os.O_APPEND
):可读可追加
- 互斥创建模式 (
os.O_WRONLY|os.O_CREATE|os.O_EXCL
):确保创建新文件
此外,Go还提供了便捷函数os.WriteFile
和os.ReadFile
,适用于简单的文件读写场景。
Go语言中的文件权限详解:644及其他权限值
在Go语言的文件操作中,文件权限是一个重要的概念。权限值(如0644)决定了谁可以读取、写入或执行文件。本文将详细解释文件权限的含义、各种权限值及其应用场景。
文件权限基础
在Unix/Linux系统中,文件权限分为三类用户:
- 所有者(User) - 文件的所有者
- 组(Group) - 文件所属的用户组
- 其他用户(Other) - 系统中的其他用户
每种用户类型有三种权限:
- 读(Read) - 允许读取文件内容
- 写(Write) - 允许修改文件内容
- 执行(Execute) - 允许执行文件(对于目录,允许访问目录内容)
权限表示方法
八进制表示法
Go语言中使用八进制数表示文件权限,如0644。这个四位数的结构如下:
- 第一位:特殊权限位(setuid, setgid, sticky)
- 第二位:所有者权限
- 第三位:组权限
- 第四位:其他用户权限
每位权限的值计算方式:
权限值通过相加组合:
- 7 = 4+2+1 (读+写+执行)
- 6 = 4+2 (读+写)
- 5 = 4+1 (读+执行)
- 4 = 4 (只读)
- 3 = 2+1 (写+执行)
- 2 = 2 (只写)
- 1 = 1 (只执行)
- 0 = 0 (无权限)
常见权限值及含义
0644 (rw-r—r—)
- 所有者:读+写 (6)
- 组用户:只读 (4)
- 其他用户:只读 (4)
- 这是最常见的文件权限,适用于大多数普通文件
err := os.WriteFile("example.txt", []byte("内容"), 0644)
|
0755 (rwxr-xr-x)
- 所有者:读+写+执行 (7)
- 组用户:读+执行 (5)
- 其他用户:读+执行 (5)
- 适用于可执行文件或目录
err := os.Mkdir("mydir", 0755)
|
0600 (rw———-)
- 所有者:读+写 (6)
- 组用户:无权限 (0)
- 其他用户:无权限 (0)
- 适用于包含敏感信息的私有文件
file, err := os.OpenFile("config.ini", os.O_CREATE|os.O_WRONLY, 0600)
|
0777 (rwxrwxrwx)
- 所有者:读+写+执行 (7)
- 组用户:读+写+执行 (7)
- 其他用户:读+写+执行 (7)
- 所有用户都有完全权限(慎用,有安全风险)
err := os.WriteFile("temp.txt", []byte("临时内容"), 0777)
|
0444 (r—r—r—)
- 所有者:只读 (4)
- 组用户:只读 (4)
- 其他用户:只读 (4)
- 适用于只读文件,防止意外修改
err := os.WriteFile("readonly.txt", []byte("只读内容"), 0444)
|
特殊权限位
SetUID (4xxx)
- 设置用户ID,当执行此文件时,进程的有效用户ID变为文件所有者的用户ID
- 示例:4755 (rwsr-xr-x)
err := os.Chmod("program", 0755|os.ModeSetuid)
|
SetGID (2xxx)
- 设置组ID,当执行此文件时,进程的有效组ID变为文件所属组的组ID
- 对于目录,新创建的文件会继承目录的组ID
- 示例:2755 (rwxr-sr-x)
err := os.Chmod("program", 0755|os.ModeSetgid)
|
Sticky Bit (1xxx)
- 粘滞位,对于目录,只有文件所有者和root用户才能删除目录中的文件
- 常用于/tmp目录
- 示例:1755 (rwxr-xr-t)
err := os.Chmod("shared_dir", 0755|os.ModeSticky)
|
使用常量定义权限
Go语言的os
包提供了一些常量来表示权限:
const ( ModeDir = fs.ModeDir ModeAppend = fs.ModeAppend ModeExclusive = fs.ModeExclusive ModeTemporary = fs.ModeTemporary ModeSymlink = fs.ModeSymlink ModeDevice = fs.ModeDevice ModeNamedPipe = fs.ModeNamedPipe ModeSocket = fs.ModeSocket ModeSetuid = fs.ModeSetuid ModeSetgid = fs.ModeSetgid ModeCharDevice = fs.ModeCharDevice ModeSticky = fs.ModeSticky ModeIrregular = fs.ModeIrregular
ModePerm = fs.ModePerm )
|
权限操作示例
查看文件权限
package main
import ( "fmt" "os" )
func main() { info, err := os.Stat("example.txt") if err != nil { fmt.Println("获取文件信息失败:", err) return } mode := info.Mode() fmt.Printf("权限八进制表示: %04o\n", mode.Perm()) fmt.Printf("权限字符串表示: %s\n", mode.String()) fmt.Printf("是否是目录: %t\n", mode.IsDir()) fmt.Printf("是否是常规文件: %t\n", mode.IsRegular()) }
|
修改文件权限
package main
import ( "fmt" "os" )
func main() { err := os.WriteFile("example.txt", []byte("内容"), 0644) if err != nil { fmt.Println("创建文件失败:", err) return } err = os.Chmod("example.txt", 0444) if err != nil { fmt.Println("修改权限失败:", err) return } fmt.Println("文件权限已修改为只读") err = os.WriteFile("example.txt", []byte("新内容"), 0644) if err != nil { fmt.Println("写入失败(符合预期):", err) } }
|
创建具有特殊权限的文件
package main
import ( "fmt" "os" )
func main() { file, err := os.OpenFile("setuid_program", os.O_CREATE|os.O_WRONLY, 0755|os.ModeSetuid) if err != nil { fmt.Println("创建文件失败:", err) return } defer file.Close() _, err = file.WriteString("#!/bin/bash\necho 'SetUID程序'") if err != nil { fmt.Println("写入内容失败:", err) return } fmt.Println("SetUID文件创建成功") }
|
需要注意的是,文件权限在Windows系统上的行为与Unix/Linux系统有所不同:
- Windows没有完全相同的权限模型
- 读写权限会被映射到Windows的文件属性(如只读属性)
- 执行权限在Windows上主要依赖于文件扩展名(如.exe、.bat等)
- SetUID、SetGID和Sticky Bit在Windows上无效
Go语言会尽量在不同平台上模拟Unix权限行为,但在编写跨平台应用时应注意这些差异。