1、操作目录
Go语言对文件和目录的操作,主要是通过os包和path包实现的。下面介绍os包和path包中的一些常用函数。
1.1、创建目录
Go语言创建目录,主要使用Mkdir()、MkdirAllO两个函数。其中,Mkdir()函数的定义如下:
func Mkdir (name string,perm FileMode) error
其中,name为目录名字,perm为权限设置码。比如perm为0777,表示该目录对所有用户可读写及可执行。
例如,创建一个名为“test”的目录,perm权限为0777的示例如下:
import (
"fmt"
"os"
)
func main() {
//创建一个名为"test"的目录,权限为0777
err := os.Mkdir("test", 0777)
if err != nil {
fmt.Println(err)
}
}
MkdirAll()函数的定义如下:
func MkdirAll(path string,perm FileMode) error
其中,path为目录的路径(例如“dir1/dir2/dir3)”,perm为权限设置码。
用MkdirAll(0函数创建目录的示例如下:
import (
"fmt"
"os"
)
func main() {
//根据path创建多级子目录,录入dir1/dir2/dir3
err := os.MkdirAll("dir1/dir2/dir2", 0777)
if err != nil {
fmt.Println(err)
}
}
在Wb开发中,多级目录使用得比较多的地方是上传文件。例如我们可以创建一个形如
“static/upload/2020/10/1”的多级目录来保存上传的文件。用MkdirAll0函数创建多级目录的示例如下。
import (
"fmt"
"os"
"time"
)
func main() {
uploadDir := "static/upload/" + time.Now().Format("2006/01/02/")
err := os.MkdirAll(uploadDir, 777)
if err != nil {
fmt.Println(err)
}
}
1.2、重命名目录
在Go语言的os包中有一个Rename()函数用来对目录和文件进行重命名。该函数也可以用于移动一个文件。该函数的定义如下:
func Rename (oldpath,.newpath string) error
其中,参数oldpath为旧的目录名或多级目录的路径,参数newpath为新目录的路径。如果newpath已经存在,则替换它。其使用示例如下。
import (
"log"
"os"
)
func main() {
//将test目录重命名为test1
oldName := "test"
newName := "test1"
err := os.Rename(oldName, newName)
if err != nil {
log.Fatal(err)
}
}
1.3、删除目录
Go语言删除目录的函数的定义如下:
func Remove (name string) error
其中,参数name为目录的名字。Remove()函数有一个局限性:当目录下有文件或者其他目录时会出错。
如果要删除多级子目,则可以使用RemoveAll()函数,其定义如下:
func RemoveAll(path string) error
其中,参数path为要删除的多级子目录。如果path是单个名称,则该目录下的子目录将全部被删除。用Remove()函数删除名为dir1的目录的示例如下。
import (
"log"
"os"
)
func main() {
err := os.RemoveAll("dir1")
if err != nil {
log.Fatal(err)
}
}
1.4、遍历目录
在Go语言的path/filepath包中,提供了Walk()函数来遍历目录,其定义如下:
func Walk(root string,walkFn WalkFunc) error
其中,参数root为遍历的初始根目录,参数walkFn为自定义函数(例如,显示所有文件夹、子文件夹、文件、子文件)。用WalK()函数遍历目录的示例如下。
import (
"fmt"
"os"
"path/filepath"
)
func main() {
//根据path创建多级子目录,例如dir1/dir2/dir3
err := os.MkdirAll("dir1/dir2/dir3", 0777)
if err != nil {
fmt.Println(err)
}
root := `./dir1`
err = filepath.Walk(root, scan)
fmt.Printf("filepath.Walk() returnned %v\n", err)
}
func scan(path string, f os.FileInfo, err error) error {
fmt.Printf("Scaned: %s\n", path)
return nil
}
Scaned: ./dir1
Scaned: dir1/dir2
Scaned: dir1/dir2/dir3
filepath.Walk() returnned <nil>
2、创建文件
Go语言os包中提供了Create()函数来创建文件,其定义如下:
func Create(name string)(*File,error)
其中,参数name为文件名字的字符串,返回值为指针型文件描述符。
用Create()函数创建一个名为name的文件,默认采用模式0666。如果文件已存在,则它会被重置为空文件。如果成功,则返回的文件描述符对象可用于文件的读写。其使用示例如下。
import (
"fmt"
"os"
)
func main() {
//创建文件
//Create()函数会根据传入的文件名创建文件,默认权限是0666
fp, err := os.Create("./demo.txt") //如果文件已经存在,则将文件清空
fmt.Println(fp, err)
fmt.Printf("%T", fp) //*os.File文件指针
if err != nil {
fmt.Println("文件创建失败")
//创建文件失败的原因有:
//1.路径不存在 2.权限不足 3.打开文件数量超过上限 4.磁盘空间不足等
return
}
}
3、打开与关闭文件
在Go语言的os包中提供了Open)函数和OpenFile()函数用来打开文件。在Open()、OpenFile()函数使用完毕后,都需要调用Close()方法来关闭文件。(go的后续版本File实现了Closer接口,可以自动关闭)
3.1、Open()函数
文件的打开使用os包中的Open()函数,其定义如下:
func Open (name string)(file *File,err Error)
其中参数name为文件名字的字符串,返回值为文件描述符对象。
文件关闭用Close()方法,其定义如下:
func (f *File)close()error
其中,参数f为文件描述符指针;Close()方法可使文件不能用于读写,它的返回值为可能出现的错误。Open()函数的使用示例如下。
import (
"fmt"
"os"
)
func main() {
//打开文件
file, err := os.Open("demo.txt")
if err != nil {
fmt.Printf("打开文件出错:%v\n", err)
}
fmt.Println(file)
//go1.19 File实现了Closer接口,可自动关闭
}
3.2、OpenFile()函数
OpenFile()函数比Open()函数更加强大,可以定义文件的名字、文件打开方式,以及文件权限设置。其定义如下:
func OpenFile(name string,flag int,perm uint32)(file *File,err Error)
其中,name为文件的名字,flag参数为打开的方式(可以是只读、读写等),perm是权限模式,形如0777。其使用示例如下。
import (
"fmt"
"os"
)
func main() {
//以读写方式打开文件
fp, err := os.OpenFile("./demo.txt", os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Println("文件打开失败!")
return
}
fmt.Println(fp)
}
4、读写文件
4.1、读文件
4.1.1、用带缓冲方式读取
这种方式使用bufio包中的NewReader()函数。其定义如下:
func NewReader(rd io.Reader)*Reader
该函数的使用示例如下。
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
//打开文件
file, err := os.Open("a.txt")
if err != nil {
fmt.Println("打开文件出错:", err)
}
reader := bufio.NewReader(file)
//循环读取文件的内容
for {
line, err := reader.ReadString('\n') //读到一个换行符就结束
if err == io.EOF {
break
}
fmt.Print(line)
}
}
4.1.2、直接读取到内存
如果想将文件直接读取到内存中,则可使用io/ioutil包中的ReadFile()函数,其定义如下:
func ReadFile(filename string)([]byte,error)
其中参数filename为文件名。ReadFile()函数的使用示例如下。
import (
"fmt"
"io/ioutil"
)
func main() {
//用io/ioutil.ReadFile()函数一次性将文件读取到内存中
filePath := "a.txt"
//content, err := os.ReadFile(filePath) 1.16版本后,改用os.ReadFile()函数
content, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println("读取文件出错:", err)
}
fmt.Printf("%v\n", content)
fmt.Printf("%v\n", string(content))
}
4.2、写文件
Go语言中os包中提供了一个名为File的对象来处理文件,该对象有Write()、WriteAt()、WriteString03种方法可以用于写文件。
4.2.1、Write()方法
Write()方法用于写入[]byte类型的信息到文件中,其定义如下:
func (file *File)Write(b []byte)(n int,err Error)
其使用示例如下。
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("a.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
fmt.Println(err)
}
content := []byte("你好!")
if _, err = file.Write(content); err != nil {
fmt.Println(err)
}
fmt.Println("写入成功")
}
4.2.2、WriteAt()方法
WriteAt()方法用于在指定位置开始写入[]byte类型的信息,其定义如下:
func (file *File)WriteAt (b []byte,off int64)(n int,err Error)
该方法表示从基本输入源的偏移量off处开始,将Ien§个字节读取到p中。它返回读取的字节数n(0≤n≤len§,以及任何遇到的错误。其使用示例如下。
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("writeAt.txt")
if err != nil {
fmt.Println(err)
}
file.WriteString("kldsfjsdlkfjsdklf")
n, err := file.WriteAt([]byte("hello world"), 10)
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
}
4.2.3、WriteString()方法
WriteString()方法用于将字符串写入文件,其定义如下:
func (file *File)Writestring(s string)(ret int,err Error)
其中参数s为string类型的字符串。该方法的使用示例如下。
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("writeString.txt")
if err != nil {
fmt.Println(err)
}
file.WriteString("hello!")
}
WriteString()方法的本质上是对Write()方法的调用。WriteString()方法的返回值就是Write()方法的返回值。
WriteString()的方法体如下:
func (f *File)Writestring(s string)(n int,err error){
return f.Write([]byte(s))
}
WriteString()方法和Write()方法的区别是参数的形式:WriteString()方法的参数是字符串,
Write()方法的参数是[]byte(s)。WriteString()方法和Write()方法的使用示例如下。
import (
"fmt"
"os"
)
func main() {
fout, err := os.Create("./write4.txt")
if err != nil {
fmt.Println(err)
}
for i := 0; i < 5; i++ {
outstr := fmt.Sprintf("%s:%d\r\n", "hello go", i)
//写入文件
fout.WriteString(outstr) //stinrg
fout.Write([]byte(outstr))
}
}
5、移动与重命名文件
Go语言的移动和重命名可以通过Rename()函数实现,其参数既可以是目录,也可以是文件。其定义如下:
func Rename (oldpath,newpath string) error
其中,参数oldpath为旧的目录或文件,参数newpath为移动与重命名后的目录或文件。
Rename()函数的使用示例如下。
import (
"fmt"
"os"
)
func main() {
_, err := os.Create("./rename.txt") //如果文件已经存在,则清空文件
if err != nil {
fmt.Println(err)
}
//创建一个名为rename的目录,权限为0777
err = os.Mkdir("rename", 0777)
//将rename.txt移动到rename文件夹下,并将其重命名为rename_new.txt
err = os.Rename("./rename.txt", "./rename/rename_new.txt")
if err != nil {
fmt.Println(err)
}
}
6、删除文件
和删除目录一样,在Go语言中删除文件也可以通过Remove()函数和RemoveAll()函数来实现。
6.1、Remove()函数
Remove()函数用于删除指定的文件或目录。如果出错,则返回*PathError类型的错误。其定义如下:
func.Remove (name string) error
6.2、RemoveAll()函数
RemoveAll()函数用于删除指定的文件或目录及它的所有下级对象。它会尝试删除所有内容,除非遇到错误并返回。如果参数path指定的对象不存在,则RemoveAll()函数会返回nil,而不返回错误。其定义如下:
func RemoveAll(path string) error
用Remove()函数及RemoveAll)(函数删除文件的示例如下:
func main() {
//创建test_rename目录
err := os.Mkdir("test_remove", 0777)
if err != nil {
fmt.Println(err)
}
fmt.Println("created dir:test_remove")
//创建test_remove.txt
_, err = os.Create("./test_remove/test_remove1.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println("created file:test_remove1.txt")
_, err = os.Create("./test_remove/test_remove2.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println("created file:test_remove2.txt")
_, err = os.Create("./test_remove/test_remove3.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println("created file:test_remove3.txt")
err = os.RemoveAll("./test_remove")
if err != nil {
fmt.Println("remove all fail:", err)
}
fmt.Println("removed all files:./test_remove")
}
7、复制文件
在Go语言中,可以使用io包的Copy()函数来实现文件复制功能。其定义如下:
func Copy(dst Writer,src Reader)(written int64,err error)
其中,参数dst为源文件指针,参数srC为目标文件指针。
import (
"fmt"
"io"
"os"
)
func main() {
_, err := os.Create("./test_copy1.zip")
if err != nil {
fmt.Println(err)
}
//打开文件,获取文件指针
srcFile, err := os.Open("./test_copy1.zip")
if err != nil {
fmt.Println(err)
}
//打开目的文件,获取文件指针
dstFile, err := os.OpenFile("./test_copy2.zip", os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
fmt.Println(err)
}
//通过copy()复制文件
result, err := io.Copy(dstFile, srcFile)
if err == nil {
fmt.Println("复制成功:", result)
}
}
除此之外,我们还可以自己封装一个函数:先通过使用os包中的os.Open()和os.Create()函数获取文件句柄(文件指针),然后通过文件句柄(文件指针)的Read()和Write()方法,按照字的节读取和写入来实现复制文件的功能。
在项目开发中,可以把复制文件封装成一个公共函数,以便在以后每次需要用到该功能时直接调用封装好的函数。对于较大文件,可以自定义一个名为DoCopy()的函数,示例如下。
import (
"fmt"
"io"
"log"
"os"
)
func DoCopy(srcFileName string, dstFileName string) {
srcFile, err := os.Open(srcFileName)
if err != nil {
log.Fatalf("源文件获取失败,err:%v\n", err)
}
distFile, err := os.Create(dstFileName)
if err != nil {
log.Fatalf("目标文件创建失败,err:%v\n", err)
}
//定义指定长度的字节切片,每次最多读取指定长度
var tmp = make([]byte, 1024*4)
//循环读取并写入
for {
n, err := srcFile.Read(tmp)
n, _ = distFile.Write(tmp[:n])
if err != nil {
if err == io.EOF {
return
} else {
log.Fatalf("赋值过程中发生错误,err:%v\n", err)
}
}
}
}
func main() {
_, err := os.Create("./test.zip")
if err != nil {
fmt.Println(err)
}
DoCopy("./test.zip", "./test2.zip")
}
8、修改文件权限
8.1、Linux中的文件权限
8.1.1、Liux中的文件权限有以下设定
- 文件的权限类型一般包括读、写、执行(对应字母为r、w、×)。
- 权限的属组有拥有者、群组、其他组这3种。每个文件都可以针对这3个属组(粒度),设置不同的r、w、x(读、写、执行)权限。
- 通常情况下,一个文件只能归属于一个用户和组。如果其他的用户想具有这个文件的权限,则可以将该用户加入具备权限的群组。一个用户可以同时归属于多个组。
8.1.2、十位二进制表示法
在Liux中,常用十位二进制表示法来表示一个文件的权限,形式如下:
-rwxrwxrwx (777)
以上权限表示所有用户(拥有者、所在群组的用户、其他组的用户)都有这个文件的读、写、执行权限。
①在十位二进制表示法中,第1位表示的是文件的类型,类型可以是下面几个中的一个:
- d:目录(directory);
- -:文件(regular file);
- s:套字文件(socket);
- p:管道文件(pipe)或命名管道文件(named pipe);
- l:符号链接文件(symbolic link);
- b:该文件是面向块的设备文件(block-oriented device file):
- c:该文件是面向字符的设备文件(character-oriented device file)。
②在十位二进制表示法中,后9位每个位置的意义(代表某个属组的某个权限)都是固定的。如果我们将各个位置权限的有无用二进制数1和0来代替,则只读、只写、只执行权限可以用3位二进制数表示:
r-- = 100
-w-= 010
--x = 001
--- = 000
转换成八进制数,则为=4,w=2,x=1,-=0(这也就是在用数字设置权限时,为何4代表读,2代表写,1代表执行)。
可以将所有的权限用二进制形式表现出来,并进一步转变成八进制数字:
rwx = 111 = 7
rw- = 110 = 6
r-x = 101 = 5
r-- = 100 = 4
-wx = 011 = 3
-w- = 101 = 2
--x = 001 = 1
--- = 000 = 0
由上可以看出,每个属组的所有的权限都可以用1位八进制数表示,每个数字都代表不同的权限(权值)。如最高的权限为是7,则代表可读、可写、可执行。
8.2、修改文件权限
在Go语言中,可使用os.Chmod()方法来修改文件的权限。该方法是对操作系统权限控制的一种封装,其定义如下:
func (f *File)Chmod(mode FileMode)error
其中参数f为文件指针。如果出错,则返回底层错误类型*PathError。用Chmod()方法修改文件权限的示例如下。
import (
"fmt"
"os"
)
func main() {
_, err := os.Create("./chmod.txt")
if err != nil {
fmt.Println(err)
}
fileInfo, err := os.Stat("./chmod.txt")
fileMode := fileInfo.Mode()
fmt.Println(fileMode) //-rw-r--r--
//重新赋值权限
os.Chmod("./chmod.txt", 0777)
fileInfo, err = os.Stat("./chmod.txt")
fileMode = fileInfo.Mode()
fmt.Println(fileMode)//-rwxrwxrwx
}
9、文件链接
9.1、硬链接
Go语言支持生成文件的软链接和硬链接。生成硬链接使用Lik(0函数,在Go1.4以后版本中,增加了对本地Windows系统中硬链接的支持。其定义如下:
func Link(oldname,newname string)error
其中,参数oldname为l旧文件名字,参数newname为新文件名字。
9.2、软链接
Go语言中,生成软链接使用Symlink(0函数。其定义如下:
func Symlink(oldname,newname string) error