VBAとjar間で対話的に標準入出力を行う

背景

つかっているシステムがVBAだった

VBAクラス

モジュールでなくクラスなので注意

' 対話的にjarとやり取りを行う
' JarOperator.cls
Option Explicit
Dim Wsh As Object
Dim Exec As Object

' jar側の標準入力完了サイン
Const EOS = "EOS"

' 標準出力を取得
Public Function AquireStdOut(St As String) As String

    Exec.StdIn.WriteLine St
    
    Dim Line As String, nLine As String
    Line = vbNullString
    nLine = vbNullString
    
    ' jar側には終わりのサインをつけるようにする
    Do While nLine <> "EOS"
        Line = nLine
        nLine = Exec.StdOut.ReadLine
    Loop
    
    AquireStdOut = Line
End Function

' 実質コンストラクタ
Public Sub SetJarPath(Path As String)
    
    ' jarファイル呼ぶときダブルクオーテーションで囲まないといけない
    Path = """" + Path + """"
    
    Dim cmd As String
    cmd = "java -jar " + Path
    
    Set Wsh = CreateObject("WScript.Shell")
    Set Exec = Wsh.Exec(cmd)
    
    ' 標準入力待ちまで繰り返す
    Exec.StdOut.ReadLine
End Sub

Private Sub Class_Terminate()
    ' 後始末
    Exec.Terminate
    Set Exec = Nothing
    Set Wsh = Nothing
End Sub

使い方

以下のようなjarファイルを作ります

// ShellTest.scala => ShellTest-assembly-0.1.jar
object Test {
var isFirst = true

def main(args: Array[String]): Unit = {
    if (args.length != 0) println(args(0))
    println("launched")
    io("")
}

def io(str: String): Unit = {
    // 実際は分岐させること想定
    str match {
      case _ => {
        if (isFirst) {
        isFirst = false
        } else {
        println(s"echo $str")
        println(s"EOS")
        }
        io(scala.io.StdIn.readLine())
    }
    }
 }
}

javaにPathを通して次のように使います

Option Explicit
Sub Test()
    Dim operator As New JarOperator
    operator.SetJarPath ("ShellTest-assembly-0.1.jar")
    Debug.Print operator.AquireStdOut("うさぎ")
    Debug.Print operator.AquireStdOut("おいし")
    Debug.Print operator.AquireStdOut("かのやま")
    Set operator = Nothing
End Sub

出力

echo うさぎ
echo おいし
echo かのやま

ScalaでJDBCのレコードを取り出す話

JDBC_Scalaメモ

ScalaからJDBC経由でMySQLにつなげた時の健忘録

まずはconnを確立

chatacterEncodingとTimezoneを伝えないと怒られる。SSLは使わないことを明示しないと怒られる。

// OperateJDBC.scala

import java.sql._  // これだとArray使えないので注意
object OperateJDBC {

  val driver = "com.mysql.cj.jdbc.Driver"
  Class.forName(driver)
  val url = "jdbc:mysql://localhost:3306/sakila" +
    "?characterEncoding=UTF-8&" +
    "serverTimezone=JST&" +
    "useSSL=false&" +
    "requireSSL=false"
  val conn = DriverManager.getConnection(url, ___, ___)
}

SELECTからとりだす

データは以下の通り

actor_ID first_name
1 PENELOPE
2 NICK
3 ED
4 JENNIFER
5 JOHNNY

まずはこんな感じでベタに実装してみる。

// OperateJDBC.scala

  // mapper。とりあえずSMALLINTだけ。
  def patternMatchObj(obj: Object, cName: String, cTypeName: String)
: (String, Any) = {
    (obj, cName, cTypeName) match {
      case (Some(o), cname, "SMALLINT UNSIGNED") =>
        cname -> o.toString.toInt
      case (Some(o), cname, _) => 
        cname -> o.toString
      case (None, cname, _) => 
        cname -> None
    }
  }

  def select(sql: String): Seq[Map[String, Any]] = {
    val stmt = conn.createStatement()
    stmt.execute(sql)
    val rs: ResultSet = stmt.getResultSet
    val md: ResultSetMetaData = rs.getMetaData
    val colCount: Int = md.getColumnCount
    val colName: Seq[String] = (1 to colCount)
      .map(md.getColumnName)
    val colTypeName: Seq[String] = (1 to colCount)
      .map(md.getColumnTypeName)

    var ret: Seq[Map[String, Any]] = Seq()
    while (rs.next()) {
      ret = ret :+ (1 to colCount)
        .map(n => Option(rs.getObject(n)))
        .zipWithIndex
        .map(t => patternMatchObj(t._1, colName(t._2), colTypeName(t._2)))
        .toMap
    }
    rs.close()
    stmt.close()

    ret
  }

テスト用コードで動かす

// TestJDBC.scala
object TestJDBC {
  def main(args: Array[String]): Unit = {
    val ret = OperateJDBC
      .select("SELECT actor_id, first_name FROM actor WHERE actor_id BETWEEN 1 AND 5")
    ret.toString.replaceAll("\\),", "),\r\n").tap(println)
  }

  implicit class RichObj[T](obj:T){
      def tap[U](f: T => U):T = {f(obj);obj;}
    }
}

出力

List(Map(actor_id -> 1, first_name -> PENELOPE),
 Map(actor_id -> 2, first_name -> NICK),
 Map(actor_id -> 3, first_name -> ED),
 Map(actor_id -> 4, first_name -> JENNIFER),
 Map(actor_id -> 5, first_name -> JOHNNY))

takeWhileを使ったパターン

varとか使っていてScala警察におこられそうなので、以下を参考にして実装

http://kmizu.hatenablog.com/entry/20121128/1354115941

  def select(sql: String):Seq[Map[String, Any]] = {
    val stmt = conn.createStatement()
    stmt.execute(sql)
    val rs: ResultSet = stmt.getResultSet
    val md: ResultSetMetaData = rs.getMetaData
    val colCount: Int = md.getColumnCount
    val colName: Seq[String] = (1 to colCount).map(md.getColumnName)
    val colTypeName: Seq[String] = (1 to colCount).map(md.getColumnTypeName)

    Iterator
      .continually{
        (1 to colCount)
          .map(n => Option(rs.getObject(n)))
          .zipWithIndex
          .map(t => patternMatchObj(t._1, colName(t._2), colTypeName(t._2)))
          .toMap
      }
      .takeWhile(x => rs.next())
      .toSeq
  }

出力。

Exception in thread "main" java.sql.SQLException: Before start of result set

多分takeWhilers.next()する前に判定しているので例外が出る。最初に判定するwhileにしたいけど方法が分からない。

Iterator.continuallyってなんだ

APIDocumentから引用。無限長Iteratorを返すらしい。

def continuallyA: Iterator[A] Creates an infinite-length iterator returning the results of evaluating an > expression.

ぐぐる

無能なので有能な先例をしらべる。

https://stackoverflow.com/questions/9636545/treating-an-sql-resultset-like-a-scala-stream

無名クラスを使ってIteratorのhasNextとnextをoverrideする方法があるらしい。今回だと以下のようになる。

new Iterator[Map[String, Any]] {
    override def hasNext: Boolean = rs.next()
    override def next(): Map[String, Any] = {
    (1 to colCount)
        .map(n => Option(rs.getObject(n)))
        .zipWithIndex
        .map(t => patternMatchObj(t._1, colName(t._2), colTypeName(t._2)))
        .toMap
    }
}.toStream

IteratorToListなんかでimmutableにすると、hasNext=>next()=>hasNext=>next()=>...と呼びだされ、hasNext == falseの時に止まる。

Iterator継承パターン

最終的にこうなった。

val ret = new Iterator[Map[String, Any]] {
    override def hasNext: Boolean = rs.next()
    override def next(): Map[String, Any] = {
    (1 to colCount)
        .map(n => Option(rs.getObject(n)))
        .zipWithIndex
        .map(t => patternMatchObj(t._1, colName(t._2), colTypeName(t._2)))
        .toMap
    }
}.toList
rs.close()
stmt.close()
ret

めでたしめでたし。

List(Map(actor_id -> 1, first_name -> PENELOPE),
 Map(actor_id -> 2, first_name -> NICK),
 Map(actor_id -> 3, first_name -> ED),
 Map(actor_id -> 4, first_name -> JENNIFER),
 Map(actor_id -> 5, first_name -> JOHNNY))

WindowsのIntelliJのScalaConsoleで文字化けする話

やること

起動/デバック構成のVMオプションに-Dfile.encoding=MS932を入れる。

f:id:usagisagi:20180421000351p:plain

winのコンソールはMS932を介しているからだと思います。

f:id:usagisagi:20180421000553p:plain

文字コードは悪い文明。いやほんとに。

ちなみに

Javaと違ってMainクラスはなく、ObjectにしないとHelloWorldできない。

object Sample {
  def main(args: Array[String]): Unit = {
    println("こんにちわ")
  }
}

東京マラソン2018回想記

概要

f:id:usagisagi:20180228204604p:plain

これはかけっこで常に最下位を取っていたアラサーが、無謀にも東京マラソン2018に参加した時の記録である。

2/11 (二週間前)

15km走りシンスプリント(脛が痛い)を発症する。ただ、足の激痛と引き換えに「トランクスで走ると太ももとパンツが擦れるので痛い」という、とても重要な気づきを得たので無駄ではなかった。

2/24(前日)

参加エントリーをするために東京ビッグサイトへ向かう。

エントリー窓口はコミケにも負けない長蛇の列が形成されていた。これほど長い列を見たのは夏コミの東方星蓮船を買いに行った時か、あるいは叶姉妹の時位か。でも当たり前だが、並んでいるのは、年齢層に幅こそあれ、オタクでは無くスポーツマン。やっぱり来たのは間違いだったかと一抹の不安を覚えつつ、痛む足を気にしながら並ぶ。

会場に入って腕にプラスチック(?)のような認証用の腕輪を巻かれる。少し厚いビニールのようなものなのに、これが外れた場合出走停止になると聞いたので大層ビビったものだった。実際は、レース後ジムで外そうしたら素手ではなかなか外れないし破けない。心配する必要は無かった。

その後、ゼッケンや記念Tシャツを貰い企業ブースへ。ランナー特典を提供している所が多数あり、これだけで参加費の元が取れるのではと思うほどお得感あふれていた。ただ、同じ製薬企業でもH光はサンプルをくれたのにO塚はくれなかった。こういう所でも企業の姿勢が見える。ただH光は「7割歩いていても6時間行くことは稀」とか言っていて、信じて歩いたら関門ギリギリだった。企業の姿勢が見える。

ランニンググッズも充実していた。ここに来るときは帰りに明日の準備をしなくてはとも思っていたが、実際はここで不足している全てが揃った。買ったものは、お土産と汗対策のリストバンド(TOKYO2018仕様)、ウェストポーチ、栄養剤のBCAAセット、靴下、ふくらはぎ用のサポーター。一番役に立ったアイテムはふくらはぎ用のサポーターで、これはCW-X製と間違えて買ったRxL製のものだったが(blogを書いている今気づいた)、脛をいい感じにサポートしてくれてとても役に立った。

あとは有名な選手らしい人が喋っていたり、ランニングクリニックとか開催していたりしていたけど、良く知らなかったり恥ずかしかったりしたのでスルー。

2/25(当日)5:30

ちゃんと目覚まし通りに起きれる。第0関門突破。

脛に湿布を張りその上からシンスプリント向けのテーピングを行う。テーピングは普段やらないので大層不格好なものになり、これで本当にサポートできるのか大層不安だったが、本番のレース時には大層役にたった。

7:05 新宿都庁

今年からコースが変わり新宿スタートになったらしい。腕輪の認証や荷物の金属探知を済ませ玄関口で待機。セキュリティは厳重に見えたが、チェックを済ませた荷物に貼る「Security checked」シールが道路にボロボロ落ちていて、本当にセキュリティしているのか不安になった。

ここでゼッケン先頭の意味を知る。どうやらゼッケンはAが準エリート(超はやい)、以下申告タイム順にB、C、D……と続き、スタート順はアルファベット順になるらしい。私のゼッケンを見るとL。どうやら最後尾らしい。周りを見ると、お年を召している方や全身コスプレマンでもE組とかF組とかだったりし、L組の私はなんだか恥ずかしくなり、ウィンドブレーカーでゼッケンを隠し、ギリギリまで着ることになった。

8:45 並ぶ

他のみんなは道路に並ぶのだが、Lの人たちは近くの公園に誘導された。公園にはLの仲間たちが沢山おり、スタート直前にも関らずトイレしていたり呑気なものだった。

9:05 スタート ~ 5km

スタート。スタート時刻と同時に発砲音が聞こえ少しビビった。テロかと思った。

スタートの合図がしてもL組はスタートできない。15分位待機を命じられてようやく少しずつ動き始める。スタートゲートをくぐるまで大体19分位かかった。

スタート1kmはゴミだらけでマラソンというより障害物競争。東京ゴミマラソン。ゴミは放置するなと案内にあったのになんだこのマナーの悪さ。道路中央にポンチョが投げ捨てられ一歩間違えるとコケる。というか確かコケてた人いた。タイツ履いていたけど大丈夫だったか。

スタートから暫くは人口密度が高く、ランナーとの接触に神経を使った。この密度は15kmぐらいまで続く。恐らく東京マラソンは記録を目指すのには向いていないだろう。しかしその中をビュンビュン追い抜くA組のネコミミランナーは何者だったのか。

5km ~ 10km

トイレに寄るけれども凄い人が並んでいた。15分位待つと言われ、関門が危うそうなので泣く泣くトイレを我慢した。

この時、少し歩いたのだが予想以上に足に力が入らず驚いた。走っている方が楽なのだが、これは体重をかけた反動で足を出しているからであり、股関節(大腿骨骨頭)に負担をかけるので優しくない。大腿骨骨頭は大切にしないと手術になったり歩行に後遺症を残すのである。

10km ~ 15km

人が空いてきたのでトイレ休憩を行う。並んでいる間に関門時刻をチェックするが、意外とギリギリなことに気づく。恐らく全レース通して20分以上関門に差をつけていない。

反対車線は28km地点でトップランナーとすれ違う。トップランナーはなんか軽やかに走っていて、28kmは私もいけそうな気になるが、実際私が28km地点についたときは死にそうであった。

足が痛くて1km1kmが遠い。普段走るとき1kmはそれほどでもない距離なのだが凄く遠かった。1km毎に標識があり、標識の後大分走って、標識を見落としたのかな?2、3km位は走ったのかな?って思うのだがやっぱり1kmしか進んでいない。無常。

15km ~ 20km

ここら辺から歩く人が目立つ。

遂に脛が痛くなくなるが、代わりに腰が痛くなり始める。

そういえば下駄で走っている人がいたけど彼は完走できたのだろうか。

20km ~ 25km

私も歩き始める。ただチアガールの応援に反応して走る位には若干体力は残っていた。

時折自分の姿を見るがあまりにも不格好な走るオタクで恰好がつかない。この時ダイエットを決意した。

そしてお腹が減る。給食が配られるらしいが、食べられるものはみかんしか残っていなかった。でも、みかん凄いおいしかった。酸っぱさが体に染みわたった。ただ道路に皮を投げ捨てるのはいかがなものか。

25km ~ 30km

このあたりは関門が厳しく、関門ギリだとキロ7分で走らないと駄目なゾーン。もう走れないので必死に歩いていた。腰が本当に痛い。

時折「ナイスラン!」と声援を受けるが、実際は私は歩いている。歩いているのに「ナイスラン」と言われるはずはないので、それは私に向けた声援でないことは明白であったが、凄く罪悪感を感じた。応援してくれる人にハイタッチもできなかった。次がもしあるならば、せめて完「走」したい。そう決意した。

30km ~ 35㎞

関門内の制限時間は25km ~ 30kmが35分と一番短いが、30km ~ 35kmも40分と(歩きにとっては)油断ならない時間。近くにいた人は「せめて30kmは行きたいね~」と口にしていたが、30km超えても気は抜けない。「ナイスラン」の声援に耐えながらひたすら歩く。

35km ~ 40km

この関門内の制限時間は70分。つまり35kmで実質ゴール。あとはゴールまで歩くだけなのだが、歩くだけでも辛い。腰の横が痛い。でも痛いが、ここまで来たので完走したい。そう思いながら歩いた。

40km ~ 42.195km

残り1kmは走るかとも思っていた、が、足が動かない。ただ、ゴール直前は曲がり角になっており、最後200m位を走れば、観客から見たら完「走」してきたように見える仕掛けとなっていた。ありがたい。

記録はネットで6時間半、スプリットで6時間45分とギリギリ(制限時間はスプリットで7時間)。確かH光の説明では7割歩いても6時間いくことは稀との話であったが、ふたを開けてみるとギリギリ。企業の姿勢が見える。

何はともあれ完走は完走。もう足を動かさなくてよい。めでたしめでたし。

家に帰るまでが東京マラソンです

社会人なのでゴールしたら家に帰らないといけない。

足は生まれたてのバンビになっているにも関わらず、荷物を受けとるまでに1km位歩かされる。気が抜けているし、歩くたびに腰に激痛が走るのにも関わらず「立ち止まらないで下さい」の標識。完走記念メダルやバスタオルを受け取るが、感動よりも座りたい。

荷物を受け取ったら電車に乗って家に帰らないといけない。生まれたてのバンビの足で立ち向かった有楽町駅の下り階段は、東京マラソンで味わった中で一番の絶望を与えてくれた。そこにはチアガールも「ナイスラン!」の応援もない。ただ、絶望と、絶望に負けて階段に座りこむランナー達がいるだけであった。

次がもしあるならばタクシー代を用意しよう。そう決意した。

データベーススペシャリスト体験記

f:id:usagisagi:20171015163057p:plain

データベーススペシャリストを受けてきたのでそのメモです。

経歴

  • 社会人経験4年
  • 薬系理系修士
  • 非IT系企業
  • DBの経験はテーブルを眺める程度
  • 資格は応用情報

使った参考書

www.amazon.co.jp

色々買いましたが、振り返ってみるとこれ1つで済みました。過去問の解説が(他のものと比べて)量質共に非常に優れています。参考書部分も無駄がない。 ただ解答用紙が狭いので、iTECからDLするとよいと思います。

他参考書は、スタディワールドの教科書、SQLドリル、リレーショナルデータベース入門、データベース設計の参考書などを買いましたが不要でした。

勉強方法

午前2

過去問道場でひたすら演習です。ただ、データベースだけでなく他分野も無視できない分量が出ますので、余裕があれば応用情報の復習をしとくとなお安心です。特にセキュリティはケアしとくと良いと思います。

午後1

過去問でひたすら演習。H28~H16あたりまで2回は回しました。

ネット見てますと「SQLは捨てる」とかよくありますが、私はよほどの事情が無い限りどの分野も勉強した方がよいと思います。今回もSQLは問2問3両方出ており避けられません。といってもSQLは参考書を全てカバーする必要はなく、DMLのSELECT文を勉強すれば十分です。

後午後2以上に時間が足りなくなりがちです。午後1だからといってあまり甘く見ないことです。

午後2

これも過去問でひたすら演習。H28~H16あたりまで2回は回しました。

最初はあまりの分量に辟易しましたが、ネテロよろしく量をこなす毎に読むスピードが速くなり、最後のあたりは逆に楽しかったです。午後2は実務経験がないと全く歯が立たないと聞いていたので怖かったですが、実際はそんなことはなく過去問演習で十分にカバーできる範囲だと思います。

以下に概念設計問題の簡単なコツを示します。

  • 最初に問題・見出しを見て全体の構成を頭に入れる。但し、途中で要件変更等があっても必要以上に怖がらず、最初から読む。
  • 問題文中のE-R図を用いて、今読んでいるところはどこかを常に頭に入れる。迷子にならないよう常に気を付ける。
  • E-R図中のリレーションが表す関係は何か、テーブルが表す単位は何かを常に考える。本文中の『各』や『毎』などは大ヒント。
  • 候補キーは何かを意識する。『ID』や『番号』は最重要。
  • よく出るパターンを身につけておく。『ヘッダ』-『明細』、『履歴管理』など。よく出るパターンは先述の参考書にも載っていますし、過去問演習をしていけば次第に慣れると思います。
  • 設問ですでに記入されている"暗黙のルール"には必ず従う。属性の命名やサブタイプ分割で迷うときはこの"暗黙のルール"に従うといける場合が多いです。私が受けた2017春午後2はその典型例だと思います。
  • 諦めない。読み進めていくうちに設問のデータベースの"勝手"が分かってきますので読むスピードはどんどん上がっていきます。たとえ、最初の1Pを読むのに20分程度かかったとしても十分間に合います。

感想

本試験では、午後1は問1,3を選択して2回見直すことが出来、問2を選択した午後2も十分に見直す時間が取れました。結果は午後で90点を超え、非常に満足できる結果でした。

実務でも試験で身に着けたスキルは役に立っています。応用情報では知識として頭に入っていれば何とかなりましたが、デスペでは使えるようになるまで反復練習をするので実務でもある程度使えるレベルまで引き上げられたと思います。名だたる大企業でも多対多のデータモデル図を平気で出してくるんですね……。でも私口出す立場にないからしーらないっ。

今後

システム監査技術者に特攻します。

pythonのcgi moduleのめも

社内でpython製のものをデモするために簡単なcgiを作ろうとしたら、意外と引っかかったのでメモ。

cgi.FieldStorage() → 辞書型

postやgetを受けるcgi.FieldStorage()は、辞書型のようで辞書型でなく、バグの温床になりそうなので変換して使う。

import cgi
form = cgi.FieldStorage()
form_dict={}
for key in form.keys():
    form_dict[key] = form.getvalue(key)

cgi.FieldStorage() のエンコード指定

cgi.FieldStorage(encording='utf-8')

webは何があるか分からないのでencodeを指定する

<input type=“checkbox”>の判定

チェックが入るとcgi.FieldStorage()["key"].valueにstring型で'on'と入る。チェックがないときはcgi.FieldStorage()にキー自体が無い。
ただ、仮に'off'とかが入ってくると怖いので、strの'on'に等しいかで判定する。

tmpフォルダ

matlabplot.pyplotなどで一時ファイルを使うとき、cgi-bin直下にtmpフォルダを作ると権限の関係でうまく動かない。tmpフォルダはcgi-binと同階層に置くこと。
後、一時ファイルを作成した後は、使う前にos.chmod等で権限を付与することも忘れない。

shphinxを使ってPythonのコードからAPIドキュメント作成

無駄に苦労したのでメモ

  • shphinxのquick_start autodocはy
  • 20行目あたり,conf.pyの以下のコメントアウトを外し、sys.path.insertの表記を[srcフォルダ] へのパスにする

# import sys
# import os

# sys.path.insert(0, os.path.abspath('.'))

import sys
import os

sys.path.insert(0, os.path.abspath('[srcDirectoryPath]'))
  • extensionsの値を ["sphinx.ext.autodoc"]

  • コマンドsphinx-apidoc -f -o [出力フォルダ] [srcフォルダ]

  • index.rstの書換えmodulesの追加
.. toctree::
   :maxdepth: 2
   :caption: Contents:

   modules 
  • コマンドsphinx-build -b html [conf.pyのフォルダ] [ビルド先フォルダ]

おまけ


というかここがわかりやすかったので参考

bonbonbe.hatenablog.com