F#からExcelファイル(BIFF8)の読み込み

Bravo2.Excel
http://www.vector.co.jp/soft/winnt/prog/se399617.html

 というライブラリを使わせて頂きました。


何度もシート名を指定したくないので一度Selectしたらシートオブジェクトをキャッシュするようにしました。
 また、C# のような using ステートメントが使えるかは試していませんが、念のために一応IDisposeを実装してます。


クラスを定義するのに結構時間が掛かり、簡単にはF#に慣れないものだなと感じます。


[使い方]

let xls = new ExcelReader(path)

// 指定したシート名のA1の値を取得
if xls.Select(name) then
    xls.["A1"]
        |> printfn "A1=\"%s\""

// シート名を全て出力
xls.Sheets
    |> List.iter (fun a -> printfn "%s" a)

xls.Close()
</pre>


[ラッピングしたソースコード]

namespace Common

open System
open Bravo2.Excel

type ExcelReader private(book, sheetNameList) =
    let mutable book : ExcelWorkBook  = book
    let mutable sheet : ExcelWorkSheet = null
    let mutable sheetNameList = sheetNameList

    // Dispose
    member public this.Dispose () =
        if book <> null then
            book.Close()
            book <- null

    // Dispose
    interface IDisposable with
        override this.Dispose() = this.Dispose()

    // Close
    member public this.Close () = this.Dispose()

    /// シートが選択されているか取得
    member public this.IsSelect with get () = sheet <> null

    /// シートの選択
    member public this.Select (name : string) =
        let SET exp =
            if exp then
                sheet <- book.WorkSheets.Item(name)
            else
                sheet <- null
            exp
        sheetNameList
            |> List.exists (fun a -> a = name)
            |> SET

    /// シート名のListを取得
    member public this.Sheets with get () = sheetNameList

    /// 指定したセルの値を取得
    member public this.Item
        with get(address : string) =
            if this.IsSelect then sheet.Cells.Item(address).Value.ToString()
            else ""

    /// 指定したセルの値を取得
    member public this.Item
        with get(sheetname : string, address : string) =
            if this.Select(sheetname) then
                sheet.Cells.Item(address).Value.ToString()
            else ""

    /// 指定したセルの値を取得
    member public this.Item
        with get(row : int, col : int) =
            if this.IsSelect then sheet.Cells.Item(row, col).Value.ToString()
            else ""

    /// 指定したセルの値を取得
    member public this.Item
        with get(sheetname : string, row : int, col : int) =
            if this.Select(sheetname) then
                sheet.Cells.Item(row, col).Value.ToString()
            else ""

    /// 指定したセルの値を取得
    member public this.Range
        with get(address : string) =
            this.[address]

    /// 指定したセルの値を取得
    member public this.Range
        with get(sheetname : string, address : string) =
            this.[sheetname , address]

    /// 指定したセルの値を取得
    member public this.Range
        with get(row : int, col : int) =
            this.[row, col]

    /// 指定したセルの値を取得
    member public this.Range
        with get(sheetname : string, row : int, col : int) =
            this.[sheetname, row, col]

    /// コンストラクタ
    public new (path) =
        let book = new ExcelWorkBook(path)
        book.Open()
        let sheetCount = book.WorkSheets.Count - 1
        let list = [
            for i = 0 to sheetCount do
                yield book.WorkSheets.Item(i).Name
        ]
        new ExcelReader(book, list)
;;

 ちなみに、RangeとItem両方定義しているのは、Itemというのを使いたくないためです。
インデクサを提供するにはItemというプロパティでなければ出来なかったので、仕方なくRangeを新しく追加しています。
まあ、でもインデクサが主体になりそうなのであまり使わないかも・・・