第11章 ファイルとディレクトリ

ファイル操作の基本

Go言語における「ファイル操作の基本」は、ファイルの読み書き、作成、および削除などを含みます。これらの操作は標準ライブラリのosioutilパッケージを用いて行われます。以下に具体的な事例を示します。


ファイルへの書き込み

ファイルを開いてデータを書き込む基本的な手順は次のとおりです。

package main

import (
    "os"
    "log"
)

func main() {
    // ファイルを開く(存在しない場合は作成)
    file, err := os.Create("example.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // ファイルにデータを書き込む
    _, err = file.WriteString("Hello, Go!")
    if err != nil {
        log.Fatal(err)
    }
}


ファイルからの読み込み

ファイルからデータを読み込む基本的な手順は次のとおりです。

package main

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

func main() {
    // ファイルの内容を読み込む
    data, err := ioutil.ReadFile("example.txt")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(data))
}


ファイルの作成

os.Create関数を用いて新しいファイルを作成できます。ファイルが既に存在する場合は、ファイルを新しい空ファイルで上書きします。

file, err := os.Create("newfile.txt")
if err != nil {
    log.Fatal(err)
}
file.Close()


ファイルの削除

os.Remove関数を用いてファイルを削除できます。

err := os.Remove("newfile.txt")
if err != nil {
    log.Fatal(err)
}


バッファリングIO

バッファリングIOでは、データは一時的なバッファ(メモリ内の一時領域)に書き込まれ、バッファが一定の量に達したときに、または明示的にフラッシュされたときに、実際の入出力先(例えばファイルやネットワーク)に書き込まれます。これにより、多数の小さなデータ書き込みをまとめて一度に行うことができ、IOの回数を減らして効率を上げることができます。

package main

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

func main() {
    file, err := os.Create("buffered.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    writer := bufio.NewWriter(file)
    _, err = writer.WriteString("Hello, Buffered IO!")
    if err != nil {
        log.Fatal(err)
    }

    // バッファをフラッシュしてデータをファイルに書き出す
    writer.Flush()
}


非バッファリングIO

非バッファリングIOでは、データは直接入出力先に書き込まれます。これは、書き込まれたデータが即座に利用可能であることを保証する必要がある場合に適していますが、頻繁なIO操作はパフォーマンスに影響を与える可能性があります。

package main

import (
    "os"
    "log"
)

func main() {
    file, err := os.Create("unbuffered.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // 非バッファリングIOを使用してデータを直接書き込む
    _, err = file.WriteString("Hello, Unbuffered IO!")
    if err != nil {
        log.Fatal(err)
    }
}


ファイルの応用操作

Go言語でのファイル操作には応用的な操作も含まれます。これにはファイルのシークとリセット、ファイルのコピー、ファイルメタデータの取得と設定などがあります。以下にこれらの操作の具体的な事例を示します。


ファイルのシークとリセット

ファイルのシーク(Seek)は、ファイル内の特定の位置に読み書きのポインタを移動する操作です。これはosパッケージのFile型のSeekメソッドを使用して行います。

package main

import (
    "os"
    "log"
)

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

    // ファイルの先頭から5バイト目にシーク
    _, err = file.Seek(5, 0)
    if err != nil {
        log.Fatal(err)
    }

    // データの読み込み(例:バイトスライスに読み込む)
    data := make([]byte, 5)
    _, err = file.Read(data)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("読み込んだデータ: %s\n", string(data))
}


ファイルのコピー

ファイルをコピーするには、元のファイルからデータを読み込み、新しいファイルに書き込む必要があります。

package main

import (
    "io"
    "os"
    "log"
)

func main() {
    // ソースファイルを開く
    source, err := os.Open("source.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer source.Close()

    // コピー先のファイルを作成
    destination, err := os.Create("destination.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer destination.Close()

    // ファイルの内容をコピー
    _, err = io.Copy(destination, source)
    if err != nil {
        log.Fatal(err)
    }
}


ファイルメタデータの取得と設定

ファイルのメタデータ(サイズ、変更日時、権限など)はosパッケージを使用して取得および設定することができます。

package main

import (
    "os"
    "log"
    "fmt"
    "time"
)

func main() {
    fileInfo, err := os.Stat("example.txt")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("ファイルサイズ:", fileInfo.Size())
    fmt.Println("変更日時:", fileInfo.ModTime())

    // ファイルの権限を変更(例: 読み書き可能に)
    err = os.Chmod("example.txt", 0666)
    if err != nil {
        log.Fatal(err)
    }

    // ファイルの最終アクセス日時と変更日時を設定
    err = os.Chtimes("example.txt", time.Now(), time.Now())
    if err != nil {
        log.Fatal(err)
    }
}


ディレクトリの操作

Go言語における「ディレクトリの操作」は、主にosおよびpath/filepathパッケージを使用して行われます。これにはディレクトリの作成、削除、ディレクトリ内のファイル一覧の取得などが含まれます。以下に具体的な事例を示します。


ディレクトリの作成

os.Mkdirまたはos.MkdirAll関数を使用して新しいディレクトリを作成できます。os.MkdirAllは、必要なすべての親ディレクトリも同時に作成します。

package main

import (
    "os"
    "log"
)

func main() {
    err := os.Mkdir("example_dir", 0755)
    if err != nil {
        log.Fatal(err)
    }
}


ディレクトリの削除

os.Removeまたはos.RemoveAll関数を使用してディレクトリを削除できます。os.RemoveAllは、指定されたディレクトリとその中のすべての内容を削除します。

err := os.Remove("example_dir")
if err != nil {
    log.Fatal(err)
}


ディレクトリ内のファイル一覧の取得

ioutil.ReadDir関数を使用して、特定のディレクトリ内のファイルとディレクトリの一覧を取得できます。

package main

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

func main() {
    files, err := ioutil.ReadDir(".")
    if err != nil {
        log.Fatal(err)
    }

    for _, file := range files {
        fmt.Println(file.Name())
    }
}


ディレクトリの移動と名前の変更

os.Rename関数を使用して、ディレクトリの名前を変更したり、別の場所に移動したりできます。

err := os.Rename("old_dir", "new_dir")
if err != nil {
    log.Fatal(err)
}


ファイルシステムの応用

Go言語におけるファイルシステムの応用操作では、ファイルパスの操作、一時ファイルとディレクトリの作成、ファイルやディレクトリのパーミッションの管理などが行われます。これらの操作はosioutilpath/filepathパッケージを使用して実行されます。以下に具体的な事例を示します。


ファイルパスの操作

path/filepathパッケージを使用して、ファイルパスの結合、分割、解析などを行うことができます。

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
	// ファイルパスの結合
    dir01 := "/path/to"
    file01 := "example.txt"
    fullPath := filepath.Join(dir, file)
    fmt.Println(fullPath) // 出力: /path/to/example.txt

    // ファイルパスの分解
    dir02, file02 := filepath.Split(fullPath)
    fmt.Println("ディレクトリ:", dir02) // 出力: /path/to/
    fmt.Println("ファイル名:", file02)  // 出力: example.txt

}


一時ファイルとディレクトリ

一時ファイルやディレクトリは、ioutilパッケージを使用して作成できます。これらは一時的なデータの保存に便利です。

// 一時ファイルの作成
tempFile, err := ioutil.TempFile("", "example.*.txt")
if err != nil {
    log.Fatal(err)
}
defer os.Remove(tempFile.Name()) // クリーンアップ

// 一時ディレクトリの作成
tempDir, err := ioutil.TempDir("", "example")
if err != nil {
    log.Fatal(err)
}
defer os.RemoveAll(tempDir) // クリーンアップ


ファイルとディレクトリのパーミッション

ファイルやディレクトリのパーミッションはos.Chmod関数を使用して設定できます。

// ファイルのパーミッション設定
err := os.Chmod("example.txt", 0644)
if err != nil {
    log.Fatal(err)
}

// ディレクトリのパーミッション設定
err = os.Chmod("some_directory", 0755)
if err != nil {
    log.Fatal(err)
}


練習問題1.

example.txtという名前の新しいファイルを作成し、"Hello, Go!"という文字列を書き込んだ後、ファイルを閉じてください。ファイルが正常に作成されたかどうかを確認するために、ファイルが存在するかどうかをチェックするコードも書いてください。

package main

// ここに必要なパッケージをインポート

func main() {
    // ここにコードを記述
}


練習問題2.

example.txtファイルを開き、その内容を読み込み、コンソールに出力してください。ファイルが存在しない場合や読み込みに失敗した場合のエラーハンドリングも実装してください。

package main

// ここに必要なパッケージをインポート

func main() {
    // ここにコードを記述
}


練習問題3.

test_directoryという名前のディレクトリを作成し、その後、ディレクトリを削除してください。また、ディレクトリの作成および削除が成功したことを確認するコードも書いてください。

package main

// ここに必要なパッケージをインポート

func main() {
    // ここにコードを記述
}