Table of Contents
Basic operation
File creation
Home Backend Development Golang About the organization of Golang file operations

About the organization of Golang file operations

Jun 28, 2021 pm 03:46 PM
golang

Some things I have done recently have used a lot of content related to file operations in golang, such as creation, deletion, traversal, compression, etc. I will sort them out here. I hope I can master a little bit about the system and put them into practice. Clarify the ambiguities.

Basic operation

File creation

When creating a file, you must pay attention to permission issues. Generally, the default file permission is 0666. About permissions Content, please refer to Uncle Bird p141 for details. Let’s review again. The file attributes r w x r w x r w #os.CreateIt seems that only the form 0xxx can be used when creating a file. For example, 0666 means that a normal file has been created. The permissions of the file owner, the permissions of the user group to which the file belongs, and the permissions of other people on the file are all 110, which means it can be read, written, and not executable.

File deletion

When deleting a file, whether it is an ordinary file or a directory file, you can use the operation

err:=os.Remove(filename) to perform it . Of course, if you want to remove the entire folder, just use the RemoveAll(path string) operation. You can take a look at the internal implementation of the RemoveAll function. The overall operation process is traversal and recursive. Other similar file operations can be implemented using similar templates. The following uses the RemoveAll function as a template to conduct a specific analysis. Pay attention to the following. Various situations:

func RemoveAll(path string) error {
// Simple case: if Remove works, we're done.
//先尝试一下remove如果是普通文件 直接删掉 报错 则可能是目录中还有子文件
err := Remove(path)
//没错或者路径不存在 直接返回 nil
if err == nil || IsNotExist(err) {
    return nil
}

// Otherwise, is this a directory we need to recurse into?
// 目录里面还有文件 需要递归处理
// 注意Lstat和stat函数的区别,两个都是返回文件的状态信息
//Lstat多了处理Link文件的功能,会返回Linked文件的信息,而state直接返回的是Link文件所指向的文件的信息
dir, serr := Lstat(path)
if serr != nil {
    if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
        return nil
    }
    return serr
}
//不是目录
if !dir.IsDir() {
    // Not a directory; return the error from Remove.
    return err
}

// Directory.
fd, err := Open(path)
if err != nil {
    if IsNotExist(err) {
        // Race. It was deleted between the Lstat and Open.
        // Return nil per RemoveAll's docs.
        return nil
    }
    return err
}

// Remove contents & return first error.
err = nil
//递归遍历目录中的文件 如果参数n<=0则将全部的信息存入到一个slice中返回
//如果参数n>0则至多返回n个元素的信息存入到slice当中
//还有一个类似的函数是Readdir 这个返回的是 目录中的内容的Fileinfo信息

for {
    names, err1 := fd.Readdirnames(100)
    for _, name := range names {
        err1 := RemoveAll(path + string(PathSeparator) + name)
        if err == nil {
            err = err1
        }
    }
    //遍历到最后一个位置
    if err1 == io.EOF {
        break
    }
    // If Readdirnames returned an error, use it.
    if err == nil {
        err = err1
    }
    if len(names) == 0 {
        break
    }
}

// Close directory, because windows won&#39;t remove opened directory.
fd.Close()
//递归结束 当前目录下位空 删除当前目录
// Remove directory.
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
    return nil
}
if err == nil {
    err = err1
}
return err
}
Copy after login

File status

Writing content from the file

This part involves more I/O related operations, and the system introduction is here In the I/O part, there are generally three ways to read and write content to files:

1. When using

f, err := os.Open(file_path) After opening the file, use f.read() f.write() combined with the custom buffer to read/read fixed content from the file each time

2. Use ioutl readFile and writeFile methods

3. Use bufio to read and write with cache. For example, through

info:=bufio.NewReader(f), implement the io.Reader interface. After the instance is loaded, you can use info.ReadLine() to read a whole line at a time until the err information is io.EOF, and the reading ends.

This blog operates on three types of files After comparing the reading speed, it seems that ioutil is more efficient when reading large files.

Each method has different applicable situations. The following are examples of reading operations in three ways. For the operation of writing files, you can refer to the reading operation:

package main

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

func check(e error) {
if e != nil {
    panic(e)
}
}

func main() {
//查看当前的工作目录路径 得到测试文件的绝对路径
current_dir, _ := os.Getwd()
fmt.Println(current_dir)
file_path := current_dir + "/temp.txt"

//方式一:
//通过ioutil直接通过文件名来加载文件
//一次将整个文件加载进来 粒度较大 err返回为nil的时候 文件会被成功加载
dat, err := ioutil.ReadFile(file_path)
//若加载的是一个目录 会返回[]os.FileInfo的信息
//ioutil.ReadDir()
check(err)
//the type of data is []uint
fmt.Println(dat)
//将文件内容转化为string输出
fmt.Println(string(dat))

//方式二:
//通过os.Open的方式得到 *File 类型的变量
//貌似是一个指向这个文件的指针 通过这个指针 可以对文件进行更细粒度的操作
f, err := os.Open(file_path)
check(err)
//手工指定固定大小的buffer 每次通过buffer来 进行对应的操作
buffer1 := make([]byte, 5)
//从文件f中读取len(buffer1)的信息到buffer1中 返回值n1是读取的byte的长度
n1, err := f.Read(buffer1)
check(err)
fmt.Printf("%d bytes: %s\n", n1, string(buffer1))

//通过f.seek进行更精细的操作 第一个参数表示offset为6 第二个参数表示文件起始的相对位置
//之后再读就从o2位置开始往后读信息了
o2, err := f.Seek(6, 0)
check(err)
buffer2 := make([]byte, 2)
//读入了n2长度的信息到buffer2中
n2, err := f.Read(buffer2)
check(err)
fmt.Printf("%d bytes after %d position : %s\n", n2, o2, string(buffer2))

//通过io包种的函数 也可以实现类似的功能
o3, err := f.Seek(6, 0)
check(err)
buffer3 := make([]byte, 2)
n3, err := io.ReadAtLeast(f, buffer3, len(buffer3))
check(err)
fmt.Printf("%d bytes after %d position : %s\n", n3, o3, string(buffer3))

//方式三
//通过bufio包来进行读取 bufio中又许多比较有用的函数 比如一次读入一整行的内容

//调整文件指针的起始位置到最开始的地方
_, err = f.Seek(10, 0)
check(err)
r4 := bufio.NewReader(f)

//读出从头开始的5个字节
b4, err := r4.Peek(5)
check(err)
//fmt.Println(string(b4))
fmt.Printf("5 bytes : %s\n", string(b4))

//调整文件到另一个地方
_, err = f.Seek(0, 0)
check(err)
r5 := bufio.NewReader(f)
//读出从指针所指位置开始的5个字节
b5, err := r5.Peek(5)
check(err)
//fmt.Println(string(b4))
fmt.Printf("5 bytes : %s\n", string(b5))

//测试bufio的其他函数

for {
    //读出内容保存为string 每次读到以&#39;\n&#39;为标记的位置
    line, err := r5.ReadString(&#39;\n&#39;)
    fmt.Print(line)
    if err == io.EOF {
        break
    }
}
//ReadLine() ReadByte() 的用法都是类似 一般都是当err为io.EOF的时候
//读入内容就结束
//感觉实际用的时候 还是通过方式三比较好 粒度正合适 还有多种处理输入的方式

f.Close()

}
Copy after login

Advanced Operations

File packaging, file decompression, file traversal, these related operations can basically be performed by referring to the RemoveAll method, which is a recursive plus traversal method.

The following is an implementation of file compression:

//将文件夹中的内容打包成 .gz.tar 文件
package main

import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
)

//将fi文件的内容 写入到 dir 目录之下 压缩到tar文件之中
func Filecompress(tw *tar.Writer, dir string, fi os.FileInfo) {

//打开文件 open当中是 目录名称/文件名称 构成的组合
filename := dir + "/" + fi.Name()
fmt.Println("the last one:", filename)
fr, err := os.Open(filename)
fmt.Println(fr.Name())
if err != nil {
    panic(err)
}
defer fr.Close()

hdr, err := tar.FileInfoHeader(fi, "")

hdr.Name = fr.Name()
if err = tw.WriteHeader(hdr); err != nil {
    panic(err)
}
//bad way
//  //信息头部 生成tar文件的时候要先写入tar结构体
//  h := new(tar.Header)
//  //fmt.Println(reflect.TypeOf(h))

//  h.Name = fi.Name()
//  h.Size = fi.Size()
//  h.Mode = int64(fi.Mode())
//  h.ModTime = fi.ModTime()

//  //将信息头部的内容写入
//  err = tw.WriteHeader(h)
//  if err != nil {
//      panic(err)
//  }

//copy(dst Writer,src Reader)
_, err = io.Copy(tw, fr)
if err != nil {
    panic(err)
}
//打印文件名称
fmt.Println("add the file: " + fi.Name())

}

//将目录中的内容递归遍历 写入tar 文件中
func Dircompress(tw *tar.Writer, dir string) {
fmt.Println(dir)
//打开文件夹
dirhandle, err := os.Open(dir + "/")
//fmt.Println(dir.Name())
//fmt.Println(reflect.TypeOf(dir))
if err != nil {
    panic(err)
}
defer dirhandle.Close()

fis, err := dirhandle.Readdir(0)
//fis的类型为 []os.FileInfo

//也可以通过Readdirnames来读入所有子文件的名称
//但是这样 再次判断是否为文件的时候 需要通过Stat来得到文件的信息
//返回的就是os.File的类型

if err != nil {
    panic(err)
}

//遍历文件列表 每一个文件到要写入一个新的*tar.Header
//var fi os.FileInfo
for _, fi := range fis {
    fmt.Println(fi.Name())

    if fi.IsDir() {

        newname := dir + "/" + fi.Name()
        fmt.Println("using dir")
        fmt.Println(newname)
        //这个样直接continue就将所有文件写入到了一起 没有层级结构了
        //Filecompress(tw, dir, fi)
        Dircompress(tw, newname)

    } else {
        //如果是普通文件 直接写入 dir 后面已经有了 /
        Filecompress(tw, dir, fi)
    }

}

}

//在tardir目录中创建一个.tar.gz文件 存放压缩之后的文件
func Dirtotar(sourcedir string, tardir string, tarname string) {
//file write 在tardir目录下创建
fw, err := os.Create(tardir + "/" + tarname + ".tar.gz")
//type of fw is *os.File
//  fmt.Println(reflect.TypeOf(fw))
if err != nil {
    panic(err)

}
defer fw.Close()

//gzip writer
gw := gzip.NewWriter(fw)
defer gw.Close()

//tar write
tw := tar.NewWriter(gw)

fmt.Println("源目录:", sourcedir)
Dircompress(tw, sourcedir)

//通过控制写入流 也可以控制 目录结构 比如将当前目录下的Dockerfile文件单独写在最外层
fileinfo, err := os.Stat("tarrepo" + "/" + "testDockerfile")
fmt.Println("the file name:", fileinfo.Name())
if err != nil {
    panic(err)

}
//比如这里将Dockerfile放在 tar包中的最外层 会注册到tar包中的 /tarrepo/testDockerfile 中
Filecompress(tw, "tarrepo", fileinfo)
//Filecompress(tw, "systempdir/test_testwar_tar/", fileinfo)

fmt.Println("tar.gz packaging OK")

}

func main() {
//  workdir, _ := os.Getwd()
//  fmt.Println(workdir)
Dirtotar("testdir", "tarrepo", "testtar")

}
Copy after login

To add

You may not have noticed the difference between the OpenFile function and the Open function before. The Openfile function can specify the permissions of the returned file descriptor. Controlled through O_RDONLY, O_WRONLY, O_RDWR, etc. The Open function calls the OpenFile function internally. The default permission is O_RDONLY. If you only use the Open function to return the file descriptor and then write to the file, a bad file descriptor error will be returned. This should still be the case. Pay more attention and work out the details carefully. Essentially, it is a problem with the file descriptors in the OS.

Add file copy operation

refer to this:https://www.socketloop.com/tutorials/golang-copy-directory-including-sub-directories-files

Reprinted in: https://www.cnblogs.com/Goden/p/4533908.html

For more golang related technical articles, please visit

golang Tutorial column!

The above is the detailed content of About the organization of Golang file operations. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to safely read and write files using Golang? How to safely read and write files using Golang? Jun 06, 2024 pm 05:14 PM

Reading and writing files safely in Go is crucial. Guidelines include: Checking file permissions Closing files using defer Validating file paths Using context timeouts Following these guidelines ensures the security of your data and the robustness of your application.

How to configure connection pool for Golang database connection? How to configure connection pool for Golang database connection? Jun 06, 2024 am 11:21 AM

How to configure connection pooling for Go database connections? Use the DB type in the database/sql package to create a database connection; set MaxOpenConns to control the maximum number of concurrent connections; set MaxIdleConns to set the maximum number of idle connections; set ConnMaxLifetime to control the maximum life cycle of the connection.

How to save JSON data to database in Golang? How to save JSON data to database in Golang? Jun 06, 2024 am 11:24 AM

JSON data can be saved into a MySQL database by using the gjson library or the json.Unmarshal function. The gjson library provides convenience methods to parse JSON fields, and the json.Unmarshal function requires a target type pointer to unmarshal JSON data. Both methods require preparing SQL statements and performing insert operations to persist the data into the database.

Golang framework vs. Go framework: Comparison of internal architecture and external features Golang framework vs. Go framework: Comparison of internal architecture and external features Jun 06, 2024 pm 12:37 PM

The difference between the GoLang framework and the Go framework is reflected in the internal architecture and external features. The GoLang framework is based on the Go standard library and extends its functionality, while the Go framework consists of independent libraries to achieve specific purposes. The GoLang framework is more flexible and the Go framework is easier to use. The GoLang framework has a slight advantage in performance, and the Go framework is more scalable. Case: gin-gonic (Go framework) is used to build REST API, while Echo (GoLang framework) is used to build web applications.

Transforming from front-end to back-end development, is it more promising to learn Java or Golang? Transforming from front-end to back-end development, is it more promising to learn Java or Golang? Apr 02, 2025 am 09:12 AM

Backend learning path: The exploration journey from front-end to back-end As a back-end beginner who transforms from front-end development, you already have the foundation of nodejs,...

How to find the first substring matched by a Golang regular expression? How to find the first substring matched by a Golang regular expression? Jun 06, 2024 am 10:51 AM

The FindStringSubmatch function finds the first substring matched by a regular expression: the function returns a slice containing the matching substring, with the first element being the entire matched string and subsequent elements being individual substrings. Code example: regexp.FindStringSubmatch(text,pattern) returns a slice of matching substrings. Practical case: It can be used to match the domain name in the email address, for example: email:="user@example.com", pattern:=@([^\s]+)$ to get the domain name match[1].

Golang framework development practical tutorial: FAQs Golang framework development practical tutorial: FAQs Jun 06, 2024 am 11:02 AM

Go framework development FAQ: Framework selection: Depends on application requirements and developer preferences, such as Gin (API), Echo (extensible), Beego (ORM), Iris (performance). Installation and use: Use the gomod command to install, import the framework and use it. Database interaction: Use ORM libraries, such as gorm, to establish database connections and operations. Authentication and authorization: Use session management and authentication middleware such as gin-contrib/sessions. Practical case: Use the Gin framework to build a simple blog API that provides POST, GET and other functions.

How to use predefined time zone with Golang? How to use predefined time zone with Golang? Jun 06, 2024 pm 01:02 PM

Using predefined time zones in Go includes the following steps: Import the "time" package. Load a specific time zone through the LoadLocation function. Use the loaded time zone in operations such as creating Time objects, parsing time strings, and performing date and time conversions. Compare dates using different time zones to illustrate the application of the predefined time zone feature.

See all articles