Как получить список файлов и папок в директории на Go: полное руководство
Современный подход: используем os.ReadDir и filepath.Walk
Действительно, ioutil.ReadDir устарел начиная с Go 1.16. Вместо него рекомендуется использовать os.ReadDir для чтения одной директории и filepath.Walk (или filepath.WalkDir) для рекурсивного обхода. Разберём все ваши сценарии.
1. Базовая работа: читаем содержимое директории
Функция os.ReadDir(path) возвращает срез []os.DirEntry. Каждый элемент — это запись директории, у которой есть методы Name(), IsDir(), Type() и Info().
package main
import (
"fmt"
"os"
)
func main() {
entries, err := os.ReadDir(".")
if err != nil {
fmt.Println("Ошибка:", err)
return
}
for _, entry := range entries {
fmt.Println(entry.Name())
}
}
2. Получаем только файлы (без папок)
Используем метод IsDir() — он возвращает true для директорий. Нам нужно false:
for _, entry := range entries {
if !entry.IsDir() {
fmt.Println("Файл:", entry.Name())
}
}
3. Получаем только папки
Аналогично, но проверяем IsDir() == true:
for _, entry := range entries {
if entry.IsDir() {
fmt.Println("Папка:", entry.Name())
}
}
4. Фильтрация по расширению
Используем пакет strings:
import "strings"
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
fmt.Println("Go-файл:", entry.Name())
}
}
5. Рекурсивный обход всех поддиректорий
Для этого используем filepath.WalkDir (появился в Go 1.16, более эффективный, чем старый filepath.Walk). Он принимает функцию обратного вызова, которая вызывается для каждого элемента файловой системы.
import (
"fmt"
"os"
"path/filepath"
)
func main() {
root := "." // можно указать любую папку
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err // если ошибка доступа, пропускаем
}
// Пропускаем корневую папку, если не нужно её выводить
if path == root {
return nil
}
fmt.Println(path)
return nil
})
if err != nil {
fmt.Println("Ошибка:", err)
}
}
6. Рекурсивно только файлы .go
Добавляем фильтрацию внутри callback-функции:
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil // пропускаем папки, но продолжаем обход внутри них
}
if strings.HasSuffix(d.Name(), ".go") {
fmt.Println(path)
}
return nil
})
Важное замечание про производительность
Если вам нужно только проверить, является ли элемент директорией, используйте d.IsDir() — он работает без дополнительного системного вызова. Метод d.Info() (который возвращает os.FileInfo) выполняет вызов stat и может быть медленнее. Для простого списка имён и типа os.ReadDir оптимален.
Полный пример: вывод всех файлов с размером
Если нужно получить размер файла, используем d.Info():
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
continue
}
if !entry.IsDir() {
fmt.Printf("%s (%d байт)\n", entry.Name(), info.Size())
}
}
Таким образом, современный Go предлагает два основных инструмента: os.ReadDir для плоского списка и filepath.WalkDir для рекурсивного обхода. Они полностью заменяют устаревший ioutil.ReadDir.
Альтернативный подход: используем os.File и метод Readdir
Если вам нужно больше контроля (например, читать директорию порциями или обрабатывать ошибки для каждого элемента отдельно), можно воспользоваться низкоуровневым методом Readdir из пакета os.
package main
import (
"fmt"
"os"
)
func main() {
dir, err := os.Open(".")
if err != nil {
fmt.Println("Ошибка открытия:", err)
return
}
defer dir.Close()
// Читаем все элементы сразу (n = -1)
fileInfos, err := dir.Readdir(-1)
if err != nil {
fmt.Println("Ошибка чтения:", err)
return
}
for _, fi := range fileInfos {
if fi.IsDir() {
fmt.Println("[DIR]", fi.Name())
} else {
fmt.Println("[FILE]", fi.Name(), "размер:", fi.Size())
}
}
}
Метод Readdir(n) возвращает []os.FileInfo (не DirEntry). Если n > 0, он читает не более n элементов — это удобно для потоковой обработки больших директорий. Если n <= 0, читает все элементы.
Когда это полезно? Если вам нужно получить расширенную информацию (размер, права доступа, время модификации) сразу для всех файлов, без дополнительного вызова Info(). Однако этот метод считается менее производительным, чем os.ReadDir, так как он выполняет системный вызов stat для каждого элемента. Для простого списка имён всегда предпочитайте os.ReadDir.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги