ファイルのメールアドレスを置換してみよう

本連載は、今注目を集めているGo言語について、より楽しく学べる連載です。二回目の今回は、Go言語の制御構文について学びましょう。その実用例として、ファイル一覧を列挙して、HTMLファイルに書かれているメールアドレスを置換するプログラムを作ってみましょう。

Go言語でファイルの一覧を取得する方法

最初にGo言語でファイルの一覧を取得する方法を学びましょう。そのためには、path/filepathパッケージにあるGlob関数を利用します。例えば、プログラムと同じディレクトリにあるファイル一覧を表示するプログラムは、以下のようになります。

package main
import (
  "fmt"
  "path/filepath"
)
func main() {
  // ファイル一覧を得る --- (*1)
  files, _ := filepath.Glob("*")
  // ファイルを一つずつ表示する --- (*2)
  for i, name := range files {
    fmt.Println(i, "=", name)
  }
}

上記のプログラムを「glob.go」という名前で保存します。そして、コマンドラインから以下のように入力して実行します。

go run glob.go

すると、カレントディレクトリにあるファイルすべてを以下のように表示します。

0 = glob.go
1 = if.go
2 = ...
  • プログラムを実行するフォルダのなかのファイルが図のように表示される

    プログラムを実行するフォルダのなかのファイルが図のように表示される

プログラムを確認してみましょう。プログラムの(*1)の部分ではGlob関数を利用してファイルの一覧を列挙します。Go言語では、関数が複数の値を返すことができます。そのため、Glob関数の値を受ける左辺では、「files」と「_」の二つを変数を指定しています。なお、Go言語で「_」を指定すると、不要なので捨てることを意味します。実際、Glob関数の第一戻り値にはファイルの一覧が文字列のスライス(配列のようなもの)で得られます。そして、第二戻り値には何かエラーが起きたとき、そのエラー内容が入ることになっています。

そして、プログラムの(*2)の部分では、ファイルの一つずつについて繰り返し表示処理を行います。このスライスに対して繰り返し処理を行う場合には、for ... range ... 構文を記述します。繰り返し構文の中では、繰り返し回数を表す変数iと、実際のファイル名nameの二つを画面に出力しています。

Go言語の制御構文について

さて、今回は、Go言語の制御構文の使い方をマスターしましょう。そもそも、制御構文とは、プログラミング言語において重要な働きをする構文です。これは、条件分岐や繰り返しを行う構文の総称です。基本的に、プログラムは上から下に実行しますが、制御構文を使うことで、処理を分岐したり、同じ処理を繰り返したりすることができます。

if構文について

Go言語の条件分岐構文であるIfは、次のように記述します。このとき、else以下は省略することができます。

[書式] 条件分岐 if

if 条件式 {
  // 条件が真の時の処理
} else {
  // 条件が偽の時の処理
}

例えば、変数aとbのどちらが大きいかを調べるプログラムは、以下のようになります。

package main
import "fmt"
func main() {
  a := 40
  b := 30
  if a <= b {
    fmt.Println("a <= b")
  } else {
    fmt.Println("a > b")
  }
}
  • ifによる条件分岐で変数aと変数bの大きさを比較し、条件に合った出力を行っている

    ifによる条件分岐で変数aと変数bの大きさを比較し、条件に合った出力を行っている

また、Go言語らしい記述方法として、ifの条件式の前に単文を指定することができるようになっています。これにより、if文の条件を説明するような変数の指定などを行うことができます。

if limit := 100; n > limit {
 // limitが100超のときの処理をここに記述
}

for構文について

続いて、繰り返し構文のfor構文を確認してみましょう。for構文の書式は、次のようになっています。

[書式] for 構文
for 初期化文; 条件式; 増加文 { // 条件式が真の間ここを実行 }

[書式] for 構文 (範囲を繰り返す場合)
for インデックス, 値 := range 配列など { // 条件式が真の間ここを実行 }

[書式] for 構文 (他の言語のwhileに相当)
for 条件式 { // 条件式が真の間ここを実行 }

書き方が三種類あるので、ちょっと複雑に見えるかもしれませんが、それほど難しいものではありません。以下の実際のプログラムを確認してみましょう。

package main
import ("fmt")
func main() {
  // 10回の繰り返し--- (*1)
  for i := 0; i < 10; i++ {
    fmt.Println(i)
  }
  // 配列を繰り返し --- (*2)
  a := [10]int{0,1,2,3,4,5,6,7,8,9}
  for i, value := range a {
    fmt.Println(i, ":", value)
  }
  // 他の言語のwhileに相当する繰り返し --- (*3)
  b := 0
  for b < 10 {
    fmt.Println(b)
    b++
  }
}
  • 3種類のfor文による繰り返しの例

    3種類のfor文による繰り返しの例

それぞれ、0から9までの数字を表示するだけのプログラムですが、実際のfor構文の使い方が分かるものになっています。(*1)は単純な10回の繰り返しを記述したもの、(*2)は最初に配列を定義して、その配列の内容を順に表示するもの、(*3)は他の言語のwhileに相当する使い方で、条件式が真の間繰り返し実行するというものになっています。

複数ファイルに書かれているメールアドレスを置換してみよう

ここまでの部分で、Go言語の制御構文の基本的な使い方が分かったところで、より実用的なプログラムを作ってみましょう。以下は、カレントディレクトリにあるHTMLファイルをくまなく巡回して、その中に書かれている「hoge@example.com」というメールアドレスを「fuga@example.com」に文字列置換するというプログラムです。

package main
import (
  "fmt"
  "io/ioutil"
  "strings"
  "path/filepath"
)
func main() {
  // HTMLファイル一覧を得る --- (*1)
  files, _ := filepath.Glob("*.html")
  // ファイルを一つずつ処理 --- (*2)
  for _, fname := range files {
    fileReplace(fname, "hoge@example.com", "fuga@example.com")
  }
}
// ファイルの中のsrcをdstに置換する関数 --- (*3)
func fileReplace(fname, src, dst string) {
  // ファイルを一括で読む --- (*4)
  bytes, _ := ioutil.ReadFile(fname)
  // 文字列置換 --- (*5)
  lines := strings.Replace(string(bytes), src, dst, -1)
  // ファイルへ書き込む --- (*6)
  result := []byte(lines)
  ioutil.WriteFile(fname, []byte(result), 0666)
  fmt.Println("ok:", fname)
}

上記のプログラムを「replace.go」という名前で保存します。そして、以下のコマンドを実行するとプログラムを実行できます。

go run replace.go

プログラムを実行すると、カレントディレクトリにあるHTMLファイルをすべてに対して、「hoge@example.com」を「fuga@example.com」への置換を行います。

  • replace.go実行前の様子

    replace.go実行前の様子

  • replace.go実行

    replace.go実行

  • replace.go実行後の様子

    replace.go実行後の様子

プログラムを確認してみましょう。(*1)の部分ではHTMLファイルの一覧を得ます。そして、(*2)の部分で列挙したファイルを一つずつ処理します。(*3)の部分ではファイルfnameの中のテキストsrcをdstに置換する関数を定義します。(*4)の部分では、ファイルを一括で読み込みます。ただし、読み込んだデータはバイト配列の型([]byte)です。これを文字列に変換するには、『string(byte配列)』のように記述します。(*5)の部分では、Replace関数を使って文字列置換を行います。(*6)の部分ではファイルへ書き込みます。ファイルへの書き込む場合、データはbyte配列である必要があるため、変換した後WriteFile関数を用いてファイルへ保存します。

まとめ

今回は、Go言語の制御構文について、掘り下げてみました。手軽に制御構文が記述できることが分かりました。また、利用例として、実際に簡単にファイルを列挙して、処理を行うプログラムを紹介しました。制御構文は、プログラミングの基本中の基本、しっかり使い方を覚えておきましょう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ、2010年 OSS貢献者章受賞。技術書も多く執筆している。