4、IO操作

Go 的 IO 模型

核心接口

Go语言的IO操作建立在几个核心接口上,这些接口构成了灵活而一致的IO模型。

io包中定义了几个最基础的接口:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

这些简单的接口组合成更复杂的接口,如:

type ReadWriter interface {
    Reader
    Writer
}

type ReadCloser interface {
    Reader
    Closer
}

type WriteCloser interface {
    Writer
    Closer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

IO操作模式

Go的IO操作遵循一些常见模式:

各个IO相关包的职责

Go标准库中与IO相关的包有多个,各自负责不同层次的功能:

os.File

在 Golang 中内置包os中,打开文件使用 Open 函数(只读)和OpenFile函数(可切换模式),关闭文件使用 Close 函数,打开文件、关闭文件以及大多数文件操作都涉及到一个很重要的结构体 os.File 结构体。

实现了io.Readerio.Writerio.Closer等接口

type File struct {
	*file // file 指针
}
type file struct {
  //是一个FD结构体类型,是一个文件的唯一标志,每一个被打开的文件在操作系统中,都会有一个文件标志符来唯一标识一个文件
	pfd        poll.FD
  //文件名
	name       string
  //文件的路径信息,也是一个结构体
	dirinfo    *dirInfo
  //是一个 bool 类型,表明该文件是否可以被追加写入内容。
	appendMode bool
}

io.EOF

error 类型。

根据 reader 接口的说明,在 n > 0 且数据被读完了的情况下,返回的 error 有可能是 EOF 也有可能是 nil

var EOF = errors.New("EOF")

文件操作

打开文件

使用os.Openos.OpenFile打开文件:

Open

Open 函数接受一个字符串类型的文件名做为参数,如果打开成功,则返回一个 File 结构体的指针,否则就返回error错误信息。

底层使用的是OpenFile(name, O_RDONLY, 0),只读

os.Open(name string) (*File, error)
// 打开文件进行读取
file, err := os.Open("input.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close() // 确保文件会被关闭

OpenFile

os.OpenFile(name string, flag int, perm FileMode) (file *File, err error)
//name:文件路径
//flag:文件打开方式,多个可使用|分隔开
//perm:打开文件的模式,即权限,例如0666、0777
// 打开文件进行读写
rwFile, err := os.OpenFile("data.txt", os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
    log.Fatal(err)
}
defer rwFile.Close()
flag可选参数
打开方式 说明
os.O_RDONLY 只读方式打开
os.O_WRONLY 只写方式打开
os.O_RDWR 读写方式打开
os.O_APPEND 追加方式打开
os.O_CREATE 不存在,则创建
os.O_EXCL 如果文件存在,且标定了O_CREATE的话,则产生一个错误
os.O_TRUNG 如果文件存在,且它成功地被打开为只写或读写方式,将其长度裁剪唯一。(覆盖)
os.O_NOCTTY 如果文件名代表一个终端设备,则不把该设备设为调用进程的控制设备
os.O_NONBLOCK 如果文件名代表一个FIFO,或一个块设备,字符设备文件,则在以后的文件及I/O操作中置为非阻塞模式。
os.O_SYNC 当进行一系列写操作时,每次都要等待上次的I/O操作完成再进行。
// 常用标志组合
// 只读模式
file, err := os.OpenFile("file.txt", os.O_RDONLY, 0)

// 只写模式,如果文件不存在则创建
file, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)

// 追加模式,如果文件不存在则创建
file, err := os.OpenFile("file.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)

// 读写模式,如果文件不存在则创建
file, err := os.OpenFile("file.txt", os.O_RDWR|os.O_CREATE, 0666)

// 创建新文件,如果文件已存在则截断为空
file, err := os.OpenFile("file.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)

关闭文件

使用 File 指针来调用 Close 函数,如果关闭失败会返回 error 错误信息。

一般在构建 File 对象后,使用defer file.Close()来保证文件关闭。

func (file *File) Close() error

其他方法

Seek

Seek设置下一次读/写的位置。

offset:为相对偏移量

whence:决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。

它返回新的偏移量(相对开头)和可能的错误。

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

Truncate

Truncate改变文件的大小,它不会改变I/O的当前位置。 如果截断文件,多出的部分就会被丢弃,也就是从头开始保留size个字节。如果出错,错误底层类型是*PathErrorsize = 0则清空文件。

func (f *File) Truncate(size int64) error

也可以使用os包提供的方法,一样的作用,name为文件名

func Truncate(name string, size int64) error

获取文件信息

type FileInfo interface {
    Name() string       // 文件的名字
    Size() int64        // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
    Mode() FileMode     // 文件的模式位 (例-rw-rw-r--)
    ModTime() time.Time // 文件的修改时间
    IsDir() bool        // 等价于Mode().IsDir()
    Sys() interface{}   // 底层数据来源(可以返回nil)
}

os.fileStat实现了FileInfo接口的所有方法,使用os.Stat(path)函数可以获取name对应文件的fileStat实例

func Stat(name string) (FileInfo, error)

示例:

// 获取文件信息
info, err := os.Stat("myfile.txt")
if err != nil {
    if os.IsNotExist(err) {
        fmt.Println("文件不存在")
    } else {
        log.Fatal(err)
    }
    return
}

fmt.Println("名称:", info.Name())
fmt.Println("大小:", info.Size(), "字节")
fmt.Println("权限:", info.Mode())
fmt.Println("修改时间:", info.ModTime())
fmt.Println("是目录:", info.IsDir())

读写文件

http://image.iswbm.com/20211228231043.png

io包

由于os.File实现了io.Readerio.Writer接口,所以这里实际调用的是File中实现的Read()Write()方法

常用方法

//读,文件读取结束的标志是返回的 n 等于 0,并且err会返回io.EOF,单次读取的大小和b的大小一样
func (f *File) Read(b []byte) (n int, err error) 
//写,以字节数组写入
func (f *File) Write(b []byte) (n int, err error)
//写,以字符串写入
func (f *File) WriteString(s string) (n int, err error)
//复制文件
io.Copy(dst io.Writer, src io.Reader) (written int64, err error)

func (f *File) Read(b []byte) (n int, err error),其中b为保存读到数据的字节切片,n为读到的字节长度,如果读到文件末尾,方法会返回0,io.EOF

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()
	//定义切片存放每次读取字节
	data := make([]byte, 1024)
	//声明切片,将每次读到的数据存放起来
	var result []byte
	for {
		i, err := file.Read(data)
		if err != nil && err != io.EOF {
			fmt.Println(err)
			break
		}
		if i == 0 {
			break
		}
		result = append(result, data[:i]...)
	}
	fmt.Println(string(result))
}

func (f *File) Write(b []byte) (n int, err error)

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0777)
	if err != nil {
		fmt.Println(err)
	}
    defer file.Close()
	s := "hello world\nhello golang"
	//写入文件,将字符串转字节数组,也可以直接使用WriteString
	n, err := file.Write([]byte(s))
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("write success,byte count =", n)
	}
}

复制

func Copy(dst Writer, src Reader) (written int64, err error),此处注意,由于File已经实现了Writer、Reader接口,所以可以直接传入File实例

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Println(err)
	}
    defer file.Close()
	fileCp, err := os.OpenFile("./testCp.txt", os.O_CREATE|os.O_WRONLY, 0777)
	if err != nil {
		fmt.Println(err)
	}
    defer fileCp.Close()
	n, err := io.Copy(fileCp, file)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("copy success,n =", n)
}

ioutil包

**注意:**此包内的方法,在Go 1.16后,被移动到了ioos包内。

常用方法

//读,由于这两个方法都是一次读取文件全部数据,所以不适合大文件,该方法在 Go1.16 被移动到 os 包
ioutil.ReadFile(filename string) ([]byte, error) 
//读,这个方法需要打开一个文件,并将文件句柄传入参数
ioutil.ReadAll(r io.Reader) ([]byte, error) 
//读取单层目录下所有文件(夹),返回FileInfo切片
ioutil.ReadDir(dirname string) ([]fs.FileInfo, error) 
//写,文件不存在则创建文件,存在则清空文件,FileMode可以直接用0666或者0777
ioutil.WriteFile(filename string, data []byte, perm os.FileMode) 

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	data, err := ioutil.ReadFile("./test.txt")
	if err != nil {
		fmt.Println(err)
	}
	//将字节数组转为string类型
	fmt.Println(string(data))
}

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	s := "hello world\nhello golang"
	err := ioutil.WriteFile("./test.txt", []byte(s), 0777)
	if err != nil {
		fmt.Println(err)
	}
}

bufio包

bufio 包实现了缓存IO。它包装了 io.Readerio.Writer 对象,创建了另外的Reader和Writer对象,它们也实现了 io.Readerio.Writer 接口,不过它们是有缓存的。

io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件。所以bufio就提供了缓冲区(分配一块内存),读和写都先在缓冲区中,最后再读写文件,来降低访问本地磁盘的次数,从而提高效率。

缓冲区原理

常用方法

bufio.Reader
//NewReader 函数用来返回一个默认大小 buffer 的 Reader 对象(默认大小是 4096)
func NewReader(rd io.Reader) *Reader
//NewReaderSize 返回一个指定大小 buffer(size 最小为 16)的 Reader 对象,如果 io.Reader 参数已经是一个足够大的 Reader,它将返回该 Reader
func NewReaderSize(rd io.Reader, size int) *Reader
//Peek 返回缓存的一个切片,该切片引用缓存中前 n 个字节的数据,该操作不会将数据读出,只是引用
func (b *Reader) Peek(n int) ([]byte, error)
//Read 从 b 中读出数据到 p 中,返回读出的字节数和遇到的错误
func (b *Reader) Read(p []byte) (n int, err error)
//Buffered 返回缓存中未读取的数据的长度
func (b *Reader) Buffered() int
//ReadSlice 从输入中读取,直到遇到第一个界定符(delim)为止,返回一个指向缓存(也就意味着随着缓存改变而改变,所以一般用ReadBytes和ReadString)中字节的 slice,在下次调用读操作(read)时,这些字节会无效,如果在找到界定符之前缓存已经满了,ReadSlice 会返回 bufio.ErrBufferFull 错误
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
//ReadBytes 和ReadSlice区别就是返回的是当前缓存中字节slice的一个拷贝
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
//ReadString 和ReadBytes一样,只不过返回的是字符串,底层调用的是ReadBytes
func (b *Reader) ReadString(delim byte) (string, error)
//ReadLine 逐行读取,底层调用的是ReadSlice('\n')
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
//读取单个UTF-8编码的Unicode字符,并返回字符及其字节大小
func (b *Reader) ReadRune() (r rune, size int, err error) 
bufio.Writer
//返回默认缓冲大小的 Writer 对象(默认是4096),关闭文件前一定要Flush,将缓冲区的数据写入w
func NewWriter(w io.Writer) *Writer
//指定缓冲大小创建一个 Writer 对象,关闭文件前一定要Flush,将缓冲区的数据写入w
func NewWriterSize(w io.Writer, size int) *Writer
//写入单个字节,直接写入文件
func (b *Writer) WriteByte(c byte) error
//写入单个 Unicode 指针返回写入字节数错误信息,直接写入文件
func (b *Writer) WriteRune(r rune) (size int, err error)
//写入字符串并返回写入字节数和错误信息,直接写入文件
func (b *Writer) WriteString(s string) (int, error)
bufio.Scanner
// 创建一个用于逐个读取输入缓冲区的扫描器
func NewScanner(r io.Reader) *Scanner
// 用于读取输入缓冲区中的下一个数据块,并将其保存在内部的缓冲区中。如果读取成功,则返回 true;如果已经读取了所有数据或者发生了错误,则返回 false。
func (s *Scanner) Scan() bool
// 用于获取内部缓冲区中的文本内容,通常与 Scan() 方法一起使用,用于获取读取的数据。
func (s *Scanner) Text() string
// 用于获取内部缓冲区中的字节内容,通常与 Scan() 方法一起使用,用于获取读取的数据。
func (s *Scanner) Bytes() []byte
// 用于获取在读取输入时发生的错误信息,如果读取过程中没有发生错误,则返回 nil;否则,返回一个非 nil 的错误对象。
func (s *Scanner) Err() error
// 用于自定义输入缓冲区大小,接受一个 []byte 类型的参数,用于指定缓冲区的大小。
func (s *Scanner) Buffer(buf []byte, max int)
// 用于指定一个分割函数,将输入分割成多个数据块,接受一个 func([]byte) bool 类型的参数,该函数在每次读取输入时被调用,用于判断是否需要将当前数据块分割成多个小块。通常用于处理非常大的数据块,以避免内存溢出等问题。
func (s *Scanner) Split(split SplitFunc)

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()
	fileBuf := bufio.NewReader(file)
	data := make([]byte, 1024)
	var result []byte
	for {
		i, err := fileBuf.Read(data)
		if err != nil && err != io.EOF {
			fmt.Println(err)
			break
		}
		if i == 0 {
			break
		}
		result = append(result, data[:i]...)
	}
	fmt.Println(string(result))
}
逐字读取utf8字符
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("./test.txt", os.O_RDWR, 777)
	if err != nil {
		fmt.Errorf("%+v", err)
	}
	reader := bufio.NewReader(file)
	r, i, err := reader.ReadRune()
	for i > 0 && err == nil {
		fmt.Print(string(r))
		r, i, err = reader.ReadRune()
	}

	file.Close()
}
逐行读
file, err := os.Open("data.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
    line := scanner.Text()  // 获取当前行文本
    // 或者使用scanner.Bytes()获取[]byte
    fmt.Println(line)
}

if err := scanner.Err(); err != nil {
    log.Fatal(err)
}
自定义分割函数

Scanner默认按行分割,但可以自定义分割方式,内置的分割函数:

file, err := os.Open("words.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
// 使用单词分割函数
scanner.Split(bufio.ScanWords)

wordCount := 0
for scanner.Scan() {
    word := scanner.Text()
    wordCount++
    fmt.Printf("单词%d: %s\n", wordCount, word)
}

if err := scanner.Err(); err != nil {
    log.Fatal(err)
}

注意:关闭文件前一定要Flush()

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("./test.txt", os.O_WRONLY|os.O_CREATE, 0777)
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()
	fileBuf := bufio.NewWriter(file)
	s := "hello world\nhello golang"
	fileBuf.Write([]byte(s))
	//关闭文件前一定要fulsh,将缓冲区内的数据写入文件
	fileBuf.Flush()
}

常见读取文件示例

一次性读取整个文件到内存(适合小文件)

// Go 1.16前
data, err := ioutil.ReadFile("input.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

// Go 1.16+
data, err := os.ReadFile("input.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

使用缓冲区分块读取(适合大文件)

file, err := os.Open("largefile.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

buffer := make([]byte, 1024) // 1KB缓冲区
for {
    bytesRead, err := file.Read(buffer)
    if err != nil {
        if err != io.EOF {
            log.Fatal(err)
        }
        break // 读取完毕
    }
    
    // 处理读取的数据
    fmt.Print(string(buffer[:bytesRead]))
}

使用bufio逐行读取(文本文件)

file, err := os.Open("lines.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
// 设置每行最大长度(可选,默认64K)
// scanner.Buffer(make([]byte, 1024), 1024*1024) // 缓冲区和最大容量

lineCount := 0
for scanner.Scan() {
    line := scanner.Text()
    lineCount++
    fmt.Printf("第%d行: %s\n", lineCount, line)
}

if err := scanner.Err(); err != nil {
    log.Fatal(err)
}

读取特定位置的数据

file, err := os.Open("data.bin")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

// 移动到文件第100个字节
_, err = file.Seek(100, 0)
if err != nil {
    log.Fatal(err)
}

// 读取10个字节
data := make([]byte, 10)
count, err := file.Read(data)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("读取了%d字节: %v\n", count, data)

常见写入文件示例

一次性写入(适合小数据)

// Go 1.16前
data := []byte("Hello, 世界!")
err := ioutil.WriteFile("output.txt", data, 0666)
if err != nil {
    log.Fatal(err)
}

// Go 1.16+
data := []byte("Hello, 世界!")
err := os.WriteFile("output.txt", data, 0666)
if err != nil {
    log.Fatal(err)
}

分块写入(适合大数据)

file, err := os.Create("output.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

// 多次写入
for i := 0; i < 5; i++ {
    data := []byte(fmt.Sprintf("第%d行数据\n", i+1))
    _, err := file.Write(data)
    if err != nil {
        log.Fatal(err)
    }
}

使用缓冲写入(提高性能)

file, err := os.Create("buffered.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

writer := bufio.NewWriter(file)

// 写入缓冲区
for i := 0; i < 1000; i++ {
    fmt.Fprintf(writer, "第%d行\n", i+1)
}

// 确保缓冲区内容写入文件
err = writer.Flush()
if err != nil {
    log.Fatal(err)
}

追加到文件

file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
    log.Fatal(err)
}
defer file.Close()

data := []byte("新的日志条目\n")
if _, err := file.Write(data); err != nil {
    log.Fatal(err)
}

目录操作

//创建
os.Mkdir(name string, perm FileMode) error //创建目录,只创建一层,相当于mkdir
os.MkdirAll(path string, perm FileMode) error //创建多层目录,相当于mkdir -p
os.Create(name string) (file *File, err error) //创建文件,如果存在则覆盖

//移动
os.Rename(oldpath, newpath string) error //重命名、移动文件

//删除
os.Remove(name string) error //删除单个
os.RemoveAll(path string) error //递归删除目录

创建和删除目录

// 创建单个目录
err := os.Mkdir("newdir", 0755)
if err != nil {
    log.Fatal(err)
}

// 创建多级目录
err = os.MkdirAll("path/to/nested/dir", 0755)
if err != nil {
    log.Fatal(err)
}

// 删除单个目录(必须为空)
err = os.Remove("emptydir")
if err != nil {
    log.Fatal(err)
}

// 删除目录及其内容
err = os.RemoveAll("dir-with-content")
if err != nil {
    log.Fatal(err)
}

遍历目录内容

列出单级目录内容

// 打开目录
dir, err := os.Open(".")
if err != nil {
    log.Fatal(err)
}
defer dir.Close()

// 读取目录中的条目
entries, err := dir.ReadDir(-1) // -1表示读取所有条目
if err != nil {
    log.Fatal(err)
}

// 遍历条目
for _, entry := range entries {
    fmt.Println("名称:", entry.Name())
    fmt.Println("是目录:", entry.IsDir())
    info, err := entry.Info()
    if err == nil {
        fmt.Println("  大小:", info.Size())
        fmt.Println("  修改时间:", info.ModTime())
        fmt.Println("  权限:", info.Mode())
    }
    fmt.Println()
}

递归遍历目录

// 使用filepath.Walk遍历目录及其子目录
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    if err != nil {
        fmt.Printf("访问 %s 错误: %v\n", path, err)
        return err
    }
    
    fmt.Printf("%-50s | %-10d | %s\n", path, info.Size(), info.Mode())
    return nil
})

if err != nil {
    log.Fatal(err)
}

WalkDir

Go 1.16 新增。

// 使用更高效的filepath.WalkDir
err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if err != nil {
        fmt.Printf("访问 %s 错误: %v\n", path, err)
        return err
    }
    
    // 只处理常规文件
    if !d.IsDir() {
        fmt.Printf("文件: %s\n", path)
    }
    return nil
})

if err != nil {
    log.Fatal(err)
}

临时文件和目录

在开发过程中,经常需要创建临时文件和目录来存储中间数据或缓存信息。Go语言的os包提供了一些函数用于创建和管理临时文件和目录。

Go 1.16 之前在ioutil包中。

创建临时文件

使用os.CreateTemp函数可以创建一个临时文件。该函数会在指定目录下创建一个具有唯一名称的临时文件,并返回该文件的文件对象和路径。

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建临时文件
    file, err := os.CreateTemp("", "example_*.txt")
    if err != nil {
        fmt.Println("Error creating temporary file:", err)
        return
    }
    defer os.Remove(file.Name()) // 在使用完后删除临时文件
    defer file.Close()

    fmt.Println("Temporary file created:", file.Name())

    // 向临时文件写入数据
    _, err = file.WriteString("Hello, Temporary File!")
    if err != nil {
        fmt.Println("Error writing to temporary file:", err)
        return
    }

    fmt.Println("Data written to temporary file successfully")
}

上述代码会在系统默认的临时文件目录下创建一个临时文件,并写入一些数据。临时文件的名称具有唯一性,并包含example_前缀。

创建临时目录

使用os.MkdirTemp函数可以创建一个临时目录。该函数会在指定目录下创建一个具有唯一名称的临时目录,并返回该目录的路径。

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建临时目录
    dir, err := os.MkdirTemp("", "exampledir_*")
    if err != nil {
        fmt.Println("Error creating temporary directory:", err)
        return
    }
    defer os.RemoveAll(dir) // 在使用完后删除临时目录

    fmt.Println("Temporary directory created:", dir)

    // 在临时目录中创建一个文件
    tempFile := fmt.Sprintf("%s/%s", dir, "tempfile.txt")
    file, err := os.Create(tempFile)
    if err != nil {
        fmt.Println("Error creating file in temporary directory:", err)
        return
    }
    defer file.Close()

    // 向文件中写入数据
    _, err = file.WriteString("Hello, Temporary Directory!")
    if err != nil {
        fmt.Println("Error writing to file in temporary directory:", err)
        return
    }

    fmt.Println("Data written to file in temporary directory successfully")
}

上述代码会在系统默认的临时文件目录下创建一个临时目录,并在该目录中创建一个文件并写入一些数据。

清理临时文件与目录

在创建临时文件和目录后,记得在使用完毕后进行清理。可以使用os.Removeos.RemoveAll函数分别删除单个文件和目录树:

package main

import (
    "fmt"
    "os"
)

func main() {
    // 创建临时文件
    file, err := os.CreateTemp("", "example_*.txt")
    if err != nil {
        fmt.Println("Error creating temporary file:", err)
        return
    }
    defer file.Close()

    // 创建临时目录
    dir, err := os.MkdirTemp("", "exampledir_*")
    if err != nil {
        fmt.Println("Error creating temporary directory:", err)
        return
    }

    // 清理临时文件
    err = os.Remove(file.Name())
    if err != nil {
        fmt.Println("Error removing temporary file:", err)
        return
    }
    fmt.Println("Temporary file removed:", file.Name())

    // 清理临时目录
    err = os.RemoveAll(dir)
    if err != nil {
        fmt.Println("Error removing temporary directory:", err)
        return
    }
    fmt.Println("Temporary directory removed:", dir)
}

上述代码会创建一个临时文件和一个临时目录,并在使用完后将其删除。

终端操作

package main

import "os"

func main() {
    var buf [16]byte
    os.Stdin.Read(buf[:])
    os.Stdin.WriteString(string(buf[:]))
}

路径解析

路径相关的函数有两个包,pathpath/filepath,两个包内有一些相同的函数,如IsAbs()Join()Dir(),两个包的区别在于:

跨平台支持:‌filepath包‌:自动根据不同的操作系统进行路径转换,确保跨平台的兼容性。例如,在Windows系统中,路径分隔符是反斜杠(\),而在Unix和Linux系统中是正斜杠(/)。filepath包能够处理这些差异,使得代码在不同操作系统上都能正常运行。

path

// 路径拼接
func Join(elem ...string) string 
// 判断文件是否是绝对路径
func IsAbs(path string) bool 
// 获取目录
func Dir(path string) string 
// 获取文件拓展名
func Ext(path string) string

filepath

// 获取targpath相对于basepath的相对路径
func Rel(basepath, targpath string) (string, error) 
// 获取绝对路径,如果path不是绝对路径,会加入当前工作目录以使之成为绝对路径。
func Abs(path string) (string, error)
// 获取路径中的最后一个元素
func Base(path string) string 
// 获取路径中除去最后一个元素后前面的部分
func Dir(path string) string 
// 获取文件拓展名
func Ext(path string) string
// 路径拼接
func Join(elem ...string) string
// 将路径拆分为目录和文件名两部分
func Split(path string) (dir, file string)