Go体験の続き

とりあえず、ファイルを開いて、そのファイル中の自分の欲しい行を表示する的なプログラムを書いてみることにする。
perlで書くとこんな感じの。

#!/usr/bin/perl
use strict;
use warnings;
my $filename = shift;
my $keyword = shift;
open(IN, $filename) or die $!;
while(<IN>){
  print $_ if ( $_ =~ /$keyword/ );
}
close(IN);
__END__

rubyで書くとこんな感じの。

#!/usr/bin/ruby
filename = ARGV[0]
keyword = ARGV[1]
open(filename) do |f|
  puts f.read.split("\n").select{|v| v.include?(keyword) }
end

上記のperlrubyのscriptはファイル中からキーワードを含む行を探すプロセスが違うけど、まあ得られる結果は同じなんで、そんな感じで。それから、エラー処理もしてないけど、まあそれは本質的な問題じゃない。

引数を取る

A Tour of Goに載っているRotting Catの例を見ると、引数の取り方が書いてあった。それを参考に、引数をただ表示するだけのプログラムarg.goを書いてみる。

package main
import (
    "fmt";
    "flag";
)
func main(){
    flag.Parse();
    if flag.NArg() != 0 {
        for i := 0; i < flag.NArg(); i++ {
            fmt.Printf("%s\n", flag.Arg(i));
        }
    } 
}

これを、コンパイルして、実行すると以下のようになる。

 % 8g arg.go
 % 8l arg.8
 % ./8.out test 1 2 3
 test
 1
 2
 3
 % 

ファイルを開く

では、次にファイルを開く。同様に、A Tour of Goに載っているRotting Catの例にファイルを開く方法などが書いてあるので、それを参考に、次のようなhello.goファイルからhelloという文字列を含む行を表示させるretrievehello.goというプログラムを作ってみることにする。

package main
import "fmt"
func main() {
	fmt.Printf("hello world.\n")
}

まず、$GOROOT/docs/progs/file.go をコンパイルしておく。

 % 8g file.go

では、まずファイルを開くだけのプログラム。

% cat retrievehello.go
package main
import(
    "./file";
    "fmt";
    "os";
)
func main() {
    file, err := file.Open("hello.go", 0, 0);
    if file == nil {
        fmt.Printf("can't open file; err=%s\n", err.String());
        os.Exit(1);
    }
    const NBUF = 512;
    var buf [NBUF]byte;
    file.Read(&buf);
    file.Close();
}

ファイルが存在するかどうか?のエラーチェックだけしてるのは、オレが中途半端な人間だから。とりあえず、hello.go存在下でこのプログラムをコンパイルして実行する分には、何のエラーも表示されないので、多分動いてるんだろう。

というわけで、ファイル中から、文字列を探す関数を作りたいんだけど、ひとまずファイルを読んで、そのまま出力するプログラムを作る。もう、A Tour of Goに載っているRotting Catをほぼ丸写しだけど、文句あるか、と。

package main
import(
    "./file";
    "fmt";
    "os";
)
func retrieve(f *file.File){
    const NBUF = 512;
    var buf [NBUF]byte;
    for {
        switch nr, er := f.Read(&buf); true {
        case nr < 0:
            fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", f.String(), er.String());
            os.Exit(1);
        case nr == 0:
            return;
        case nr >0:
            file.Stdout.Write(buf[0:nr]);
        }
    }
}
func main() {
    file, err := file.Open("hello.go", 0, 0);
    if file == nil {
        fmt.Printf("can't open file; err=%s\n", err.String());
        os.Exit(1);
    }
    retrieve(file);
    file.Close();
}

これで、hello.goの中身がまるっと表示されるプログラムができた。ここで、先程の成果を合わせて、引数で指定したファイルを開くようにすると、mainの先頭をちょっといじれば良い。

package main
import(
    "./file";
    "flag";
    "fmt";
    "os";
)
func retrieve(f *file.File){
    const NBUF = 512;
    var buf [NBUF]byte;
    for {
        switch nr, er := f.Read(&buf); true {
        case nr < 0:
            fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", f.String(), er.String());
            os.Exit(1);
        case nr == 0:
            return;
        case nr >0:
            file.Stdout.Write(buf[0:nr]);
        }
    }
}
func main() {
    flag.Parse();
    var filename string;
    if flag.NArg() == 0 {
        filename = "hello.go";
    }else{
        filename = flag.Arg(0);
    }
    file, err := file.Open(filename, 0, 0);
    if file == nil {
        fmt.Printf("can't open file; err=%s\n", err.String());
        os.Exit(1);
    }
    retrieve(file);
    file.Close();
}

文字列を探す

func retrieve()内で、ファイルの中身をbyte型の配列bufに読み込んでいるので、この中からキーワードを探すようにすれば良い。ファイルの中身をstring型とかで読んで、string.index()とかそんな感じでできたりしないんかい、、、、という気もするんだけど、ちょっとここで休憩。
今日はここまでってことにしよう。