GOPARTS「テキストファイル・文字列操作」編

# はじめに

この夏、やっと職場へのGoのインストール許可を得られました。本業に勤しみつつ寸暇を見つけて「Webクライアント」「文字列カウンタ」「メール送信」「画像リサイザ」といった簡単なツールをGoで作成しました。小粒ながら何れも本業のWebシステムと連動して動作する立派なプロダクションツールです。

職場ではGoを頻繁に使えないため「いざツールを作るぞ！」となる都度、いろいろと思い出したり調べたりしながらコーディングしています。そこで、自分が良く使う処理のパーツとして「テキストファイル・文字列の操作」に関するものをざっくりとまとめてみました。

# パーツ

```go:構造体
type Date struct {
	Year  string
	Month string
	Day   string
}
```

## システム

GOMAXPROCSを最大CPU数に設定

```go:
	runtime.GOMAXPROCS(runtime.NumCPU())
```

環境変数を取得

```go:
	dsn := os.Getenv("GO_OCI8_CONNECT_STRING")
```

## エラー

生成

```go:
	var ErrArgSize = errors.New("Argument size error.")
```

## スライス

追加

* 長さ（第2引数）は0で生成する。
* 追加する個数が（ある程度）わかっている場合は、キャパシティ（第3引数）に指定する。
	* 内部でのメモリ操作が削減されるため。
* appendの戻り値を忘れずに。

```go:
	slc := make([]int, 0, 10)
	for i := 0; i < 10; i++ {
		slc = append(slc, i)
	}
```

## 書式化

%.2d - 数値なら2桁になるまで左ゼロ詰め。

```go
	d := fmt.Sprintf("%.2d/%.2d", 12, 3)
```

%s - 文字列ならそのままで。
%q - 文字列ならクォート付きで。

```go
	a := []string{"とうきょう", "とっきょ", "きょかきょく"}
	s := fmt.Sprintf("%s", a)
	q := fmt.Sprintf("%q", a)
```

%T - 値の型名を。

```go
	a := []string{"とうきょう", "とっきょ", "きょかきょく"}
	t := fmt.Sprintf("%T", date)
```

%v - 構造体なら{値,...}の文字列で。
%+v - 構造体なら{属性名=値,...}の文字列で。
%#v - 構造体なら構造体{属性名=値,...}の文字列で。

```go
	date := &Date{Year: "2014", Month: "12", Day: "3"}
	v := fmt.Sprintf("%v", date)
	pv := fmt.Sprintf("%+v", date)
	sv := fmt.Sprintf("%#v", date)
```

## ファイル情報

ディレクトリ配下のファイル一覧を取得

* 取得したファイルがディレクトリだった場合、その配下は参照されない。

```go
	fis, err := ioutil.ReadDir(dirname)
```

ディレクトリかどうか判定

```go
	if fi.IsDir() {
```

ファイルの状態を確認

* ファイル存在チェックとして代用。

```go
	_, err := fi.Stat()
```

## ファイル操作

オープン

* deferでのクローズを忘れずに。

```go
	fi, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer fi.Close()
```

ファイルの状態を確認

* ファイル存在チェックとして代用。

```go
	_, err := os.Stat(filename)
```

ファイルを一行ずつ処理

```go
	scan := bufio.NewScanner(fi)
	for scan.Scan() {
		text := scan.Text()
		// 以下、行に対する処理
		fmt.Println(text)
	}
	if err := scan.Err(); err != nil {
		return err
	}
```

読み込み

* 全部読み込んで[]byte化するので、サイズの大きいファイルでは要注意。

```go
	b, err := ioutil.ReadFile(filename)
```

書き出し

```go
	b := buf.Bytes()
	err := ioutil.WriteFile(filename, b, 0644)
```

## 日時

現在日時をtimeで取得

```go
	t := time.Now()
```

timeを文字列に変換

* "2006/01/02 15:04:05.000"はフォーマット文字列（年月日時分秒ミリ秒）。

```go
	s := t.Format("2006/01/02 15:04:05.000")
```

文字列をtimeに変換

* "2006/01/02"はフォーマット文字列（年月日）

```go
	t, err := time.Parse("2006/01/02", "2014/12/03")
```

スリープ

```go
	time.Sleep(10 * time.Second)
```

## 文字列

### 変換

int（整数）に変換

```go
	i, err := strconv.Atoi("12345")
```

[]byte（バイト配列）を変換

```go
	s := string(b)
```

### 操作

小文字化・大文字化

```go
	l := strings.ToLower("GoLang")
	u := strings.ToUpper("GoLang")
```

部分一致

* 平仮名は1文字が3バイトなのでidxは6。
* 一致しない時のidxは-1。

```go:
	idx := strings.Index("とうきょうとっきょきょかきょく", "きょ")
```

分割

```go:
	v := strings.Split("とうきょうとっきょきょかきょく", "きょ")
```

トリム

```go:
	s := strings.Trim("**きょ**", "*")
```

置換

* 第4引数にて、n<0の場合は全て置換。

```go:
	s := "とうキョうとっキョキョかキョく"
	r := strings.Replace(s, "キョ", "きょ", -1)
```

連結

```go:
	a := []string{"とう","うとっ","","か","く"}
	s := strings.Join(a, "きょ")
```

追記

```go:
	buf := bytes.NewBufferString("とうきょう")
	buf.WriteString("とっきょ")
	buf.WriteString("きょかきょく")
	// stringに変換
	s := buf.String()
	// []byteに変換
	b := buf.Bytes()
```

## JSON

構造体をJSONバイト配列に変換

```go
	date := &Date{Year: "2014", Month: "12", Day: "3"}
	b, err := json.Marshal(date)
```

JSONバイト配列を構造体に変換

```go
	b := []byte(`{"Year": "2014", "Month": "12", "Day": "3"}`)
	var date Date
	err := json.Unmarshal(b, &date)
```

## ハマった点

* 配列やスライスは定数として定義できません。

# おわりに

2014年、日本のGo界隈は「プロダクション投入の年」として盛り上がり、一部の企業でもGoの本格投入や本番稼働が実現されています。規模は小さ過ぎますが、自分もその流れに影響を受け、その波に乗れたことを嬉しく思っています。

皆さんも出来る範囲からGoを導入して、ガンガン実績を作って行きましょう！q@w@p