iso tank - プログラムな?話

丸付き数字から丸囲み数字(丸数字)に変換

洋々亭にて、様々なVBAコードが公開されている。

この中に含まれている、丸付き数字(「○2」など)を丸囲み数字(「②」など)に変換する関数を、⓪~㊿まで対応できるように改造した。

ちなみになんでこんな関数が必要かというと、一部の法令(児童福祉法など)は、項番号の代わりに丸数字を使用していることがあるが、丸数字は機種依存文字(環境依存文字)なので正しく表示されないコンピュータが存在する恐れがあり、e-Govでは「○2」としている。これを本来意図するところの丸数字に置き換えるため。

現在ではほとんどの機種(環境)で丸数字が表示できるようになっており、これを一括変換したいという要望があったので、洋々亭のコードを拝借し、かつ範囲を広げた。

'---------------------------------------------------------------------------------------------------
' 丸付き数字置換関数
' ◆機能の説明
'   ・「○3」形式の表記を「③」に変換する
'   ・一部文字列はUnicode文字に変換する
'   ・一定数を超える数字の場合は丸囲み数字が使用できないため、角かっこで囲む(○51→[51])
'   ※項番号を上記の方法で表記している法令があるための対処(児童福祉法等)。
' オリジナル:洋々亭 2006
'---------------------------------------------------------------------------------------------------
Private Function ToCircledNum(ByRef srcStr As String) As String
    
    Dim srcLen As Long      '引数の文字列長(○を含む)
    Dim tmpAsc As String    '引数から○を除去し半角英数に変換した文字列
    Dim chrNum As Long      '変換後の数字
    Dim i As Long           'イテレータ
    
    'まず全角数字を半角数字に置き換え、次に半角数字を丸数字に置き換える。
    Let srcLen = Len(srcStr)
    If srcLen < 2 Then
        Exit Function
    End If
    Let tmpAsc = String$(srcLen - 1, vbNullChar)
    For i = 2 To srcLen
        Mid(tmpAsc, i - 1, 1) = Mid$("0123456789", CLng(InStr("0123456789", Mid$(srcStr, i, 1))), 1)
    Next i
    Let chrNum = CLng(tmpAsc)
    Select Case chrNum
    Case Is < 0                                 '0未満は非対応([]で括る)
        Let ToCircledNum = "[" & tmpAsc & "]"
    Case 0                                      '丸0  = U+24EA( 9450) chrNum=0のためそのまま
        Let ToCircledNum = ChrW(9450)
    Case Is < 21                                '1~20は普通に丸囲み数字が使用できる
        Let ToCircledNum = Mid$("①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳", chrNum, 1)
    Case Is < 36                                '丸21 = U+3251(12881) 21から始まるため 12881 - 21 = 12860
        Let ToCircledNum = ChrW(12860 + chrNum)
    Case Is < 51                                '丸36 = U+32B1(12977) 36から始まるため 12977 - 36 = 12941
        Let ToCircledNum = ChrW(12941 + chrNum)
    Case Else                                   '51以上も非対応([]で括る)
        Let ToCircledNum = "[" & tmpAsc & "]"
    End Select
End Function

「引数には丸記号+全角英数字の組み合わせの文字列しか来ない」という前提で作ってる関数なので、呼び出し前に正規表現やLikeマッチングでのふるい落としが必要。簡単な判定は19行目でやってるけど(引数が1文字以下なら何も返さず終了)。

やってることは洋々亭のmarumoji関数と一緒。まず全角英数字を半角英数字に置き換え(22~25行目)、整数型に変換(26行目)。あとはSelect Case文で0未満と50超は対応する丸数字がないため[]で括る、⓪と㉑~㊿はChrW関数で文字コードから文字を取得。①~⑳は文字を並べてMid関数で対応する数字の位置に該当する文字を取得。

①~⑳もChr関数を使えば取得できるが、文字を並べてMid関数で取得した方がちょっとだけ早い。

Public Sub SpeedTest()
    Dim testNums(2000000) As Long
    Dim dummyStr As String
    Dim procTime As Single
    Dim i As Long
    For i = LBound(testNums) To UBound(testNums)
        Let testNums(i) = RandomLong(1, 20)
    Next i
    Let procTime = Timer
    For i = LBound(testNums) To UBound(testNums)
        Let dummyStr = Mid$("①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳", testNums(i), 1)
    Next i
    Debug.Print "Test1: " & Timer - procTime        'Test1: 0.140625
    Let procTime = Timer
    For i = LBound(testNums) To UBound(testNums)
        Let dummyStr = Chr(34623 + testNums(i))
    Next i
    Debug.Print "Test2: " & Timer - procTime        'Test2: 0.171875
End Sub

Private Function RandomLong(ByRef minVal As Long, ByRef maxVal As Long, Optional ByRef doRandomize As Boolean = True) As Long
    If doRandomize Then
        Randomize
    End If
    Let RandomLong = Int(Rnd * (maxVal - minVal + 1)) + minVal
End Function

⓪~㊿を全部並べてMid関数で取得した方が分かりやすいしできるならそうしたいんだけど、VBAは内部的にUnicode文字を使用しているものの、VBEはUnicode文字をサポートしていない

ChrW関数などを使用してUnicode文字をサポートしている出力先(例えばWord文書など)に出力すること自体は可能なので、こういった書き方になった。もっとスマートな方法はないだろうか。

Pythonいじり

なんだか暇なので、レスポンスヘッダ表示装置を国際化ドメイン名に対応させてみた。

作るにあたって色んな国際化ドメイン名を調べてみたけど、中国とか台湾とかやたら多いのな。

あと下記はhttplibライブラリの例外クラスの引数のメモ。どこにも書いてなかったからさ。

httplib.InvalidURL
引数の持つプロパティ:message
httplib.NotConnected
引数の持つプロパティ:message
httplib.UnknownProtocol
引数の持つプロパティ:message, version
httplib.IllegalKeywordArgument
※なぜかraiseしただけでInternalServerErrorになるなーと思ったらPython 2.6.2にはない例外だった。
SourceForge参照。
httplib.UnknownTransferEncoding
引数の持つプロパティ:message
httplib.UnimplementedFileMode
引数の持つプロパティ:message
httplib.IncompleteRead
引数の持つプロパティ:message, expected, partial
httplib.CannotSendRequest
引数の持つプロパティ:message
httplib.CannotSendHeader
引数の持つプロパティ:message
httplib.ResponseNotReady
引数の持つプロパティ:message
httplib.ImproperConnectionState
CannotSendRequest、CannotSendHeader、ResponseNotReadyの親クラス。
引数の持つプロパティ:message
httplib.BadStatusLine
引数の持つプロパティ:message, line
httplib.HTTPException
httplib下の例外クラスすべての祖。
引数の持つプロパティ:message

Pythonのお勉強

Pythonのお勉強の一環でレスポンスヘッダを表示するスクリプトを自作してみた。

なんだかんだ他のサイトにもあるけど広告とか不要なものとかがウザかったので。作ろうと思ってからだいたい1日でできあがった。何かをやろうとして完成するまでの現在最速記録なんじゃないかな。えっへん。

新たな問題

前回非常に困ったことになったわけだが、とりあえず別フレーム(leftとか)からスクリプトを呼び出して メインのフレーム(mainとか)のhtmlを書き換える方法に成功したのでまずこの回避策でやっていくことにした。

で、$(function(){})はdocument読み込み時にしか(つまりleftを読み込まないと)動作しないので、mainのページだけで遷移すると イベントを取得できない、つまり書き換えができない。なのでmainを読み込んだら同時にleftも読み込めよという命令をmainの方に入れた。 そしたら全体を更新するとleftを2回呼んじゃうんだよね。効率悪いけど仕方ないよね。

で、

新たな問題。jQuery.ajaxを使ったスクリプトでcache:falseにしていると、ページを再読込してスクリプトを実行するたびに呼び出すxmlファイルのデータサイズ分だけメモリ使用量が肥大化していくことが判明。 たぶんIE6とか古いブラウザ限定で発生するようだ。ChromeとかIE8では再現しなかった。メモリリーク・・・というのだろうか?

とりあえず、IE6の人がいたらこのテストページを開いて、タスクマネージャとかそういうメモリ監視するツールとにらめっこしてみてほしい。 たぶんページを更新するたびに1MBぐらいずつ使用メモリが増えていく。

助けてください。本気で。

俺にプログラミングその他を教えてくれ。できたら今直面している問題を解決して。

ブラウザ:
ソフトウェアベンダーが独自開発したアプリケーションに載っている、これまたベンダー独自開発のIEコンポーネントブラウザ。 ちなみにインストールされているIEのバージョンは6あたり
俺が作ろうとしたプログラム:
jQueryを利用した小さなプログラム。その日の日直や宿直を表示する。
現象:
$.ajaxで、success: function(foo){var(foo)}とすると、その関数は正常に完了するが、そのページでPDFを開こうとするとIEのスクリプトエラーが発生。 「インターフェイスがサポートされていません」と出る。が、そのまま続行するとPDFは普通に開ける。htmlは何のエラーもなく開ける。
サイト:
いまだに<frameset>とか<frame>とか使ってるうんこな内部用サイト。
今のところ判明していること:
  • たとえばjQueryを使わずに”document.write("Hello, World!");”とかすると普通に表示される。 なので発生のタイミングはおそらくjQueryでデータの読み取りに成功して関数が呼び出された直後から。
  • コマンドプロンプトで”regsvr32 urlmon.dll”を実行してからアプリケーションを再起動すると、エラーなくPDFが開けるようになる。 でもPC再起動するとまたエラー発生。
  • うんこなフレームを使ってるサイトなので、ためしにpdfを別ページに開くように”target=_top”とかしたらうまくいった。 どうやらこのプログラムを呼び出しているページにpdfを呼び出そうとすると?エラーが起こるらしい。他のページに移動とかしたが状況まったく同じ。
とりあえずどうしよう:
  • PCにWindowsUpdateをかけてIE近辺のプログラムを更新すれば直るかもしれないが、下手に更新かけてシステムに悪影響が出たらかなり困る。主に俺が死ぬ。 あと作ったプログラムが余計に動くなったらやっぱり俺が悲しみで死ぬ。だいたいプログラム一個の都合でシステムを更新するってどうよ。ねぇ。
  • 日直や宿直を表示する部分をフレーム分けてさらに別ページにしようかと思ったがレイアウト的にカオスになるので困っちゃん。
  • つまりスクリプトを呼び出しているページにPDFを呼び出さなきゃいいわけだから、<frameset>とか<frame>とか書いちゃってるindex.htmlで スクリプトを呼び出して、つまりいわゆるleft.htmlだとかright.htmlだとかいうところの日直表示テーブルを更新できないかとか。 なんかその類のHPを見ると色々方法書いてるんだけど、なぜかうまくいかない。getElementsByTagNameはなにも返さないしcontentWindowも働いていないかも。 しかもそれがjQueryになるとどうすればいいのかもっとわからない。

とりあえず仙台のヨドバシ行ってjQueryの本とajaxの本を買ってきた。今日家帰ったら読んではみるけどこんなピンポイントな解決方法書いてるわけないだろ。

PC、爆発しなさい。

あ、家のPCは爆発しちゃだめだからね。