カテゴリー: BackEnd

Golangのファイル操作基本

はじめに

まだまだGolang勉強中のnukkyです。今回は理解は必須であろうファイル操作についてまとめてみました。

ファイル/ディレクトリの操作

ファイル/ディレクトリの存在を確認

os.Open 関数を使ってファイルやディレクトリの存在を確認します。

package main

import (
    "fmt"
    "os"
)

func main() {
    fp, err := os.Open("/tmp/test.txt")
    if os.IsNotExist(err) {
        fmt.Println("file does not exist")
        return
    }
    // ErrNotExist 以外のエラー
    if err != nil {
        fmt.Println(err)
        return
    }
    defer fp.Close()
}

ファイル/ディレクトリの名前変更

名前の変更は os.Rename を使用します。

package main

import (
    "fmt"
    "os"
)

func main() {
    if err := os.Rename("foo", "bar"); err != nil {
        fmt.Println(err)
    }
}

ファイル/ディレクトリの削除

os.Remove を使用してファイルやディレクトリを削除します。

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Remove("./tmp")
    fmt.Println(err)
}

ただし、上記の処理ではファイルが存在するディレクトリは削除できないので、中にあるファイルごと全て削除したい場合には、 os.RemoveAll() を使用します。

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.RemoveAll("./tmp")
    fmt.Println(err)
}

ディレクトリ内のファイル確認

ディレクトリ内のファイル一覧を取得したい場合、 io/ioutil の ReadDir() を使用します。

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    files, _ := ioutil.ReadDir("./")
    for _, f := range files {
        fmt.Println(f.Name())
    }
}

ファイルの作成

ファイルの簡単な読み書きをするには os.Open / os.Create 関数で充分ですが、もっと細かな設定とともにファイルを開きたい場合には os.OpenFile 関数を使います。 os.OpenFile 関数は以下の引数が設定されています。

  1. ファイル名 string
  2. フラグ int
  3. ファイルモード(POSIX パーミッション) FileMode

第2引数のフラグは os パッケージに定義されている定数を指定します。

const (
  O_RDONLY int  // 読み込み専用
  O_WRONLY int  // 書き込み専用
  O_RDWR   int  // 読み書き可能
  O_CREATE int  // ファイルが無ければ新規作成
  O_TRUNC  int  // ファイル内容を削除
  O_APPEND int  // ファイル後端に追加で書き込み
  O_EXCL   int  // (O_CREATE とともに使用して)ファイルが存在すればエラー
  O_SYNC   int  // 入出力が同期されたファイル
)

例えば、ファイルを読み書き可能、無ければ作成、既に存在するならエラーを返す、という条件で開きたい場合は、以下のように指定します。

f, err := os.OpenFile("hoge.txt", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0755)
if err != nil {
    log.Fatal(err)
}

第3引数のファイルモードでは32ビットの下位9ビットで POSIX パーミッションを指定します。 POSIX パーミッションは0から始まる8進の整数リテラルを指定します。たとえばパーミッションとして「rwxr-xr-x」を与えたい場合は「0755」を指定します。 「rw-rw-rw-」なら「0666」です。

f, err := os.OpenFile("hoge.txt", os.O_RDWR|os.O_CREATE, 0755)
// POSIX パーミッション「rwxr-xr-x」(0755) でファイルを作成
if err != nil {
    log.Fatal(err)
}

また、ファイルモードでは一部定数も用意されています、

type FileMode uint32

// ファイルモードの定数
const (
  ModeDir        FileMode = 1 << (32 - 1 - iota) // d
  ModeAppend                                     // a
  ModeExclusive                                  // l
  ModeTemporary                                  // T
  ModeSymlink                                    // L
  ModeDevice                                     // D
  ModeNamedPipe                                  // p
  ModeSocket                                     // S
  ModeSetuid                                     // u
  ModeSetgid                                     // g
  ModeCharDevice                                 // c
  ModeSticky                                     // t

  ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
  ModePerm FileMode = 0777  // ファイルの POSIX パーミッションのためのフィルタ
)

どのように使用するかと言いますと例えば「drwxr-xr-x」を与えたい場合に以下のように指定します。

f, err := os.OpenFile("hoge.txt", os.O_RDWR|os.O_CREATE, os.ModeDir|0755)
// POSIX パーミッション「drwxr-xr-x」でファイルを作成
if err != nil {
    log.Fatal(err)
}

ディレクトリの作成

os.Mkdir()が使えます。第二引数はモードです。

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.Mkdir("tmp", 0777)
    fmt.Println(err)
}

ファイルの読み書き

ioutil.ReadFile

ioutil.ReadFile を使えば一度にファイルの内容を読み込むことができます。
取得したデータはバイト型なので、文字列型にする場合は以下のように string(data) します。

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(data))
}

ioutil.ReadAll

ioutil.ReadAll は開いているファイルをEOFまですべて読み込みます。
ファイルの内容はバイト型なので、文字列型として使用する場合は string(data) します。

package main

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

func main() {
    fp, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer fp.Close()

    data, err := ioutil.ReadAll(fp)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(data))
}

ioutil.WriteFile

ioutil.WriteFile はファイルに一度にデータを書き込む関数です。ファイルが存在していなければ、新規で作成されます。
ioutil.WriteFile の引数は、第一引数にファイルのパス、第二引数に書き込む文字をバイト化したもの、第三引数はファイルのパーミッションです。

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data := "test"

    err := ioutil.WriteFile("hoge.txt", []byte(data), 0664)
    if err != nil {
        fmt.Println(err)
    }
}

io.Copy

ファイルからファイルに内容をコピーするには、io.Copy を使います。
io.Copy の引数は io.Writer と io.Reader になります。
なので、その要件を満たすもの(*os.File など)を渡します。

package main

import (
    "io/ioutil"
    "os"
    "log"
)

func main() {
    w, err := os.Create("write.txt")
    if err != nil {
        log.Fatal(err)
    }

    r, err := os.Open("read.txt")
    if err != nil {
        log.Fatal(err)
    }

    _, err = io.Copy(w, r)
    if err != nil {
        log.Fatal(err)
    }
}

ファイルなどの内容を一行ずつ処理

改行区切りで一行ずつ処理するには、 bufio.NewScanner でループ処理します。
Scan() 関数は、行がある限り true を返すので、Text() で一行ずつ取得できます。

package main

import (
    "fmt"
    "bufio"
    "os"
    "log"
)

func main() {
    b, err := os.Open("hoge.txt")
    if err != nil {
        log.Fatal(err)
    }
    s := bufio.NewScanner(b)

    for i := 1; s.Scan(); i++ {
        line := s.Text()
        fmt.Println(i, line)
    }

    if err := s.Err(); err != nil {
        log.Fatal(err)
    }
}

TOML

ファイル操作ということで、最後におまけ程度ですが設定ファイルについて、単純なtxt配列で設定ファイルを用意しても良いのですが、 go の場合 TOML がとても便利なので簡単に紹介させていただきます。https://github.com/BurntSushi/toml

.tomlファイル

設定ファイルとして拡張子 toml のファイルを作成します、サンプルとして中身はこのようにします。

// test.toml
[User]
name  = "nukky"
age     = 0

設定ファイルの読み込み

それでは上記の設定ファイルを実際に読み込めるようにしたいと思います。

package main

import (
    "fmt"
    "os"
    "io/ioutil"
    "github.com/BurntSushi/toml"
)

//Test 設定ファイル
type Test struct {
    User TestUser
}

//TestUser 設定ファイルのユーザ部分
type TestUser struct {
    Name string
    Age  int
}

var test Test

func main() {
    _, err := toml.DecodeFile("./test.toml", &test)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(test.User.Name)
}

 

さいごに

ファイル操作はアプリの基本ですので、しっかり押さえておきたいと思います。

おすすめ書籍

   

nukky

シェア
執筆者:
nukky
タグ: golang

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

3週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前