JScript.NET  新JavaScript入門  JavaScript,Neo-Generation  DOM  WSH  掲示板  表紙
Written 8/30/03
JScript.NET
入出力(2)
バイナリファイル
バイナリファイルの入出力には FileStreamクラスを用います。
次は、ファイルを1バイトずつ読んでは書いて、 ファイルをコピーするプログラムです。
    cp.js
    -------------------------------------------------------------------
    import System;
    import System.IO;
    
    var args : String[] = Environment.GetCommandLineArgs();
    if(args.length != 3) {
        System.Console.Error.WriteLine("usage : cp file1 file2.");
        Environment.Exit(1);
    }
    
    var ifs : FileStream;
    var ofs : FileStream;
    try {
        ifs = new FileStream(args[1], FileMode.Open, FileAccess.Read);
        ofs = new FileStream(args[2], FileMode.Create);
    }
    catch(e) {       //ファイルがオープンできない
        System.Console.Error.WriteLine("error : can't open file.");
        Environment.Exit(1);
    }
    var b : byte;
    while(ifs.Position != ifs.Length) {   //ファイル終端の判定
        b = byte(ifs.ReadByte());         //1バイト読む
        ofs.WriteByte(b);                 //1バイト書く
    }
    ifs.Close();
    ofs.Close();
 
入力・出力ストリームは次のように作成します。
    ifs = new FileStream(args[1], FileMode.Open, FileAccess.Read);
    ofs = new FileStream(args[2], FileMode.Create);
 
FileModeFileAccess は適当に設定します。
1バイト読むのは次のようにします。
    b = byte(ifs.ReadByte());
 
ReadByte の返り値はintなのでbyteにキャストしているところに注意してください。
1バイト書くのは次のようにします。
    ofs.WriteByte(b);
 
最後に入出力ストリームともCloseで閉じます。
Byte以外の入出力
Byteだけ読めても多くの場合はあまり役に立たないでしょう。 intなどのプリミティブデータ型の入出力には BinaryReaderBinaryWriter を用います。
次はGIFファイルから画像の幅・高さを抽出します。
    cp.js
    -----------------------------------------------------------------------
    import System;
    import System.IO;
    import System.Console;
    
    var args : String[] = Environment.GetCommandLineArgs();
    if(args.length == 1) {       //引数がない場合
        Console.Error.WriteLine("usage : gifinfo gif1 [gif2 ...].");
        Environment.Exit(1);
    }
    
    var fs : FileStream;
    var br : BinaryReader;
    var header : byte[] = new byte[6];
    var width : short;
    var height : short;
    //引数ごとに出力
    for(var i = 1; i < args.length; i++) {
        try {
            fs = new FileStream(args[i], FileMode.Open, FileAccess.Read);
        }
        catch(e) {       //ファイルがオープンできない
            Console.Error.WriteLine("error : can't open " + args[i] + ".");
            Environment.Exit(1);
        }
        br = new BinaryReader(fs);
        br.Read(header, 0, 6);           //6バイト読み飛ばし
        width = br.ReadInt16();          //7・8バイト目をshortで読む
        height = br.ReadInt16();         //9・10バイト目をshortで読む
        print("width  : " + width + "\nheight : " + height);
        fs.Close();
    }
 
これは次のように使います。
    >gifinfo b.gif
    width  : 104
    height : 122
 
まず、BinaryReader は次のようにFileStreamから得ます。
    fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
    br = new BinaryReader(fs);
 
幅・高さはGIFファイルのそれぞれ7・8バイト目と9・10バイト目に short(Int16)で書かれています。 そこで、Readで6バイト読み、 その後、ReadInt16で読みます (もっとも、ファイル構造を知らなくても 簡単に画像情報を得ることができるようですが)。
このように読むメソッドは、ReadInt32とか ReadDoubleとか 読みたいデータ型の種類ごとに用意されています。 詳しくは、BinaryReader メンバ(MS) をご覧ください。
BinaryWriterクラスは次のように用います。
    stripe.js
    --------------------------------------------------------------------------
        ...
    //パラメータの決定
    var Bpr : int = (int((width * 3 - 1) / 96) + 1) * 4; //1行あたりのバイト数
    var imagesize : int = Bpr * height;                  //イメージサイズ
    var filesize : int = 0x3E + imagesize;               //ファイルサイズ
    
    //作成するファイルをオープン
    var ofs : FileStream;
    try {
        ofs = new FileStream(args[1], FileMode.Create);
    }
    catch(e) {       //ファイルがオープンできない
        System.Console.Error.WriteLine("error : can't open file.");
        Environment.Exit(1);
    }
    
    var bw : BinaryWriter = new BinaryWriter(ofs);
    
    //ヘッダ部を書く
    bw.Write(short(0x4D42));     //shortで書き込む
    bw.Write(filesize);          //intで書き込む
        ...
    ofs.Close();
 
これは縞模様のBMPファイルを作るプログラムです (肝心の縞模様の部分は上では省略していますが)。 次のように、ファイル名と幅・高さを指定してBMPファイルを作ります。
    >stripe b.bmp 235 317
 
試してみてください。
上にあるように、BinaryWriterWriteメソッドで書き込みます。
Writeメソッドはオーバーロードされていて、 引数がshortならshortで、intならintで書き込みます。
ランダムアクセス
バイナリファイルを開いて、一部変えるということをしたいときもあるでしょう。 この場合、FileAccessをReadWriteにすればよいです。
次はGIFファイルの色を変えるプログラムです。
    colorchange.js
    -----------------------------------------------------------------------
        ...
    //色を変換
    var old_color : byte[] = new byte[3];
    var new_color : byte[] = new byte[3];
        ...
    //入出力ストリームを開く
    var fs : FileStream;
    try {
        fs = new FileStream(args[1], FileMode.Open, FileAccess.ReadWrite);
    }
        ...
    var p_size : int;
    var b : byte;
    var color : byte[] = new byte[3];
    try {
        //10バイト分進む
        fs.Seek(10, SeekOrigin.Current);
        //パレットのサイズを得る
        b = byte(fs.ReadByte());
        p_size = 2 << (b & 7);
        //2バイト分進む
        fs.Seek(2, SeekOrigin.Current);
        //パレットを読んで
        //もしold_colorだったらnew_colorにする
        for(i = 0; i < p_size; i++) {
            fs.Read(color, 0, 3);
            if(color[0] == old_color[0]
                    && color[1] == old_color[1] && color[2] == old_color[2]) {
                fs.Seek(-3, SeekOrigin.Current);    //今読んだ分だけ戻って
                fs.Write(new_color, 0, 3);          //新しい色を書く
            }
        }
    }
        ...
    fs.Close();
 
これは次のように使います。
    >colorchange b.gif ffffff 0000ff
 
こうすると画像の白の部分が青になるはずです。 ならなかったら自分で直してください。
パレットの部分で、
    fs.Read(color, 0, 3);
 
としてcolorという配列に3バイト分読んでいます。
もしこれがold_colorと一致すれば、
    fs.Seek(-3, SeekOrigin.Current);
 
として今の位置から3バイト戻り、そこで、
    fs.Write(new_color, 0, 3);
 
としてnew_colorという配列を3バイト分書き込みます。
Seekメソッドの2つ目の引数は、
    SeekOrigin.Begin SeekOrigin.Current SeekOrigin.End
 
のいずれかを取ることができます。
入出力(1) exit