Goでファイル操作ばかりしてる俺が語る！爆速7フォーマット実践サンプル【JSON/YAML/CSV/txt/Excel/ini/XML】

## はじめに

現在の部署に入ってから、Goでのファイル操作ばかりを行なっているので、この記事では、Goでの色々なファイルの読み書きについて説明します。

## JSON

https://pkg.go.dev/encoding/json

### inputファイル

```json:input.json
{
    "name": "John",
    "age": 30
}
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"encoding/json" // JSONエンコード/デコード用パッケージ
	"fmt"           // フォーマット済み入出力用パッケージ
	"log"           // ログ出力用パッケージ
	"os"            // ファイル操作などのOS機能用パッケージ
)

// User構造体はJSONデータのユーザー情報を表現します
type User struct {
	Name string `json:"name"` // JSONの"name"キーに対応
	Age  int    `json:"age"`  // JSONの"age"キーに対応
}

const filePath = "input.json" // 読み込むJSONファイルのパス

func main() {
	// 読み込み専用でファイルをオープン
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err) // エラー発生時はログ出力して終了
	}
	defer file.Close() // 関数終了時にファイルを必ずクローズ

	var user User
	// JSONデコーダーでファイルの内容をUser構造体にデコード
	if err := json.NewDecoder(file).Decode(&user); err != nil {
		log.Fatal(err) // デコードエラーがあればログ出力して終了
	}

	// デコードしたユーザー情報を出力
	fmt.Printf("%+v\n", user) // {Name:John Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"encoding/json" // JSONのエンコード/デコード用パッケージ
	"log"           // エラーログ出力用パッケージ
	"os"            // ファイル操作などのOS機能用パッケージ
)

// User構造体はユーザー情報を保持します
type User struct {
	Name string `json:"name"` // JSONの"name"キーに対応
	Age  int    `json:"age"`  // JSONの"age"キーに対応
}

const filePath = "output.json" // 出力ファイルのパス

func main() {
	// 書き込み可能な状態でファイルをオープン（存在しなければ作成、存在すれば内容を上書き）
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatal(err) // エラー発生時はログ出力して終了
	}
	defer file.Close() // 関数終了時にファイルを必ずクローズ

	// 書き込むユーザー情報を定義
	user := User{
		Name: "John",
		Age:  30,
	}

	// json.Encoderを使って、ユーザー情報をJSON形式でファイルに書き込む
	if err := json.NewEncoder(file).Encode(user); err != nil {
		log.Fatal(err) // エンコードまたは書き込みエラーの場合、ログ出力して終了
	}
}
```

## YAML

https://pkg.go.dev/encoding/json

### inputファイル

```yaml:input.yaml
name: John
age: 30
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"fmt"           // フォーマット済み入出力用パッケージ
	"log"           // ログ出力用パッケージ
	"os"            // ファイル操作用パッケージ
	"gopkg.in/yaml.v3" // YAMLエンコード/デコード用パッケージ
)

// User構造体はYAMLデータのユーザー情報を表現します
type User struct {
	Name string `yaml:"name"` // YAMLの"name"キーに対応
	Age  int    `yaml:"age"`  // YAMLの"age"キーに対応
}

const filePath = "input.yaml" // 読み込むYAMLファイルのパス

func main() {
	// 読み込み専用でファイルをオープン
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	var user User
	// YAMLデコーダーでファイルの内容をUser構造体にデコード
	if err := yaml.NewDecoder(file).Decode(&user); err != nil {
		log.Fatal(err)
	}

	// デコードしたユーザー情報を出力
	fmt.Printf("%+v\n", user) // {Name:John Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"log"           // エラーログ出力用パッケージ
	"os"            // ファイル操作用パッケージ
	"gopkg.in/yaml.v3" // YAMLエンコード/デコード用パッケージ
)

// User構造体はユーザー情報を保持します
type User struct {
	Name string `yaml:"name"` // YAMLの"name"キーに対応
	Age  int    `yaml:"age"`  // YAMLの"age"キーに対応
}

const filePath = "output.yaml" // 書き込み先のYAMLファイルパス

func main() {
	// 書き込み可能な状態でファイルをオープン（存在しなければ作成、存在すれば上書き）
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	user := User{
		Name: "John",
		Age:  30,
	}

	// YAMLエンコーダーを使って、ユーザー情報をYAML形式でファイルに書き込む
	if err := yaml.NewEncoder(file).Encode(user); err != nil {
		log.Fatal(err)
	}
}
```

## CSV

https://pkg.go.dev/encoding/csv

### inputファイル

```csv:input.csv
Name,Age
John,30
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"encoding/csv" // CSVデコード用パッケージ
	"fmt"        // フォーマット済み入出力用パッケージ
	"log"        // ログ出力用パッケージ
	"os"         // ファイル操作用パッケージ
	"strconv"    // 文字列⇔数値変換用パッケージ
)

// User構造体はCSVデータのユーザー情報を表現します
type User struct {
	Name string
	Age  int
}

const filePath = "input.csv" // 読み込むCSVファイルのパス

func main() {
	// 読み込み専用でCSVファイルをオープン
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		log.Fatal(err)
	}

	// ヘッダー行を除き、2行目のレコードをUser構造体に変換（例として）
	if len(records) < 2 {
		log.Fatal("レコードが不足しています")
	}
	age, err := strconv.Atoi(records[1][1])
	if err != nil {
		log.Fatal(err)
	}
	user := User{
		Name: records[1][0],
		Age:  age,
	}

	// 読み込んだユーザー情報を出力
	fmt.Printf("%+v\n", user) // {Name:John Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"encoding/csv" // CSVエンコード用パッケージ
	"log"        // ログ出力用パッケージ
	"os"         // ファイル操作用パッケージ
	"strconv"    // 数値を文字列に変換するためのパッケージ
)

// User構造体はユーザー情報を保持します
type User struct {
	Name string
	Age  int
}

const filePath = "output.csv" // 書き込み先のCSVファイルパス

func main() {
	// 書き込み可能な状態でCSVファイルをオープン（存在しなければ作成、存在すれば上書き）
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// ヘッダー行の書き込み
	if err := writer.Write([]string{"Name", "Age"}); err != nil {
		log.Fatal(err)
	}

	user := User{
		Name: "John",
		Age:  30,
	}
	// ユーザー情報の書き込み
	record := []string{user.Name, strconv.Itoa(user.Age)}
	if err := writer.Write(record); err != nil {
		log.Fatal(err)
	}
}
```

## txt

https://pkg.go.dev/bufio

### inputファイル

```txt:input.txt
John
30
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"bufio"    // バッファ付き入出力用パッケージ
	"fmt"      // フォーマット済み入出力用パッケージ
	"log"      // ログ出力用パッケージ
	"os"       // ファイル操作用パッケージ
	"strconv"  // 文字列⇔数値変換用パッケージ
)

const filePath = "input.txt" // 読み込むテキストファイルのパス

func main() {
	// 読み込み専用でテキストファイルをオープン
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	var lines []string
	// 1行ずつ読み込み
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	// 入力ファイルは2行あることを想定（1行目: Name、2行目: Age）
	if len(lines) < 2 {
		log.Fatal("入力ファイルの行数が不足しています")
	}

	name := lines[0]
	age, err := strconv.Atoi(lines[1])
	if err != nil {
		log.Fatal(err)
	}

	// 読み込んだユーザー情報を出力
	fmt.Printf("{Name:%s, Age:%d}\n", name, age) // {Name:John, Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"bufio" // バッファ付き入出力用パッケージ
	"log"   // ログ出力用パッケージ
	"os"    // ファイル操作用パッケージ
)

const filePath = "output.txt" // 書き込み先のテキストファイルパス

func main() {
	// 書き込み可能な状態でテキストファイルをオープン（存在しなければ作成、存在すれば上書き）
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	// ユーザー情報 "John" と "30" を1行ずつ書き込む
	if _, err := writer.WriteString("John\n"); err != nil {
		log.Fatal(err)
	}
	if _, err := writer.WriteString("30\n"); err != nil {
		log.Fatal(err)
	}
	writer.Flush()
}
```

## Excel

https://pkg.go.dev/github.com/xuri/excelize/v2

### inputファイル

input.xlsx

| Name | Age |
|------|-----|
| John | 30  |

このテーブルは、シート "Sheet1" の以下のセルに対応します:
- **セル A1**: `Name`
- **セル B1**: `Age`
- **セル A2**: `John`
- **セル B2**: `30`

### 読み込みサンプルコード

```go:main.go
package main

import (
	"fmt"       // フォーマット済み入出力用パッケージ
	"log"       // ログ出力用パッケージ
	"strconv"   // 文字列⇔数値変換用パッケージ
	"github.com/xuri/excelize/v2" // Excel操作用パッケージ
)

const filePath = "input.xlsx" // 読み込むExcelファイルのパス

func main() {
	f, err := excelize.OpenFile(filePath)
	if err != nil {
		log.Fatal(err)
	}
	// シート "Sheet1" のセルから値を取得
	name, err := f.GetCellValue("Sheet1", "A2")
	if err != nil {
		log.Fatal(err)
	}
	ageStr, err := f.GetCellValue("Sheet1", "B2")
	if err != nil {
		log.Fatal(err)
	}
	age, err := strconv.Atoi(ageStr)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("{Name:%s, Age:%d}\n", name, age) // {Name:John, Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"log" // ログ出力用パッケージ
	"github.com/xuri/excelize/v2" // Excel操作用パッケージ
)

const filePath = "output.xlsx" // 書き込み先のExcelファイルパス

func main() {
	f := excelize.NewFile()
	// 新しいシート "Sheet1" を作成
	sheetName := "Sheet1"
	index := f.NewSheet(sheetName)
	// ヘッダーの書き込み
	f.SetCellValue(sheetName, "A1", "Name")
	f.SetCellValue(sheetName, "B1", "Age")
	// ユーザー情報の書き込み
	f.SetCellValue(sheetName, "A2", "John")
	f.SetCellValue(sheetName, "B2", 30)
	// アクティブなシートを設定
	f.SetActiveSheet(index)
	// Excelファイルを保存
	if err := f.SaveAs(filePath); err != nil {
		log.Fatal(err)
	}
}
```

## ini

https://pkg.go.dev/gopkg.in/ini.v1

### inputファイル

```ini:input.ini
[User]
Name = John
Age = 30
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"fmt"        // フォーマット済み入出力用パッケージ
	"log"        // ログ出力用パッケージ
	"gopkg.in/ini.v1" // INIファイル操作用パッケージ
)

const filePath = "input.ini" // 読み込むINIファイルのパス

func main() {
	cfg, err := ini.Load(filePath)
	if err != nil {
		log.Fatal(err)
	}
	name := cfg.Section("User").Key("Name").String()
	age, err := cfg.Section("User").Key("Age").Int()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("{Name:%s, Age:%d}\n", name, age)
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"log"        // ログ出力用パッケージ
	"gopkg.in/ini.v1" // INIファイル操作用パッケージ
)

const filePath = "output.ini" // 書き込み先のINIファイルパス

func main() {
	cfg := ini.Empty()
	sec, err := cfg.NewSection("User")
	if err != nil {
		log.Fatal(err)
	}
	sec.NewKey("Name", "John")
	sec.NewKey("Age", "30")
	if err := cfg.SaveTo(filePath); err != nil {
		log.Fatal(err)
	}
}
```

## XML

https://pkg.go.dev/encoding/xml

### inputファイル

```xml:input.xml
<?xml version="1.0" encoding="UTF-8"?>
<User>
    <Name>John</Name>
    <Age>30</Age>
</User>
```

### 読み込みサンプルコード

```go:main.go
package main

import (
	"encoding/xml" // XMLエンコード/デコード用パッケージ
	"fmt"         // フォーマット済み入出力用パッケージ
	"log"         // ログ出力用パッケージ
	"os"          // ファイル操作用パッケージ
)

// User構造体はXMLデータのユーザー情報を表現します
type User struct {
	XMLName xml.Name `xml:"User"`
	Name    string   `xml:"Name"`
	Age     int      `xml:"Age"`
}

const filePath = "input.xml" // 読み込むXMLファイルのパス

func main() {
	// 読み込み専用でXMLファイルをオープン
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	var user User
	// XMLデコーダーでファイルの内容をUser構造体にデコード
	if err := xml.NewDecoder(file).Decode(&user); err != nil {
		log.Fatal(err)
	}
	// デコードしたユーザー情報を出力
	fmt.Printf("%+v\n", user) // {XMLName:{Space: Local:User} Name:John Age:30}
}
```

### 書き込みサンプルコード

```go:main.go
package main

import (
	"encoding/xml" // XMLエンコード/デコード用パッケージ
	"log"         // ログ出力用パッケージ
	"os"          // ファイル操作用パッケージ
)

// User構造体はユーザー情報を保持します
type User struct {
	XMLName xml.Name `xml:"User"`
	Name    string   `xml:"Name"`
	Age     int      `xml:"Age"`
}

const filePath = "output.xml" // 書き込み先のXMLファイルパス

func main() {
	// 書き込み可能な状態でXMLファイルをオープン（存在しなければ作成、存在すれば上書き）
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	user := User{
		Name: "John",
		Age:  30,
	}

	encoder := xml.NewEncoder(file)
	encoder.Indent("", "    ")
	if err := encoder.Encode(user); err != nil {
		log.Fatal(err)
	}
}
```

## まとめ

今回の記事では、Go言語を用いた各種ファイルフォーマットの読み込みと書き込みのサンプルコードを紹介しました。JSON、YAML、CSV、TXT、EXCEL、INI、XMLと多岐にわたるファイル形式に対応するコード例は、現場で頻繁にファイル操作を行う私自身の経験に基づいており、実務ですぐに活用できる内容となっています。各サンプルコードは公式パッケージの仕様に準拠しているため、初学者から実務者まで参考にしていただければ幸いです。