Maison > développement back-end > Golang > À propos de l'organisation des opérations sur les fichiers Golang

À propos de l'organisation des opérations sur les fichiers Golang

藏色散人
Libérer: 2021-06-28 15:53:10
avant
2357 Les gens l'ont consulté

Certaines choses que j'ai faites récemment ont utilisé beaucoup de contenu lié aux opérations sur les fichiers dans Golang, telles que la création, la suppression, le parcours, la compression, etc. peut maîtriser un peu le système et les mettre en pratique. Clarifier les ambiguïtés.

Opérations de base

Création de fichiers

Lors de la création d'un fichier, vous devez faire attention aux problèmes d'autorisation. Généralement, l'autorisation de fichier par défaut est 0666. Concernant les autorisations Contenu, veuillez vous référer à Oncle Bird p141 pour plus de détails. Revoyons les attributs du fichier r w x r w x r w > Il semble que seul le formulaire 0xxx puisse être utilisé lors de la création d'un fichier. Par exemple, 0666 signifie qu'un fichier normal a été créé. Les autorisations du propriétaire du fichier, les autorisations du groupe d'utilisateurs auquel le fichier appartient et les autorisations des autres personnes sur le fichier sont toutes de 110, ce qui signifie qu'il peut être. lu, écrit et non exécutable. os.Create

Suppression de fichier

Lors de la suppression d'un fichier, qu'il s'agisse d'un fichier ordinaire ou d'un fichier de répertoire, vous pouvez utiliser l'opération comme

pour l'effectuer. Bien sûr, si vous souhaitez supprimer l'intégralité du dossier, utilisez simplement l'opération err:=os.Remove(filename). Vous pouvez jeter un œil à l'implémentation interne de la fonction RemoveAll. Le processus d'opération global est traversant et récursif. D'autres opérations de fichiers similaires peuvent être implémentées à l'aide de modèles similaires. Ce qui suit utilise la fonction RemoveAll comme modèle pour effectuer une analyse spécifique. attention aux points suivants. Diverses situations : RemoveAll(path string)

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
}
Copier après la connexion

État du fichier

Écriture et écriture du contenu du fichier

Cette partie implique davantage d'opérations liées aux E/S et à l'introduction du système. est ici Organisons la partie E/S De manière générale, il existe trois façons de lire et d'écrire du contenu dans un fichier :

1 Après avoir utilisé

pour ouvrir le fichier, utilisez f, err := os.Open(file_path) directement après. en le combinant avec un tampon personnalisé. Lire/lire le contenu fixe du fichier à la fois f.read() f.write()

2. Utilisez les méthodes readFile et writeFile de ioutl

3. cache, par exemple via

Après avoir chargé l'instance qui implémente l'interface io.Reader, vous pouvez utiliser info.ReadLine() pour lire une ligne entière à la fois jusqu'à ce que les informations d'erreur soient io.EOF et que la lecture se termineinfo:=bufio.NewReader(f)

Ce blog compare la vitesse de lecture de trois opérations sur des fichiers. Il semble qu'ioutil soit plus efficace lors de la lecture de fichiers volumineux.

Chaque méthode a des situations applicables différentes. Voici des exemples d'opérations de lecture de trois manières. Pour l'opération d'écriture de fichiers, vous pouvez vous référer à l'opération de lecture :

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()

}
Copier après la connexion

Opérations avancées<🎜. >

Emballage de fichiers, décompression de fichiers, parcours de fichiers, ces opérations associées peuvent essentiellement être effectuées en se référant à la méthode RemoveAll, qui est une méthode récursive plus traversée.

Ce qui suit est une implémentation de la compression de fichiers :

//将文件夹中的内容打包成 .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")

}
Copier après la connexion

Ajoutez cela

Vous n'avez peut-être pas remarqué la différence entre la fonction OpenFile et la fonction Open auparavant. La fonction Openfile peut spécifier le. autorisations du descripteur de fichier renvoyé Contrôlé via O_RDONLY, O_WRONLY, O_RDWR, etc. La fonction Open appelle la fonction OpenFile en interne. L'autorisation par défaut est O_RDONLY Si vous utilisez uniquement la fonction Open pour renvoyer le descripteur de fichier, puis écrivez dans le fichier, une erreur de descripteur de fichier incorrect sera renvoyée. Faites plus attention et travaillez soigneusement les détails. Il s'agit essentiellement d'un problème avec les descripteurs de fichiers du système d'exploitation.

Ajouter une opération de copie de fichier

reportez-vous à ceci :https://www.socketloop.com/tutorials/golang-copy-directory-inclure-sub-directories-files

Réimprimé dans : https://www.cnblogs.com/Goden/p/4533908.html

Pour plus d'articles techniques liés au golang, veuillez visiter

golang Tutoriel colonne!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:csdn.net
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal