ファイルまわりはシェル本体との密接な連携が必要なので、結局、io.*
以下の関数を全部自前バージョンで置き換えることにした。
最初からそうすればよかった。
あと、bit32.*
については、自前で途中まで作っていたが、GopherLua 用のBixData/gluabit32: A bit32 library for the GopherLua VMという既製のものを利用することにした。utf8.*
はないので、やはり自分で作るしかないようだ。
ファイルまわりはシェル本体との密接な連携が必要なので、結局、io.*
以下の関数を全部自前バージョンで置き換えることにした。
最初からそうすればよかった。
あと、bit32.*
については、自前で途中まで作っていたが、GopherLua 用のBixData/gluabit32: A bit32 library for the GopherLua VMという既製のものを利用することにした。utf8.*
はないので、やはり自分で作るしかないようだ。
Go言語は例外がないのでアウトという人が多いわけですが、自分は「例外が取り扱いにくいシーン」というものを経験しているので、その点は実は肯定的なんですよね。
たとえば、コンストラクタで「様々な例外」が発生する時、下手に try の範囲を広げると「予期せぬ場所での例外」まで拾ってしまうので、できればコンストラクタだけを小さく try で囲みたいのです (try の範囲を広げるのであれば System.Exception ではなくて、細かい例外を全部列挙しなければいけない)
しかしながら
Try Dim foo = New System.IO.StreamWriter(fname,false,System.Text.Encoding.Default) Catch ex as Exception End Try
だと foo が他で参照できなくて困るので、結果として
Dim foo as System.IO.StreamWriter Try foo = New System.IO.StreamWriter(fname,false,System.Text.Encoding.Default) Catch ex as Exception End Try
と書かなくてはいけません。でも、これ面倒くさいですよね。結局は
Try '*** 事前に書き込むフォルダーが存在するか程度は確認する if 文などは書いておく *** Using foo As New System.IO.StreamWriter(fname,false,System.text.Encoding.Default) End Using Catch ex As Exception MsgBox(ex.ToString) ' このあたりは本当はもっとちゃんと書いてるので、ツッコミ勘弁 End Try
みたいに書くようになってしまいました。むむむ
うん、標準入力をうまいこと GopherLua 内の標準入力にアサインできていないせい で、GopherLua の開発元に相談してみた。
「そんな簡単にいかへんでー」という話なので(いや、わしもそう思てた)、ちょっと何かうまい方法考えてプルリクを送ってみるかー(決意)
package main import ( "reflect" ) type privateOne struct { m1 string m2 string } func sub(from interface{}) interface{} { t := reflect.ValueOf(from).Elem().Type() obj := reflect.New(t) p := obj.Elem() p.Field(0).SetString("foo") p.Field(1).SetString("bar") return obj.Interface() } func main() { value := sub(&privateOne{"a", "b"}) if val, ok := value.(*privateOne); ok { println(val.m1, val.m2) } else { println(reflect.TypeOf(value).String()) } }
実行してみよう!
$ ./privateStruct.exe panic: reflect: reflect.Value.SetString using value obtained using unexported field goroutine 1 [running]: reflect.flag.mustBeAssignable(0x1b8) c:/go/src/reflect/value.go:231 +0x1fa reflect.Value.SetString(0x478260, 0xc042052040, 0x1b8, 0x492f30, 0x3) c:/go/src/reflect/value.go:1551 +0x32 main.sub(0x473aa0, 0xc042052020, 0x0, 0x4660bc) C:/Users/hymko/go/src/github.com/zetamatta/experimental/privateStruct/main.go:16 +0x141 main.main() C:/Users/hymko/go/src/github.com/zetamatta/experimental/privateStruct/main.go:22 +0x97 exit status 2
普通に怒られた。ちなみに m1 → M1 、m2 → M2 などとフィールドの名前を変えると、期待動作する。
$ ./privateStruct.exe foo bar
仕様動作を無理やり超えようとしたので、無理かなーとは思ってたんだが、やっぱりか!残念!(強引な解決が無理なら、ライブラリの開発元への「お願い」が通るのを期待するしかないなー)
これを解決するには勝手に変な解釈がされないように、あくまで「文字列」としてセルに貼るプログラムを作ればよろしい。
仕様のイメージとしては「変換プログラムを「送るメニュー」とか、拡張子 CSV に割り当てると、CSVファイルをクリックした時、自動で Excel シートに安全に変換される」というのが望ましい。
拡張子に割り当てる、送るメニューに登録するとなると、スクリプト言語よりは、EXEファイルの方がよい。となると Go言語が適している。
Go言語で開発するとなると、2つのアプローチがある
「1.」は速度的に有利だが、テンポラリな .xlsx ファイルを作成してしまう。いきなり独立した .xlsx ファイルではなく、一度内容をチェックしてから、問題ないことを確認してから、ちゃんとした名前で保存する形の方が個人的には望ましく思えた。
ということで「2.」のアプローチを採用した。
で、完成品がこちらに。
変換した後、起動した Excel は敢えてそのままに。加工なり、セーブなりはユーザが自分でしてくださいということで(その方が「自分は」使いやすいと思った。もちろん .SaveAs や .Quit も出来るが、余計なお世話感強い)
"encoding/csv"
で読み込む
"unicode/utf8"
.Valid で UTF8 であれば UTF8 で、さもなければ現在のコードページ(日本語環境なら cp932=ShiftJIS)と解釈する.Value
プロパティーに代入するのではなく、直前に .NumberFormatLocal
プロパティーに "@"
を設定して、セルの型を文字列にする。
C:\> type foo.csv | pipe2excel
C:\> pipe2excel foo.csv bar.csv
いやー、しかし、Windows の分散オブジェクト(COM)の仕組み、それを操作する go-ole ライブラリ、Go言語の標準ライブラリ群の充実度はすばらしいぞ、デミウルゴス
以上
map のキーは文字列限定だが、値については任意の型 Ok
import ( "reflect" "sort" ) // SortedKeys makes sorted strings' array from keys of the given map whose key's type is string. func SortedKeys(mapInt interface{}) []string { values := reflect.ValueOf(mapInt).MapKeys() result := make([]string, len(values)) for i, value1 := range values { result[i] = value1.String() } sort.Strings(result) return result }
こんな感じで使う
for _, key := range texts.SortedKeys(BoolOptions) { val := BoolOptions[key]
reflect を使ってるから、たぶんすごく遅いんだろうなぁ
昔作った子プロセスが想定どおり呼ばれているかテストするためのスタブモジュールを MIT ライセンスで公開します。 Windows用ですが、.NET Framework 4.0 以降が必要です。
zetamatta/genestub: Stub module to test the child-process is called as expected.
実行すると、GUIのタブで起動コマンドラインや環境変数を表示したり、標準出力やエラー出力、ERRORLEVELを設定できます。
受託開発では、責任範囲の切り分けのために、担当する機能単位で、実行ファイルを分けることも多いんですよね。そういった時に他のモジュールをちゃんと想定通り呼び出せているかをログを足さずにチェックするツールがあれば便利だと、会社でこっそり作りました。
ずっと、自分の bin ファイルに置いておいたものですが、結構、一般向けに使えるかと思いましたので、会社にあるモジュールへの依存コードの履歴を塗りつぶして(おいおい)、公開することにしました。
利用する際は、GENESTUB.exe という名前そのままではなく、呼ばれる子プロセスのモジュール名にリネームしてご利用ください。
以上です。