« 第九 本番 | Main | 最初からCで書いておけば・・・ »

VBScriptでバイナリファイルを読み書きする

後輩のBANGから相談を受けました。

「Tambourineさんは、VBは得意ですか?」

むむっ?僕様ちゃんがVBを使っていたのは遙か昔、まだ16bitだった頃であるがそれでもお役に立てるかの?そもそも、なんでVB?

「いや、Windowsで実行出来るものだったら、何でもいいんですけど」

ふむふむ。やりたいことはなんじゃ?

「あるファイルに決まったバイト数ごとに改行を入れたいのです」

なるほど。自称Rubyistに聞いていることはわかっておるのじゃろうな?

  #!/usr/bin/ruby
  f = open(ARGV[0],"r")
 
  i = 1
  rlen = ARGV[1].to_i
 
  f.each_byte do |b|
    print b.chr
    if i < rlen
      i += 1
    else
      print "\n"
      i = 1
    end
  end

どうじゃ、もってけ。礼なら晩飯をおごる以上のことは要らんぞ?

「Rubyはお客様のPCに入っていないからダメです」

わがままじゃの・・・。Exerbでruby.exeをくっつけたろか・・・。まあ、そんならCででも書いたらよかろう。getchしてputchして、時々、13とか10とか書き込んで終わりじゃ。

「コンパイラ、持ってません」

GCCぐらい自分のPCに入れておくのがSEのたしなみだぞ?いざというときのメイキンストールに困るジャマイカ。まあ、実は私も今、そらでCを書くのは大変、自信がないのじゃが。随分、書いてないからのぉ・・・

そうじゃ、ここの所、バイトでVBScriptをいじっていたのだが、あれなら良かろう。動かす予定のPCにWSHは入っているな?

「なんですか、それ?」

知らぬか・・・。とりあえず、コマンドプロンプトでcscriptって叩いてみんしゃい

「あ、なんか出ました」

なら、大丈夫。ちと、10分ほど待っててね・・・うんとしょぃ!

  Set fso = CreateObject("Scripting.FileSystemObject")
  Set f1 = fso.OpenTextFile(WScript.Arguments(0))
  rlen = CInt(WScript.Arguments(1))
 
  Do Until f1.AtEndOfLine
    line = f1.Read(rlen) & Chr(13)
    WScript.StdIn.Write line
  Loop

これでよかんべ。

「Tambourineさん、ダメっす。日本語が入るとずれます」

なんじゃと?むむむ・・・、2バイト文字もひっくるめて文字数を数えて、バイト数とはずれるのか。しまった・・・。しかも、Excelとかと同じように、この時点ですでにUNICODEになっているんじゃろうなあ。

「Excelで同じことになったことがあります。Excel VBAではこんな感じで解決出来ました」

strOut = StrConv(MidB(StrConv(strTmp, vbFromUnicode), pos, rlen), vbUnicode)

えーっと、読み込んだ文字列を一度、UNICODEからSJISに変換して、MidBでバイトごとに切って、さらにUNICODEに戻すのか。

・・・BAD!

ばーーーーっど!!こんなはしたないコードは認めん!

そもそも、テキストだと思ってファイルを開くのが良くないんじゃな。・・・FileSystemObjectを使う限りはだめみたいだ。

ここから、解析機関Gを叩きまくり。ふむふむ、ADODB.Streamってのを使えばいいらしいぞ。どうやってもADODB.Streamのメソッド一覧とかが見つからないのは、何故だろう。

ちょちょいと実験。

  Dim sIn,sOut
 
  inputfile = WScript.Arguments(0)
  outputfile = WScript.Arguments(1)
  rlen = CInt(WScript.Arguments(2))
 
  Set sIn = CreateObject("ADODB.Stream")
  Set sOut = CreateObject("ADODB.Stream")
 
  sIn.Type = 1 ' バイナリーモード
  sOut.Type = 1
  sIn.Open
  sOut.Open
 
  sIn.LoadFromFile(inputfile)
   
  Do Until sIn.EOS
    line = sIn.Read(rlen)
    sOut.Write line
  Loop
 
  sOut.SaveToFile outputfile, 2 '上書きモード

うむ。とりあえず、inputfileとoutputfileで同じ物は出てくる。この間に改行を出せばいいんだな

とりあえず、最後のループをこんな感じにしてみた。Byte型だったら書けるだろうと思って、CByteしてみる。

  Do Until sIn.EOS
    line = sIn.Read(rlen)
    sOut.Write line
    sOut.Write CByte(13)
  Loop

どうだっ!

ADODB.Stream: 引数が間違った型、許容範囲外、または競合しています。

お前の日本語が間違っとる!

ま、型が違うというとるんだから、違うんだろうなあ・・・。えと、そもそもADODB.Streamで読み込んだら、どんな型なんだ?

  Do Until sIn.EOS
    line = sIn.Read(rlen)
    WScript.Echo TypeName(line)
    sOut.Write line
  Loop

Byte()と表示される。なるほど、Byte型じゃなくて、Byte型の配列じゃないとダメなんだな。んじゃ、配列に突っ込んでみよう。

  Dim bCr(1)
  bCr(0) = CByte(13)
 
  Do Until sIn.EOS
    line = sIn.Read(rlen)
    sOut.Write line
    sOut.Write bCr
  Loop

うりゃ!

ADODB.Stream: 引数が間違った型、許容範囲外、または競合しています。

強情だというのは、臆病だということですよ・・・((C)鴻上尚史)

また、解析機関Gに聞いてみる。こんなサイトがみつかった

http://sei.qee.jp/program/hta/sample/bstream.html

WindowsXPの標準機能だけで生のバイナリデータを扱いたくて調べたら、ADODB.Streamというオブジェクトが使えるとわかりました。ところが喜んだのも束の間。確かにファイルからの読み書きはできますが、自分で新規にデータを作れないようなのです。何しろTypeNameで調べたデータ型 (右図) が、VBScriptにはないのですから……。

がふっ。作れないのですか。そうですか

しかしここでくじけては技術屋ではない!というわけで、Byte()型の0x00~0xFFを配列に持つクラスを強引に作りました。理論上は、これでどんなデータでも生成できることになります

ぐれいと!

どらどら・・・。むう、要するに一つADODB.Streamをテキストモードで開き、そこに欲しいデータをChr()して書き込んで、今度はバイナリモードで読み出しちゃうんですな。なるほど、読んできたらByte()になってるんだからそれでいいわけだ。

というわけで、完成版はこちら

  Dim sIn,sOut
 
  '改行コードを作成
  Set sTmp = CreateObject("ADODB.Stream")
  sTmp.Type = 2 'テキストモード
  sTmp.Charset = "shift_jis"
  sTmp.Open
 
  sTmp.WriteText vbCrLf
 
  sTmp.Position = 0
  sTmp.Type = 1
 
  bCrLf = sTmp.Read(2)
 
  Set sTmp = Nothing
 
  '本体処理
  If WScript.Arguments.Count = 3 Then
    Set sIn = CreateObject("ADODB.Stream")
    Set sOut = CreateObject("ADODB.Stream")
 
    sIn.Type = 1
    sOut.Type = 1
    sIn.Open
    sOut.Open
 
    sIn.LoadFromFile(WScript.Arguments(0))
    rlen = CInt(WScript.Arguments(2))
   
    Do Until sIn.EOS
      line = sIn.Read(rlen)
      sOut.Write line
      sOut.Write bCrLf
    Loop
 
    sOut.SaveToFile WScript.Arguments(1), 2
  Else
    WScript.Echo "引数エラー"
  End If

ばっちりだぜ!・・・しかし、ここまで時間をかける意味があったかどうかは謎だなあ。

|

« 第九 本番 | Main | 最初からCで書いておけば・・・ »

パソコン・インターネット」カテゴリの記事

Comments

> 「あるファイルに決まったバイト数ごとに改行を入れたいのです」
> 「Tambourineさん、ダメっす。日本語が入るとずれます」

この時点で要件がおかすぃ、というツッコミはなしかい。

Posted by: nac | December 09, 2006 11:39 AM

別におかしくないよ。

日本語のフィールドがある固定長データを、Excelに読み込みたいという、素朴な願いだから。

世の中の人は、かくもExcelが大好きですから・・・

Posted by: Tambourine | December 09, 2006 12:41 PM

Greatings, Everything dynamic and very positively! :) Thanks Elcoj

Posted by: Elcoj | March 23, 2009 10:34 PM

複数テキスト・ファイルを結合するスクリプトに、公開していただいている、貴コードを使わせていただきました。
ご了解をお願いします。

バイナリで読み書きするので、文字コードを意識しなくて良い点が、特長です。

貴重なノウハウを教えていただき、感謝いたします。

Posted by: 渡辺真 | February 05, 2011 10:54 AM

私のオリジナルな部分なんてないようなものですし、了解するも何もありません。お役に立てたなら幸いです

Posted by: Tambourine | February 05, 2011 08:30 PM

「wsh バイナリファイル分割」キーでここへ、失礼とは思いますが、途中の軽妙なボヤキに共感し思わず吹き出しちゃいました。(^^)
参考にさせていただきます。ありがとうございました。
 F1GP感なども覗かせていただきますね。

Posted by: 定年プログラマ | July 03, 2012 10:41 PM

Post a comment



(Not displayed with comment.)


Comments are moderated, and will not appear on this weblog until the author has approved them.



TrackBack


Listed below are links to weblogs that reference VBScriptでバイナリファイルを読み書きする:

« 第九 本番 | Main | 最初からCで書いておけば・・・ »