MikoScript 言語仕様

 バッファ

 バッファとは、任意のバイナリーデータや文字列等の読み書き(入出力)が
できるメモリーブロックです。このバッファは、構造体のバイナリーデータを
扱う時や、正規表現で高度な文字列検索を行なう時などに、活用します。

 バッファの入出力は、ファイルの場合と同様の形態で行ないます。しかし、
バッファは、ファイルよりも、動作が高速で手軽に扱えます。また、バッファ
では、ファイルにはできない高度な操作も行なえます。その反面、バッファの
データは、ファイルのように外部記憶装置に保存されるわけではありません。

 バッファを使用するには、まず、Bufferクラスのインスタンスを生成します。
以降、そのインスタンスに対して、Bufferクラスのメンバー関数をコールする
ことによって操作します。以下は、その関数の一覧です。

・Bufferインスタンスの生成
  ::Buffer
・バッファの基本操作
  .Write        バッファへの書き込み(出力)
  .Read         バッファからの読み込み(入力)
  .Seek         現入出力位置の移動
  .Size         書き込み済みサイズの取得/変更
  .BuffSize     バッファサイズの取得/変更
  .Fill         特定範囲内の全バイト値の設定
  .IsEnd        入力終端到達の確認
  .ClearEnd     入力終端到達フラグのクリア
・バッファ内のメモリー領域操作
  .Keep         メモリー領域の確保
  .Move         メモリー領域の移動
  .Replace      メモリー領域の置換
・バッファ内の文字列検索
  .Find         正規表現パターンの検索開始
  .FindNext     正規表現パターンの検索再行
  .FoundText    検索一致部の文字列の取得
  .FoundPos     検索一致部の開始位置の取得
  .FoundSize    検索一致部のサイズの取得
  .FoundID      検索一致部の識別値の取得
  .ReferCount   正規表現パターンの参照指定数
  .SkippedText  検索不一致部の文字列の取得
  .SkippedPos   検索不一致部の開始位置の取得
  .SkippedSize  検索不一致部のサイズの取得

●Bufferインスタンスの生成/破棄
 バッファを使用するには、Bufferクラスのインスタンスを生成する必要が
あります。Bufferクラスは、本言語システムに予め組み込まれていて、その
実体は、グローバルスコープにあります。この Bufferクラスのインスタンス
を生成する一般的な書式は、次のようになります。
  ::Buffer( <サイズ>, <アドレス> )
 <サイズ> は、生成するバッファの初期サイズ(バイト数)を指定します。
<サイズ> の指定がない場合は、デフォールトの 1024 バイトになります。
バッファのサイズは、必要に応じて、自動的に拡張されますが、あらかじめ
必要なバッファサイズが分かっている場合には、<サイズ> を指定しておいた
ほうが、不用なメモリーの割り当てや、サイズの自動拡張に伴う無駄な処理を
回避できます。なお、バッファの最大サイズは、128 メガバイトです。これを
超える <サイズ> は指定できません。また、これが自動拡張の限界になります。

 <アドレス> の指定は、通常、必要ありません。<アドレス> を指定すると、
バッファの生成直後に、そのアドレスから、そのバッファのサイズ分のバイト
を読み込んで、そのバッファの初期データとして設定します。その際、もし、
メモリーのアクセス違反が発生すると、バッファの生成は失敗します。但し、
これで、スクリプトの実行がアボートされるわけではありません。

 バッファが生成された直後、バッファサイズは、上述の初期サイズになります。
一方、書き込み済み範囲のサイズは、0になります。

 バッファ内には、現入出力位置があります。これは、バッファの先頭からの
オフセット値(バイト数)で示され、その位置から、各入出力が始まります。
バッファが生成された直後、この現入出力位置は、バッファの先頭にあります。
つまり、その値は、0になっています。

 生成した Bufferクラスのインスタンスは、通常は、変数に代入しますが、
関数の引数や返値にすることもできます。単に生成しただけだと、自動的に
破棄されます。変数に代入する場合は、例えば、次のようになります。
    buf = ::Buffer();
これで、buf という変数に、Bufferクラスのインスタンスが代入されます。
以降は、この buf に対して、各種の操作を行ないます。例えば、
    buf.Write( -123, "ABC" );
    buf.Seek( 0 );
    buf.Read( A'LONG, B'LINE );
    print A,B;
のようになります。バッファの各操作関数に関しては、後で説明しています。
ちなみに、この例のスクリプトを実行すると、
    -123, ABC
がプリントされます。

 バッファのインスタンスの生成時に、メモリーが足りないか、または、前述
のメモリーアクセス違反が発生すると、その生成は失敗になります。その場合、
例外が発生します。この例外は、エラーワープで捕捉できます。この場合の
エラーワープ先のラベル名は、ファイルエラーの場合と共通で、
  OnFileError
になります。勿論、全エラー共通の OnError というラベルでも、この例外を
捕捉できます。一方、エラーワープ先が設定されていなければ、null が
返されます。これで、エラーが発生したかどうかを判断できます。例えば、
    buf = ::Buffer( 256, 0 );
    if( buf == null )
        'Error!( ::ErrorMessage( $err_code, $err_info ), "\n" );
を実行すると、0 番地に対するアクセス違反が発生するので、このバッファの
インスタンスの生成に失敗します。そのため、buf 変数には、null が代入され
ます。一方、次のように、エラーワープが設定されていると、制御は、そこへ
移行します。
    buf = ::Buffer( 256, 0 );
    print "ここには来ない!";
    return;

  OnFileError:
    print ::ErrorMessage( $err_code, $err_info );
ちなみに、このどちらの例を実行しても、
    指定アドレス(0x00000000)のメモリーにアクセスできません
とプリントされます。

 Bufferクラスのインスタンスの破棄は、一般のインスタンスの破棄と同様
です。つまり、delete 文で、例えば、
    delete buf;
のように、明示的に行なうことができます。また、それが属するシステム
スコープが消滅する時に、自動的に(暗黙的に)破棄されます。なお、
一般的には、「インスタンスの破棄とデストラクタ」で説明しています。

 Bufferクラスのデストラクタは、そのバッファのインスタンスで使われて
いた全メモリーを解放します。この機能は、本言語システムに予め組み込ま
れています。

 Bufferクラスへのメンバー関数の追加と、Bufferクラスの派生クラスの
設定に関しては、本章の最後で説明します。

●バッファの基本操作関数
 ここでは、Bufferクラスのメンバー関数のうち、入出力等、基本的な操作を
行なう関数について、説明します。
 以下で、Buffer という表記は、Bufferクラスのインスタンスを示します。

 Write 関数 -- バッファへの書き込み(出力)
書式: Buffer.Write( <項目>, ... )
機能: 引数で指定された各 <項目> のデータを、その入出力形式に応じて、
    バッファへ書き込みます。
説明: 本関数の引数には、バッファへ出力する <項目> を指定します。
    この引数は、任意の個数だけ指定できます。
    各引数は、指定された順に出力されます。
    各引数には、以下の種類の <項目> が指定できます。
     ・数値や文字列の定数/変数
     ・構造体、純粋配列、ビットフィールド
     ・ファイル/バッファのインスタンス
    各 <項目> は、それに「入出力形式」が設定されていれば、それに応じて、
    出力されます。さもなければ、デフォ−ルトの形式で出力されます。
    バッファへの書き込みは、現入出力位置から始まります。
    現入出力位置は、バッファへ書き込んだバイト数分だけ進みます。
    書き込みがバッファ終端を超過する場合、バッファサイズは自動的に
    拡張されます。
返値: 書き込み成功時、書き込んだバイト数( >= 0 )
    書き込み失敗時(エラー時)、-1
注意: 本関数の実行時にエラーが発生すると、OnFileError で捕捉できる
    例外が発生します。
用例: 

 Read 関数 -- バッファからの読み込み(入力)
書式: Buffer.Read( <箱>, ... )
機能: 引数で指定された各 <箱> の入出力形式に応じて、バッファから
    データを読み込んで、その <箱> に代入します。
説明: 本関数の引数には、バッファから入力したデータを設定する <箱> を
    指定します。この引数は、任意の個数だけ指定できます。
    各引数へは、指定された順に入力されます。
    各引数には、以下の種類の <箱> が指定できます。
     ・数値や文字列の変数
     ・構造体、純粋配列、ビットフィールド
     ・ファイル/バッファのインスタンス
    各 <箱> には、入出力形式が設定されている必要があります。
    各 <箱> への入力は、この入出力形式に応じて行なわれます。
    <箱> に入出力形式が無い場合に関しては、以下で説明しています。
      ・純粋配列の入出力
      ・入出力形式なしでの入出力
    バッファからの読み込みは、現入出力位置から始まります。
    現入出力位置は、バッファから読み込んだバイト数分だけ進みます。
    読み込みができるのは、書き込み済みの範囲内のみです。
返値: 読み込み成功時、読み込んだバイト数( >= 0 )
    読み込み失敗時(エラー時)、-1
注意: 書き込み済み範囲の終端を越えて読み込もうとした場合、入力対象の 
    <箱> の種類によって、その対応が異なります。これも、「入出力形式」
    の章で説明しています。もし、エラーになる場合には、例外が発生
    します。この例外は、エラーワープで捕捉できます。この
    場合のエラーワープ先のラベル名は、ファイルの場合と共通で、
      OnFileEnd
    になります。勿論、全エラー共通の OnError というラベルでも、この
    例外を捕捉できます。一方、エラーワープ先が設定されていなければ、
    -1 が返されるだけです。
補説: 書き込み済み範囲の終端を越えて読み込もうとした時には、また、
    入力終端到達フラグがセットされます。これは、IsEnd() 
    関数と ClearEnd() 関数に関連しています。
注意: 本関数の実行時に、上記の入力終端到達以外のエラーが発生すると、
    OnFileError で捕捉できる例外が発生します。
用例: 

 Seek 関数 -- 現入出力位置の移動
書式: Buffer.Seek( <位置>, <基点> )
機能: バッファ内の現入出力位置を、<位置> と <基点> で指定された位置に
    移動します。また、現入出力位置を取得します。
説明: <位置> は、<基点> からの相対値(バイト数)を指定します。
    <基点> は、次のどれかの文字列で指定します。
      "top" or "TOP" ・・・ 先頭から(デフォールト)
      "cur" or "CUR" ・・・ 現在位置から
      "end" or "END" ・・・ 終端から
    <基点> が省略された時は、先頭からになります。
    <位置> が省略された時は、現入出力位置は、移動しません。
    現入出力位置を取得するだけの場合、<位置> と <基点> を省略、
    つまり、引数なしで、本関数をコールします。
返値: 移動した場合、その移動後の現入出力位置
    移動しなかった場合、現在の現入出力位置
    エラーの場合、-1
注意: <位置> と <基点> で指定された現入出力位置が、書き込み済み範囲の
    終端を越える場合、書き込み済み範囲がそこまで拡張されます。また、
    それが、バッファ終端を越える場合には、バッファサイズがそこまで
    拡張されます。拡張された部分の内容は、不定です。
補説: 本関数の実行で、入力終端到達フラグはクリアされます。
用例: 
        bf = ::Buffer();
        bf.Seek( 100 );       // 先頭から 100 バイトの位置へ移動
        bf.Seek( 80,"cur" );  // 現位置から 80 バイト先の位置へ移動
        bf.Seek( 0, "end" );  // 終端位置へ移動
        bf.Seek( -4,"end" );  // 終端から 4 バイト前の位置へ移動
        bf.Seek();            // 現在位置を取得
Size 関数 -- 書き込み済みサイズの取得/変更 書式: Buffer.Size( <サイズ> ) 説明: <サイズ> の指定がない場合、現在の書き込み済みサイズ(バイト数)     を返します。     <サイズ> の指定がある場合、書き込み済みサイズを、その指定値     のバイト数に、変更します。この変更では、現在のサイズよりも、     大きくすることも、小さくすることもできます。     現在のサイズよりも大きくする場合:       元の書き込み済み範囲の内容はそのままで、その末尾に増加分が       追加されます。その増加部分の内容は不定です。現入出力位置は       変わりません。     現在のサイズよりも小さくする場合:       元の書き込み済み範囲内の末尾部分が、書き込み済みの範囲から       外されます。その際、バッファサイズ自身は、変わりません。       現入出力位置が、書き込み済みの範囲外になってしまう時、       現入出力位置は、その範囲の終端位置に更新されます。 返値: サイズ変更した場合、その変更後の書き込み済みサイズ     サイズ変更しなかった場合、現在の書き込み済みサイズ     エラーの場合、-1 BuffSize 関数 -- バッファサイズの取得/変更 書式: Buffer.BuffSize( <サイズ> ) 説明: <サイズ> の指定がない場合、現在のバッファサイズ(バイト数)を     返します。     <サイズ> の指定がある場合、バッファサイズを、その指定値の     バイト数に、変更します。この変更では、現在のサイズよりも、     大きくすることも、小さくすることもできます。     現在のサイズよりも大きくする場合:       現在の書き込み済み範囲とその内容はそのままで、バッファの       サイズだけが増えます。現入出力位置も、変わりません。     現在のサイズよりも小さくする場合:       バッファの末尾部がカットされます。そのカット部分に、元の       書き込み済み範囲が重なる場合、その重なった部分の内容は、       消失し、書き込み済み範囲は、新しいバッファサイズの全域に       なります。また、現入出力位置が、書き込み済みの範囲外に       なってしまう時、現入出力位置は、その範囲の終端位置に更新       されます。 返値: サイズ変更した場合、その変更後のバッファサイズ     サイズ変更しなかった場合、現在のバッファサイズ     エラーの場合、-1 Fill 関数 -- 特定範囲内の全バイト値の設定 書式: Buffer.Fill( <サイズ>, <値> ) 機能: 現入出力位置から <サイズ> で指定されたバイト数分の範囲内にある     全バイトを、<値> で指定されたバイト値に設定します。 説明: <サイズ> は、正の数値でなければいけません。さもなければ、     エラーになります。     <値> は、その整数値の下位8ビット(1バイト)のみが有効です。     <値> が省略された場合、0 値として扱います。     本関数の実行で、現入出力位置は、<サイズ> 分だけ進みます。     <サイズ> で指定された範囲が、バッファ終端を越える場合、その分     だけ、バッファが拡張されます。 返値: 正常終了時、<サイズ> の正の整数値     エラー時、0または負の整数値 IsEnd 関数 -- 入力終端到達の確認 書式: Buffer.IsEnd() 説明: バッファからの読み込みで、書き込み済み範囲の終端を越えようと     した場合、入力終端到達フラグがセットされます。     本関数は、このフラグがセットされていれば、真(1) を返します。     さもなければ、偽(0) を返します。 ClearEnd 関数 -- 入力終端到達フラグのクリア 書式: Buffer.ClearEnd() 説明: バッファからの読み込みで、書き込み済み範囲の終端を越えようと     した場合、入力終端到達フラグがセットされます。     本関数は、このフラグをクリア(リセット)します。 ●バッファ内のメモリー領域操作関数  ここでは、Bufferクラスのメンバー関数のうち、メモリー領域操作に関連 した関数について、説明します。  以下で、Buffer という表記は、Bufferクラスのインスタンスを示します。 Keep 関数 -- メモリー領域の確保 書式: Buffer.Keep( <サイズ>, <値> ) 機能: 現入出力位置から、<サイズ> で指定されたバイト数分のメモリー領域     を確保します。 説明: <サイズ> は、正の数値でなければいけません。さもなければ、エラー     になります。     <値> が指定されていれば、確保したメモリー領域内の全バイトを、     その整数値(の最下位のバイト値)に設定します。     <値> が省略された場合、確保したメモリー領域内の値は不定です。     現バッファのサイズでは、指定されたサイズのメモリー領域を確保     できない場合、バッファサイズが拡張されます。     確保したメモリー領域は、書き込み済み範囲になります。     現入出力位置は、本関数の実行では変わりません。 返値: 正常終了時、確保したメモリー領域のサイズ(バイト数)     エラー時、0または負の整数値 Move 関数 -- メモリー領域の移動 書式: Buffer.Move( <移動先>, <移動元>, <サイズ> ) 機能: <移動先> の位置へ、<移動元> の位置から <サイズ> バイト分の     ブロックを移動(各バイト値をコピー)します。 説明: <移動先> と <移動元> は、バッファの先頭からのオフセット値を     指定します。どちらも単位は、バイト数です。     <サイズ> は、移動元のブロックのバイト数を指定します。     どの引数も、省略不可で、0以上の数値を指定する必要があります。     移動元ブロックの範囲で、書き込み済み範囲外になる部分があれば、     その部分は移動対象外になります。つまり、移動されるのは、あく     まで、書き込み済み範囲内の部分に限られます。     移動先の範囲が、現バッファサイズを超える場合、バッファサイズが     その分だけ、拡張されます。     移動元の範囲と、移動先の範囲が重なっても、特に支障ありません。     現入出力位置は、本移動では変わりません。 返値: 成功時、実際に移動したメモリー領域のサイズ(バイト数)     エラー時、0 以下の整数値 Replace 関数 -- メモリー領域の置換 書式: Buffer.Replace( <範囲>, <項目>, ... ) 機能: 現入出力位置から <範囲> で指定されたメモリー領域の部分を削除     して、その代わりに、<項目>, ... のデータを挿入します。 説明: <範囲> は、現入出力位置からの相対バイト数を指定します。     この値が正の時、現入出力位置から順方向の範囲になります。     この値が負の時、現入出力位置から逆方向の範囲になります。     いずれの場合でも、書き込み済み範囲外の部分は、除外されます。     <範囲> の値が 0 か、または、<範囲> が省略された時、削除される     範囲はありません。この場合、挿入だけが行なわれます。     <項目>, ... は、Write 関数の場合と同様です。しかし、本関数の     場合は、そのデータが上書きされるのではなく、挿入されます。     現入出力位置は、この挿入の最後の位置まで進みます。     この挿入に際して、現バッファサイズが足りない場合は、自動的に     拡張されます。     <項目>, ... が指定されなかった場合、削除だけが行なわれます。 返値: 置換成功時、挿入したデータのバイト数( >= 0 )     置換失敗時(エラー時)、-1 注意: <項目>, ... のデータの挿入時にエラーが発生すると、OnFileError     で捕捉できる例外が発生します。 ●バッファ内の文字列検索関数  ここでは、Bufferクラスのメンバー関数のうち、文字列検索に関連した 関数について、説明します。  以下で、Buffer という表記は、Bufferクラスのインスタンスを示します。 Find 関数 -- 正規表現パターンの検索開始 書式: Buffer.Find( <正規表現>, <範囲> ) 機能: <正規表現> に一致する文字列部を、<範囲> で指定された検索範囲内で、     検索します。 説明: <正規表現> には、検索する正規表現のパターンを規定します。     正規表現に関しては、「正規表現」の章で詳しく説明しています。     <正規表現> を省略すると、前回と同じ正規表現が使用されます。     この効用については「補説」で述べています。なお、この省略時に、     前回の正規表現が無ければ、エラーになります。     <範囲> を省略すると、現入出力位置から書き込み済み範囲の終端     までが検索範囲になります。     <範囲> を指定することによって、検索範囲を限定できます。     <範囲> には、現入出力位置からの相対バイト数を指定します。     <範囲> が正の時、現入出力位置から順方向の範囲になります。     <範囲> が負の時、現入出力位置から逆方向の範囲になります。     いずれの場合でも、書き込み済み範囲外の部分は、除外されます。     <範囲> が0の時、空の検索範囲になります。     本検索は、検索範囲内で、<正規表現> に一致する文字列部を、最初に     見つけたところで終了します。その時、現入出力位置は、その一致     文字列部の先頭位置に移動します。     本検索で、検索範囲の最後まで <正規表現> が見つからなかった時も、     検索は終了しますが、その時には、現入出力位置は、現状のままに     なります。 返値: 検索成功時、<正規表現> に一致した文字列部のバイト数( ≧ 0 )     検索失敗時、次のどれかの負値になります。       -1: 一致部なし or 引数不正等       -2: 検索中断(ユーザー要求による)       -3: 検索経路が多過ぎる       -4: 致命的エラー 補説: 本関数の引数に、<正規表現> が指定された時には、そのパターンが     解析されて、その結果が保持されます。これは、今回の検索だけでなく、     次回以降の検索時にも有効です。そのため、同じ正規表現を何回も     検索する場合には、初回だけ <正規表現> を指定して、2回目以降は     <正規表現> を省略するようにします。そうすれば、2回目以降では、     正規表現を解析する時間が省かれるので、処理が速くなります。     なお、検索成功時、現入出力位置が、その一致文字列部の先頭位置に     移動するので、2回目以降の検索では、その開始前に、現入出力位置     を、その一致文字列部の末尾に、移動させておく必要があります。     ちなみに、FineNext 関数では、それが考慮されています。 補説: 正規表現パターンの解析結果は、各 Buffer のインスタンスごとに     個別に保持されます。この解析結果は、本関数が、<正規表現> を     伴ってコールされた時に、更新されます。また、この解析結果は、     そのインスタンスが破棄される時に、いっしょに破棄されます。 注意: 逆方向範囲の検索( <範囲> に負値を指定した場合 )では、     最右一致優先が、デフォールトの検索モードになります。     一方、順方向範囲の検索では、最左一致優先がデフォールトです。 FindNext 関数 -- 正規表現パターンの検索再行 書式: Buffer.FindNext() 機能: 前回と同じ正規表現パターンで次に一致する文字列部を検索します。 説明: 本検索の検索範囲は、次のようになります。      ・前回が順範囲の場合、前回の一致部の末尾の直後から前回の        検索範囲の最後まで。      ・前回が逆範囲の場合、前回の検索範囲の先頭から前回一致部の        先頭の直前まで。     本検索は、検索範囲内で、前回の正規表現に一致する文字列部を、     最初に見つけたところで終了します。その時、現入出力位置は、     その一致文字列部の先頭位置に移動します。     本検索で、検索範囲の最後まで、その正規表現が見つからなかった     時も、検索は終了しますが、その時には、現入出力位置は、現状の     ままになります。     本検索は、前回の検索が、失敗か、または、行なわれていない時には、     無条件に、失敗します。 返値: 検索成功時、一致した文字列部のバイト数( ≧ 0 )     検索失敗時、次のどれかの負値になります。       -1: 一致部なし       -2: 検索中断       -3: 検索パターンが複雑過ぎる       -4: 致命的エラー 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、本結果は不定です。 FoundText 関数 -- 検索一致部の文字列の取得 書式: Buffer.FoundText( i ) 機能: 前回の検索で正規表現と一致した部分の文字列を返します。 説明: 引数 i が 0 か、省略の時、正規表現全体と一致した部分の文字列を     返します。但し、正規表現内に「最重要参照グループ」があれば、     それに一致する部分の文字列を返します。     引数 i が 1 以上の時、第 i 番目の「参照グループ」と一致する     部分の文字列だけを返します。     引数 i で指定された一致部分がなければ、null を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、本結果は不定です。 FoundPos 関数 -- 検索一致部の開始位置の取得 書式: Buffer.FoundPos( i ) 機能: 前回の検索で正規表現と一致した部分の開始位置を返します。 説明: この開始位置は、バッファの先頭からのオフセット値(バイト数)を     示します。     引数 i が 0 か、省略の時、正規表現全体と一致した部分の文字列の     開始位置を返します。但し、正規表現内に「最重要参照グループ」が     あれば、それに一致する部分の文字列の開始位置を返します。     引数 i が 1 以上の時、第 i 番目の「参照グループ」と一致する     部分の文字列の開始位置を返します。     引数 i で指定された一致部分がなければ、-1 を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、本結果は不定です。 FoundSize 関数 -- 検索一致部のサイズの取得 書式: Buffer.FoundSize( i ) 機能: 前回の検索で正規表現と一致した部分のサイズ(バイト数)を返します。 説明: 引数 i が 0 か、省略の時、正規表現全体と一致した部分の文字列の     サイズを返します。但し、正規表現内に「最重要参照グループ」が     あれば、それに一致する部分の文字列のサイズを返します。     引数 i が 1 以上の時、第 i 番目の「参照グループ」と一致する     部分の文字列のサイズを返します。     引数 i で指定された一致部分がなければ、-1 を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、本結果は不定です。 FoundID 関数 -- 検索一致部の識別値の取得 書式: Buffer.FoundID() 機能: 前回の検索で正規表現と一致したパターンの識別値を返します。 説明: この識別値というのは、「一致パターン識別値」のことです。     この一致パターン識別値が指定されていないパターンに一致していた     時には、0 を返します。     前回の検索がないか、失敗していた時には、-1 を返します。 ReferCount 関数 -- 正規表現パターンの参照指定数 書式: Buffer.ReferCount() 説明: 本バッファの検索に使われた正規表現内の「参照グループ」の数を     返します。本バッファで検索が未実行の場合は、-1 を返します。 SkippedText 関数 -- 検索不一致部の文字列の取得 書式: Buffer.SkippedText() 説明: 前回の検索で正規表現と一致しなかった部分(読み飛ばした部分)の     文字列を返します。     本バッファで検索が未実行の場合は、null を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、結果は不定です。 SkippedPos 関数 -- 検索不一致部の開始位置の取得 書式: Buffer.SkippedPos() 説明: 前回の検索で正規表現と一致しなかった部分(読み飛ばした部分)の     開始位置(バッファ先頭からのオフセット値)を返します。     本バッファで検索が未実行の場合は、-1 を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、結果は不定です。 SkippedSize 関数 -- 検索不一致部のサイズの取得 書式: Buffer.SkippedSize() 説明: 前回の検索で正規表現と一致しなかった部分(読み飛ばした部分)の     サイズ(バイト数)を返します。     本バッファで検索が未実行の場合は、-1 を返します。 注意: 前回の検索から本関数コールまでの間に、バッファの内容が変わって     いれば、結果は不定です。 ●バッファ操作例  まず、比較的簡単な例として、テキストファイルをバッファに読み込んで、その中に ある特定の文字列を検索して、その情報をプリントするプログラムを示します。ここで、 検索する文字列は、コマンド行のパラメータで指定する方法など、いろいろありますが、 以下のように、文字列入力用のダイアログボックスから入力することにします。
    word = ::Input( "検索文字列:" );
    if( ! word )    // 入力キャンセル or 空文字列入力?
        return;
テキストファイルの指定の仕方にも、いろいろありますが、ここでは、簡単のため、
とりあえず、"TextFile.txt" という名前にしておきます。このファイルをオープン
するのは、以下のようになります。
    FileName = $"TextFile.txt";
    file = ::File.Open( FileName, "in" );
    if( file == null )
    {
        ::Alert( FileName + "\nファイルが見つかりません!" );
        return;
    }
オープンしたテキストファイルを、以下のように、Text というバッファに読み込みます。
    Text = ::Buffer();
    cn = file.Read( Text );
    file.Close();
    if( cn < 0 )
    {
        ::Alert( FileName + "\nファイル読み込みエラー発生!" );
        return;
    }
バッファに読み込まれたテキストファイルの内容は、以下のように検索して、指定された
文字列が何箇所で見つかるのかをプリントします。
    Text.Seek(0);
    pattern = "#i#e" + word + "#E";   // 検索パターン
    N = 0;
    for( cn = Text.Find( pattern ) ; cn > 0 ; cn = Text.FindNext() )
        N++;
    print ##「${word}」は、${N} 箇所で見つかりました。##;
ここでの検索パターンは、正規表現になりますが、これについて少し説明しておきます。
#i は、英字大小の区別をなくします。 #e 〜 #E は、この間の文字列( ここでは、
word という変数に入っている文字列)内に、正規表現のメタ文字があってもそれを無効
にします。

 上記の処理では、テキストファイルの内容をすべてバッファに読み込んでから検索しま
したが、そのサイズが非常に大きいと、1度に読み込むのは無理があります。以下では、
テキストファイルの内容を何回かに分けてバッファに読み込んで検索する例を示します。
    word = ::Input( "検索文字列:" );
    if( ! word )    // 入力キャンセル or 空文字列入力?
        return;

    FileName = $"TextFile.txt";
    file = ::File.Open( FileName, "in" );
    if( file == null )
    {
        ::Alert( FileName + "\nファイルが見つかりません!" );
        return;
    }
    // ここまでは前と同じ、ここからが違う!

    #set  Bn  4*1024                // バッファサイズ
    Text = ::Buffer( #Bn );
    Text.Find( $"^.*@(\n)", 0 );    // 任意の1行に一致する正規表現の事前設定
    'match( "#i#e" + word );        // 検索パターンを事前設定
    Mn = 0;     // 一致行数
    Ln = 0;     // 現在の行番号(現在までの行数)
    Rn = 0;     // 前回の残部のサイズ
    Le = "";    // 各行の改行コード

    while(1)
    {
        // バッファにテキストファイルの内容を読み出す(前回の残部の後に追加)
        Text.Seek( Rn );
        if( file.Read( Text'BYTE( #Bn - Rn )) < 0 )
        {
            ::Alert( ##${ Ln +1 } 行目: ## +
                     ::ErrorMessage( $err_code, $err_info ) );
            return;
        }
        Tn = Text.Size();
        if( Tn <= Rn )  // 今回の読み込みなし?
            break;

        // 改行コード(CR/LF)の片割れ(LF)が先頭にある場合はそれを読み飛ばす
        Ri = 0;
        if( Rn == 0  &&  Le == "\r" )
        {
            Text.Seek( 0 );
            if( Text.Read( Ls'C(1) ) == 1  &&  Ls == "\n" )
                Ri = 1;
        }
        Text.Seek( Ri );
        Le = "";

        // バッファ内のテキストを先頭から順に検索
        while(( cn = Text.Find()) > 0 )
        {
            Ln++;
            Le = Text.FoundText(1);     // この行の改行コード( CR/LF, LF, or CR )
            if( Text.FoundText()'match >= 0 )   // この行は検索パターンを含むか?
            {
                Mn++;
                print Ln : ": " : Text.FoundText() :-;
            }
            Ri += cn;   // 現時点までの検索済範囲
            Text.Seek( Ri );
        }

        // バッファ末尾の未処理の残部を先頭に移動
        Rn = Tn - Ri;
        Text.Move( 0, Ri, Rn );
        Text.Size( Rn );
    }

    print "一致行数 = ": Mn, " 全行数 = ": Ln;
    file.Close();   // ← これがなくても、自動で行われる
    return;         // ← これがなくても、暗黙で行われる
 この例では、バッファのサイズは僅か 4 KB しか使いません。それでも任意のサイズの
テキストファイルを調べることができます。但し、1行の文字列の長さと、検索文字列の
長さは、このバッファサイズ以下でないといけません。この制約は、通常の用途では特に
問題ないと思いますが、もし不足なら、バッファサイズは任意に変更できます。
 この例ではまた、検索文字列が見つかった箇所の行番号とその行の内容をプリントする
ようになっています。各行の改行コードは、CR/LF, LF, CR のどれにも対応しています。
CR/LF では、バッファの終端で CR と LF が分断される可能性がありますが、その対応も
含まれています。もしこの対応がなければ、行番号の値がおかしくなってしまいます。



(例)構造体データのバッファへの書き込み


(例)構造体データのバッファからの読み出し


(例)バッファからファイルへの書き込み



●メンバー関数の追加
 Bufferクラスが、本言語システムに予め組み込まれているということは、
既に述べましたが、その Bufferクラスに、任意のメンバー関数を直接追加
することもできます。それには、次のようにします。

 例えば、バッファ内に書き込まれている全バイトを 16進値でダンプする
メンバー関数は、次のように定義します。
    function ::Buffer.HexDump()
    {
        Bytes'UBYTE(16);
        .Seek(0);
        for( offset = 0 ;; offset += 16 )
        {
            bn = .Read( Bytes );
            if( bn <= 0 )
                break;
            line = offset'x(6) + "  ";
            for( i = 0 ; i < bn ; i++ )
                line += Bytes(i)'x(2) + " ";
            if( bn < 16 )
                line += "   "'rep( 16 - bn );
            line += " " + Bytes'gets( bn )
                'mend( , "_.........・..・..................." );
            print line;
        }
    }
このメンバー関数は、組み込みのメンバー関数と同様にコールできます。
例えば、次のようになります。
    buf = ::Buffer();
    buf.Write( 0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C );
    buf.Write( "ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n" );
    buf.Write( "あいうえお\r\n" );
    buf.HexDump();
ちなみに、これを実行すると、次の通りプリントされます。
 000000  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  _.........・..・..
 000010  41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50  ABCDEFGHIJKLMNOP
 000020  51 52 53 54 55 56 57 58 59 5A 0D 0A 82 A0 82 A2  QRSTUVWXYZ・・あい
 000030  82 A4 82 A6 82 A8 0D 0A                          うえお・・
●派生クラスの設定
 本言語システムに予め組み込まれている Bufferクラスの派生クラスを、
設定することもできます。それには、例えば、次のようにします。
    class  MyBuffer : ::Buffer
    {
        function Construct( name )
        {
            .[ ::Buffer.Construct ]();    // ※1
            .Name = name;
            print "構築: " : .Name;
        }
        function Destruct()
        {
            .[ ::Buffer.Destruct ]();    // ※2
            print "破棄: " : .Name;
        }
    };
この派生クラスのコンストラクタでは、基底クラス、つまり、Bufferクラスの
コンストラクタをコールする必要があります。( 上記 ※1 のところ )
また同様に、この派生クラスのデストラクタでは、基底クラスのデストラクタ
をコールする必要があります。( 上記 ※2 のところ )

 この派生クラスは、単に説明用のものですが、次のように、このクラスの
インスタンスを生成して、使用することができます。
    bf = MyBuffer( "Test-Buffer" );
    bf.Write( 123, "ABC" );
    bf.Seek( 0 );
    bf.Read( X'LONG, Y'C(3) );
    print bf.Name, X, Y;
    delete bf;
ちなみに、これを実行すると、次の通りプリントされます。
  構築: Test-Buffer
  Test-Buffer, 123, ABC
  破棄: Test-Buffer