チュートリアル


べた書きGrADSデータの読み出し

GrADS のファイルを読み込む例を示して

を見ていきたいと思います。

ファイルアクセス

ファイルのオープンとクローズは File クラスまたは 組み込み関数 open を使用して
file = File.open(filename [,mode])  or  file = open(filename [,mode])
   :
file.close
または、ブロックを使用して
File.open(filename [,mode]){|file|
   :
}
とします。

読み込みには
line = file.gets            # 1行読み込む
str = file.read(n)          # nバイト読み込む
file.each_byte{|ch| hoge}   # 1バイトづつ読み込んでブロックを実行する
file.each_line{|line| hoge} # 1行づつ読み込んでブロックを実行する
をつかいます。 このイタレーターとファイルのオープンクローズを合わせたものが
File.foreach(filename){|line| hoge}  # File.open{|file|file.each_line{|line|hoge}}とほぼ同じ
lines = file.readlines               # linesは各行の配列
です。

書き込みは
file.puts(strings)
file.print(strings)
です。C++のように
file << str1 << str2
なんてこともできます。

では GrADS のコントロールファイルをよんでいきましょう
grads.ctl

文字列操作

ruby がもっとも得意とするのがこの文字列操作です。 String クラス には様々なメソッドが用意させており、 また強力な 正規表現 も使うことができます。

コントロールファイルの各行はスペースで区切られた要素から成り立っています。 この要素からなる Array クラス を作ってみましょう。
open('grads.ctl','r'){|file|
  ary = file.gets.split     # ary = ["DSET","^./data"]
}
簡単ですね。 String#split(pattern) は pattern で指定された区切り文字 or 正規表現で文字列を分割し、 それらの部分文字列の配列を返します。 pattern が省略されると $;の値(nilの場合' ')が使われます。
これらのデータの Hash クラス を作ります。
data = Hash.new
open('grads.ctl','r'){|file|
  while line = file.gets do
    ary = line.split
    data[ary[0]] = ary[1..-1]
  end
}
先頭がスペースの行は前行からの続きです。
if line =~ /^\s/ then
  data[last] = data[last]+ary
else
  last = ary[0]
  data[last] = ary[1..-1]
end
とします。

変数は別の Hash に入れたいと思います。
longname = Hash.new
unit = Hash.new
var = false
if line =~ /^VARS/ then
  nvar = line.split[1]
  var = true
elsif line =~ /^ENDVARS/ then
  var = false
elsif var then
  ary = line.split
  name = ary[0]
  str = ary[3..-1].join(' ')
  longname[name] = str[0..str.index('[')-2]
  unit[name] = str[str.index('[')+1..-3]
end

軸は少し工夫が必要です。
dims = Hash.new
data.each_key{|key|
  if key =~ /^.DEF$/
    ary = data[key]
    name = key[0].downcase
    if ary[1] == 'LINEAR'
      dims[name] = NArray.sfloat(ary[0].to_i).indgen!(ary[2].to_f,ary[3].to_f)
    elsif ary[1] == 'LEVELS'
      dims[name] = NArray.to_na( ary[2..-1].collect{|str| str.to_f} )
    end
    data.delete(key)
}

あとは個別の処理
data.each_key{|key|
  if key == 'TITLE' then
    data[key] = data[key].join(' ')
  elsif key == 'UNDEF'
    data[key] = data[key][0].to_f
  end
}
Array#join(str) は 配列の各要素を str を区切り文字とする文字列に変換します。

以上まとめると
grads1.rb

となり各データがそろったことになります。

バイナリデータの扱い

ではコントロールファイルが読めたところで 今度はバイナリファイルを読みたいと思います。
grads1.data
128(x) x 64(y) x 10(z) x 2(var) x 12(month) x 4(sfloat)
というようにデータが入っています。
NArray.to_na(str,"sfloat",dim0,dim1,..)
を使うと簡単です。
idim = x.length
jdim = y.length
kdim = z.length
tdim = t.length
u = NArray.sfloat(idim,jdim,kdim,tdim)
v = NArray.sfloat(idim,jdim,kdim,tdim)
file = open('grads.data','r')
for m in 0..tdim-1
  str = file.read(4*idim*jdim*kdim)
  u[true,true,true,m] = NArray.to_na(str,"sfloat",idim,jdim,kdim)
  str = file.read(4*idim*jdim*kdim)
  v[true,true,true,m] = NArray.to_na(str,"sfloat",idim,jdim,kdim)
end
file.close
もし NArray が無い場合は
ary = str.unpack('f*')
で Array が取り出せます。

もしこのデータのエンディアンが異なる場合 grads2.data
NArray.to_na(str,.....).swap_byte
str.unpack('e*') (リトルエンディアン)  or  str.unpack('g*') (ビッグエンディアン)
を使います。

あとはこのデータを煮ても焼いてもあなた次第です。


return