« 第九 本番 | 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 at 11:39 AM

別におかしくないよ。

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

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

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

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

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

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

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

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

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

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

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

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

Posted by: 定年プログラマ | July 03, 2012 at 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

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/47905/12989670

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

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