91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言中ReadDir 和 DirEntry的區別是什么

發布時間:2021-08-03 15:50:14 來源:億速云 閱讀:404 作者:Leah 欄目:編程語言

這期內容當中小編將會給大家帶來有關Go語言中ReadDir 和 DirEntry的區別是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

為什么需要ReadDir?

簡短的答案是:性能。

當調用讀取文件夾路徑的系統函數時,操作系統一般會返回文件名_和_它的類型(在Windows下,還包括如文件大小和最后修改時間等的stat信息)。然而,原始版本的Go和Python接口會丟掉這些額外信息,這就需要在讀取每個路徑時再多調用一個stat。系統調用的性能較差 ,stat 可能從磁盤、或至少從磁盤緩存讀取信息。

在循環遍歷目錄樹時,你需要知道一個路徑是文件還是文件夾,這樣才可以知道循環遍歷的方式。因此即使一個簡單的目錄樹遍歷,也需要讀取文件夾路徑并獲取每個路徑的stat信息。但如果使用操作系統提供的文件類型信息,就可以避免那些stat系統調用,同時遍歷目錄的速度也將提高幾倍(在網絡文件系統上甚至可以快十幾倍)。具體信息可以參考Python版本的基準測試。

不幸的是,兩種語言中讀取文件夾的最初實現都不是最優的設計,不使用額外的系統調用stat就無法獲取類型信息:Python中的os.listdir和Go中的 ioutil.ReadDir

我在2012年首次想到Python的scandir背后的原理,并為2015年發布的Python 3.5實現了這個函數(從這里可以了解更多這個過程的信息)。此后這個函數不斷地被改進完善:比如,增加with控制語句和文件描述符的支持。

對于Go語言,除了基于Python版本的經驗提出一些改進建議的評論外,我沒有參與這個提案或實現。

Python vs Go

我們看下新的“讀取文件夾”的接口,尤其關注下它們在Python和Go中有多么的相似。

在Python中調用os.scandir(path),會返回一個os.DirEntry的迭代器,如下所示:

class DirEntry:    # This entry's filename.    name: str    # This entry's full path: os.path.join(scandir_path, entry.name).    path: str    # Return inode or file ID for this entry.    def inode(self) -> int: ...    # Return True if this entry is a directory.    def is_dir(self, follow_symlinks=True) -> bool: ...    # Return True if this entry is a regular file.    def is_file(self, follow_symlinks=True) -> bool: ...    # Return True if this entry is a symbolic link.    def is_symlink(self) -> bool: ...    # Return stat information for this entry.    def stat(self, follow_symlinks=True) -> stat_result: ...

訪問namepath屬性將不會拋出異常,但根據操作系統和文件系統,以及路徑是否為符號鏈接,方法的調用可能會拋出OSError異常。比如,在Linux下,stat總是會進行一次系統調用,因此可能會拋出異常,但is_X的方法一般不會這樣。

在Go語言中,調用os.ReadDir(path),將會返回一個os.DirEntry對象的切片,如下所示:

type DirEntry interface {    // Returns the name of this entry's file (or subdirectory).    Name() string    // Reports whether the entry describes a directory.    IsDir() bool    // Returns the type bits for the entry (a subset of FileMode).    Type() FileMode    // Returns the FileInfo (stat information) for this entry.    Info() (FileInfo, error)}

盡管在真正的Go風格下,Go版本更加簡單,但你一眼就可以看出二者之間多么相似。實際上,如果重新來寫Python的scandir,我很可能會選擇一個更簡單的接口——尤其是要去掉follow_symlinks參數,不讓它默認跟隨處理符號鏈接。

下面是一個使用os.scandir的例子——一個循環計算文件夾及其子文件夾中文件的總大小的函數:

def get_tree_size(path):    total = 0    with os.scandir(path) as entries:        for entry in entries:            if entry.is_dir(follow_symlinks=False):                total += get_tree_size(entry.path)            else:                total += entry.stat(follow_symlinks=False).st_size    return total

在Go中(一旦1.16發布),對應的函數如下所示:

func GetTreeSize(path string) (int64, error) {    entries, err := os.ReadDir(path)    if err != nil {        return 0, err    }    var total int64    for _, entry := range entries {        if entry.IsDir() {            size, err := GetTreeSize(filepath.Join(path, entry.Name()))            if err != nil {                return 0, err            }            total += size        } else {            info, err := entry.Info()            if err != nil {                return 0, err            }            total += info.Size()        }    }    return total, nil}

高級結構很相似,當然有人可能會說:“看,Go的錯誤處理多么繁瑣!”沒錯——Python代碼非常簡潔。在簡短腳本的情況下這沒有問題,而這也是Python的優勢。

然而,在生產環境的代碼中,或者在一個頻繁使用的命令行工具庫中,捕獲stat調用的錯誤會更好,進而可以忽略權限錯誤或者記錄日志。Go代碼可以明確看到錯誤發生的情況,可以讓你輕松添加日志或者打印的錯誤信息更好。

更高級的目錄樹遍歷

另外,兩個語言都有更高級的循環遍歷目錄的函數。在Python中,它是os.walk。Python中scandir的美妙之處在于os.walk的簽名無需改變,因此所有os.walk的用戶(有非常多)都可以自動得到加速。

比如,使用os.walk打印文件夾下所有非點的路徑:

def list_non_dot(path):    paths = []    for root, dirs, files in os.walk(path):        # Modify dirs to skip directories starting with '.'        dirs[:] = [d for d in dirs if not d.startswith('.')]        for f in files:            if f.startswith('.'):                continue            paths.append(os.path.join(root, f))    return sorted(paths)

從Python3.5開始,os.walk底層使用scandir代替listdir,根據操作系統和文件系統,這可以顯著提升1.5到20倍的速度。

Go (pre-1.16版本)語言中有一個相似的函數,filepath.Walk,但不幸的是 FileInfo 接口的設計無法支持各種方法調用時的錯誤報告。正如我們所知,有時函數會進行系統調用——比如,像Size這樣的統計信息在Linux下總是需要一次系統調用。因此在Go語言中,這些方法需要返回錯誤(在Python中它們會拋出異常)。

是否要嘗試去掉錯誤處理的邏輯來重復使用 FileInfo 接口,這樣現有代碼就可以顯著提速。實際上,Russ Cox提出一個提案 issue 41188就是這個思路(提供了一些數據來表明這個想法并不像聽起來那么不靠譜)。然而,stat 確實會返回錯誤,因此像文件大小這樣潛在的屬性應該在錯誤時返回0。這樣對應的結果是,要把這個邏輯嵌入到現有的API中,需要大量需要推動改動的地方,最后Russ確認 無法就此達成共識,并提出 DirEntry 接口。

這表明,為了獲得性能提升, filepath.Walk 的調用需要改成 filepath.WalkDir ——盡管非常相似,但遍歷函數的參數是DirEntry 而不是 FileInfo

下面的代碼是Go版本的使用現有filepath.Walk 函數的list_non_dot

func ListNonDot(path string) ([]string, error) {    var paths []string    err := filepath.Walk(path, func(p string, info os.FileInfo,                                    err error) error {        if strings.HasPrefix(info.Name(), ".") {            if info.IsDir() {                return filepath.SkipDir            }            return err        }        if !info.IsDir() {            paths = append(paths, p)        }        return err    })    return paths, err}

當然,在Go 1.16中這段代碼也可以運行,但如果你想得到性能收益就需要做少許修改——在上面的代碼中僅需要把 Walk 替換為 WalkDir,并把 os.FileInfo 替換成 os.DirEntry

err := filepath.WalkDir(path, func(p string, info os.DirEntry,

上述就是小編為大家分享的Go語言中ReadDir 和 DirEntry的區別是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武隆县| 五华县| 黎城县| 新邵县| 崇阳县| 石嘴山市| 德钦县| 襄城县| 崇仁县| 长丰县| 石柱| 永顺县| 类乌齐县| 霍邱县| 陇川县| 同江市| 太和县| 来宾市| 大竹县| 武邑县| 克什克腾旗| 金湖县| 宁南县| 巫山县| 定安县| 西乡县| 韶关市| 珠海市| 灵璧县| 承德县| 兰西县| 定州市| 盐津县| 麻栗坡县| 金昌市| 塔城市| 临高县| 平潭县| 原平市| 潮安县| 商都县|