I/Oまわり練習

Groovyでとにかくなんか作ってみよう、ということで題材探し。
しばらく前の日経ソフトウェアスクリプト言語の特集があったことを思い出した。2003年9月号のPerlRubyの特集。「スクリプト言語の便利さを実感する五つの例題」という記事があった。ファイルを読み書きしているし丁度よさそう。でも解答は当然PerlRubyのコード。PerlRubyもまったく読めないけど解説を頼りに挑戦してみよ。

  • 例題1 メールアドレスをマージしてソート

1000行のメールアドレスを収めたファイルを読み込み、メールアドレスの重複を省いて並べ替える。

import java.io.*
import java.util.*

file = new File(args[0])

file.withReader {reader |
    list = reader.readLines()     
    set = new TreeSet(list)
    
    set.each {
        println it
    }
}
  • 例題2 CSVのカラムを入れ替える

4カラムからなるCSVファイルの2番目と3番目を入れ替える

import java.io.*
import java.util.*

file = new File(args[0])

file.withReader {reader |
    list = reader.readLines()     
    
    list.each {
        cols = it.tokenize(",")
        Collections.swap(cols, 1, 2)
        println cols.join(",")
    }
}
  • 例題3 複数ファイルのサイズと最初の3文字を一覧

異なるデータが入っている1000個のテキストファイルのサイズと最初の3文字を一覧にする。テキストファイルはあるディレクトリかそのサブディレクトリに入っている。

import java.io.*

dir = new File(args[0])
tree dir

def tree(dir) {
    num = 3
    
    if (dir.isFile()) {
        
        dir.withReader { reader |
            // charの配列が使えないので1行読み取り
            line = reader.readLine()
            if (line.size() >= num) { 
                chars = line.substring(0,num)
            } else {
                chars = line
            }
            
            println "name=${dir.name} size=${dir.length()} chars=${chars}"
        }
        // なにかを明示的に返さないとおこられるのでnullを返す
        return null 
    } 
    
    array = dir.listFiles()
    
    if (array == null) {
        return null
    } 
    
    // array.each {...}の中で再帰呼び出しをしようと
    // したけどおこられたのでforをつかう
    for (file in array) {
        tree file
    } 
}
  • 例題4 例題2と例題3の結果をExcel形式で保存

(記事にはExcelとはOLEで連結するとあるけれど、OLEってよくわからない。POIでできそうだしとりあえず気にしないことにしよう。)

import org.apache.poi.hssf.usermodel.*
import java.io.*

book = new HSSFWorkbook()

files = []
args.each {
    files << new File(it)
}

files.each { file |
    sheetName = file.name.tokenize("\\.").getAt(-2)
    sheet = book.createSheet(sheetName)
    rowIdx = 0
    
    file.eachLine { line |
        row = sheet.createRow(rowIdx++)
        cellIdx = 0
        line.tokenize(",").each { 
            cell = row.createCell((Short)cellIdx++)
            cell.setCellValue(it)
        }
    }
}

// 変数streamのクラスはjava.io.BufferedOutputStream
new File("test.xls").withOutputStream { stream |
    book.write(stream)
}
  • 例題5 HTMLの構文をチェックする

(時間切れ。また今度挑戦する、眠いよ...)


気づいたことなど

  • 配列の要素数取得にarray.lengthとできない。array.size()とする。
  • Javaのようにreturnだけの記述じゃだめなのか?(明示的に何かを返さなければいけない?)
  • eachのようなクロージャを使ったループ内からはクラスに属さないメソッドを呼べない?(普通のメソッドに対しての呼び出しは試してない)
  • 上記のコードはgroovy-1.0-beta-4で作った。違うバージョンではまた違った書き方が可能になるかもということに注意。とくにプリミティブ型の配列。