MikoScript 言語仕様
入出力形式
ファイルやバッファの入出力では、様々な形態のデータが対象になります。
これらの個々のケースごとに多種類の入出力関数を設けるのは、あまり得策
ではありません。本言語では、入出力関数を、.Read と .Write に統一し、
データ側に、入出力形式を設定するようになっています。
以下に、本言語で設定可能な入出力形式の一覧を示します。
文字列データ(固定長)形式:
'C(n) n 桁の一般文字列
'I(n) n 桁の整数(10進)文字列
'X(n) n 桁の整数(16進)文字列
'F(n) n 桁の実数(f型表記)文字列
文字列データ(不定長)形式:
'LINE or 'LN 改行区切り文字列
'CSV コンマ区切り文字列
'SSV 空白区切り文字列
'KC(n) n 字の一般文字列
バイナリーデータ(固定長)形式:
'BYTE 8 ビット整数(符号有り)
'UBYTE 8 ビット整数(符号無し)
'SHORT 16 ビット整数(符号有り)
'USHORT 16 ビット整数(符号無し)
'LONG 32 ビット整数(符号有り)
'ULONG 32 ビット整数(符号無し)
'FLOAT 32 ビット浮動小数点数
'DOUBLE 64 ビット浮動小数点数
ビットフィールド(固定長)形式:
'BIT_FIELD(n) n バイトのビットフィールド
'BIT(n) or 'UBIT(n) n ビット整数値(符号無し)
'SBIT(n) n ビット整数値(符号有り)
構造体(レコード)形式
'STRUCT
これらの入出力形式は、リレー型関数で、入出力対象のデータ(箱や値)に
設定します。それを、入出力関数(ファイル/バッファの .Read, .Write 関数)
に渡すと、それに応じて入出力動作が行なわれます。
このように、入出力形式とは、入出力対象のデータと、実際に入出力される
バイト列との変換仕様を規定するものです。
箱に入出力形式を設定しても、入出力以外の殆どの動作には影響しません。
例えば、ある箱に 'BYTE の入出力形式を設定しても、通常通り、その箱に
整数や実数、あるいは、文字列等を代入することができます。また、この箱を、
演算式に使っても、それが、BYTE の精度になるわけではありません。
ところで、本言語の入出力形式の仕様は、他のスクリプト言語には、あまり
見られないかもしれません。本仕様の一部は、COBOL を参考にしていますが、
大部分は、本言語独自のものです。
●文字列データ(固定長)形式
この種の入出力形式は、基本的に、固定長のバイト列内に納められた文字列
データを入出力する時に使います。ここでは、まず、この形式での出力と入力
の各種のパターンを説明してから、この形式を設定するリレー型関数について
説明します。
最初に、文字列または数値を、固定長のバイト列に変換して出力する各種の
場合を説明します。以下では、出力バイト列のサイズを、n バイトとします。
文字列 → 固定長バイト列
文字列を固定長バイト列として出力する場合、次のようになります。
その文字列の長さ(バイト数)が、n 未満の時、
その文字列自身と、その後に 0 値のバイト列を補充して出力します。
その文字列の長さ(バイト数)が、n 以上の時、
その先頭から n バイト分を出力します。なお、その末尾で、全角文字の
2バイトが分断される場合、その片割れを半角空白にして出力します。
整数値 → 固定長バイト列(10進表記)
整数値を10進文字列に変換して、固定長バイト列として出力する場合、
次のようになります。
この値を 'd 変換で10進表記にした文字列を元に、
この文字列の長さ(バイト数)が、n 未満の時、
その文字列自身と、その後に 0 値のバイト列を補充して出力します。
この文字列の長さ(バイト数)が、n 以上の時、
整数値が正なら、その末尾から n バイト分を出力します。
整数値が負なら、マイナス符号(-)とその文字列の末尾から n - 1 バイト分を
出力します。
整数値 → 固定長バイト列(16進表記)
整数値を16進文字列に変換して、固定長バイト列として出力する場合、
次のようになります。
この値を8桁の16進表記に変換した文字列を元に、
この文字列の長さ(バイト数)が、n 未満の時、
その文字列自身と、その後に 0 値のバイト列を補充して出力します。
この文字列の長さ(バイト数)が、n 以上の時、
その末尾から n バイト分を出力します。
実数値 → 固定長バイト列(f型表記)
実数値を文字列に変換して、固定長バイト列として出力する場合、
次のようになります。
この値をf型表記に変換した文字列を元に、
この文字列の長さ(バイト数)が、n 未満の時、
その文字列自身と、その後に 0 値のバイト列を補充して出力します。
この文字列の長さ(バイト数)が、n 以上の時、
まず、小数点以下の下位の方の桁から切り取ります。
それでも超過する場合は、整数部の上位の方の桁から切り取ります。
このような対処で、丁度 n バイトになるようにして、出力します。
次に、固定長のバイト列を入力して、文字列または数値に変換する各種の
場合を説明します。以下では、入力バイト列のサイズを、n バイトとします。
固定長バイト列 → 文字列
入力バイト列から、n バイト分を読み込みます。この先頭から順に走査して、
0 終端( 0 値のバイト)があれば、そこまで、なければ、その最後までを、
文字列として入力します。
入力バイト列から、n バイト分を読み込めない場合、つまり、入力終端まで
の残りが n バイト未満の場合、この入力値は、null になります。
固定長バイト列(10進表記)→ 整数値
固定長バイト列 → 文字列 のようにして入力した文字列に対して、
'int(10) 変換します。この整数値が、本入力値になります。
固定長バイト列(16進表記)→ 整数値
固定長バイト列 → 文字列 のようにして入力した文字列に対して、
'int(16) 変換します。この整数値が、本入力値になります。
固定長バイト列(実数表記)→ 実数値
固定長バイト列 → 文字列 のようにして入力した文字列に対して、
'float 変換します。この実数値が、本入力値になります。
最後に、文字列データ(固定長)の入出力形式を設定するリレー型について、
説明します。
T'C(n) n 桁の一般文字列
機能: 対象 T に「 n 桁の一般文字列 」を示す入出力形式を設定します。
説明: この入出力形式は、次の対象に設定可能です。
・文字列の定数または変数
・数値(整数、実数)の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
n の有効範囲は、0 〜 1000 です。
n が省略時、n = 1 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次のように、各データ型に応じて、固定長バイト列が出力されます。
・文字列 → 固定長バイト列
・整数値 → 固定長バイト列(10進表記)
・実数値 → 固定長バイト列(f型表記)
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、
・固定長バイト列 → 文字列
のようにして入力された文字列が、設定されます。
T'I(n) n 桁の整数(10進)文字列
機能: 対象 T に「 n 桁の整数(10進)文字列 」を示す入出力形式を
設定します。
説明: この入出力形式は、次の対象に設定可能です。
・数値(整数、実数)の定数または変数
・文字列の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
n の有効範囲は、0 〜 120 です。
n が省略時、n = 6 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次のように、各データ型に応じて、固定長バイト列が出力されます。
・整数値 → 固定長バイト列(10進表記)
・実数値 → 整数値 → 固定長バイト列(10進表記)
・文字列 → 固定長バイト列
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、
・固定長バイト列(10進表記)→ 整数値
のようにして入力された整数値が、設定されます。
T'X(n) n 桁の整数(16進)文字列
機能: 対象 T に「 n 桁の整数(16進)文字列 」を示す入出力形式を
設定します。
説明: この入出力形式は、次の対象に設定可能です。
・数値(整数、実数)の定数または変数
・文字列の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
n の有効範囲は、0 〜 120 です。
n が省略時、n = 6 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次のように、各データ型に応じて、固定長バイト列が出力されます。
・整数値 → 固定長バイト列(16進表記)
・実数値 → 整数値 → 固定長バイト列(16進表記)
・文字列 → 固定長バイト列
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、
・固定長バイト列(16進表記)→ 整数値
のようにして入力された整数値が、設定されます。
T'F(n) n 桁の実数(f型表記)文字列
機能: 対象 T に「 n 桁の実数(f型表記)文字列 」を示す
入出力形式を設定します。
説明: この入出力形式は、次の対象に設定可能です。
・数値(実数、整数)の定数または変数
・文字列の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
n の有効範囲は、0 〜 120 です。
n が省略時、n = 6 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次のように、各データ型に応じて、固定長バイト列が出力されます。
・実数値 → 固定長バイト列(f型表記)
・整数値 → 実数値 → 固定長バイト列(f型表記)
・文字列 → 固定長バイト列
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、
・固定長バイト列(実数表記)→ 実数値
のようにして入力された実数値が、設定されます。
●文字列データ(不定長)形式
この種の入出力形式は、基本的に、文字列データとして、不定長のバイト列を
入出力する時に使います。以下に、この入出力形式を設定するリレー型について、
説明します。
T'LINE or T'LN 改行区切り文字列
機能: 対象 T に「 改行区切り文字列 」を示す入出力形式を設定します。
説明: 'LN は、'LINE の簡略形です。どちらを使っても機能は同じです。
この入出力形式は、次の対象に設定可能です。
・文字列の定数または変数
・数値(整数、実数)の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が無い時、改行コードの文字列("\r\n")を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次の各データ型に対応した文字列と、その後に改行コード(CR+LF)を
付加したバイト列が、出力されます。
・文字列 → それ自身
・整数値 → 10進整数表記文字列
・実数値 → g型実数表記文字列
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、その
入力バイト列から読み込まれた1行分の文字列が、その変数に、設定
されます。この文字列には、その行末の改行コードが含まれます。
この1行分の文字列というのは、その入力バイト列の今回の入力開始
位置から、行終端(CR+LF, LF, CR のどれか)の直後までです。その
間に行終端がなければ、入力終端までになります。
用例: 次のスクリプトは、"File.txt" というファイルの内容を1行ずつ
読み出して、それに行番号を付けて、プリントします。
file = ::File.Open( "File.txt", "in" );
if( file == null )
'Error!( "Failed to open file!\n" );
line'LN;
n = 0;
while( file.Read( line ) >= 0 && line != null )
print "%3d: %s"'fmt( ++n, line ) : -;
file.Close();
T'CSV コンマ区切り文字列
機能: 対象 T に「 コンマ区切り文字列 」を示す入出力形式を設定します。
説明: この入出力形式は、次の対象に設定可能です。
・文字列の定数または変数
・数値(整数、実数)の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
その値を 'csv 変換 した文字列と、その後にコンマ(,)1字を付加した
バイト列が、出力されます。
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、
この変数には、入力バイト列から、次のようにして読み込まれた
CSVフィールドの値(文字列)が、設定されます。
・入力バイト列の開始位置を、CSVフィールドの始まりとします。
・このフィールドの最初の文字が
二重引用符の場合、「二重引用符フィールド」として扱います。
それ以外の場合、「通常フィールド」として扱います。
・二重引用符フィールドの場合:
・この囲い内は、通常文字として扱います。
・この囲い内に、改行があっても構いません。
・この囲い内の連続した2つの二重引用符は、1つの二重引用符の
通常文字として扱います。
・この囲いの左右端の二重引用符は、フィールド値には含みません。
・この囲いの後に、通常文字があると、それ以降は、通常フィールド
として扱います。
・通常フィールドの場合:
・コンマ、改行は、含めません。
・途中に二重引用符があっても通常文字として扱いにします。
・空のフィールドの値は、空文字列になります。
・区切りのコンマは、勿論、フィールド値には含まれません。
・入力バイト列の読み込みは、コンマ、改行、入力終端のどれかで、
終了します。
・入力バイト列の読み込みが、コンマで終了した場合、
次回の読み込みは、その直後の位置からになります。
・入力バイト列の読み込みが、改行で終了した場合、
次回の読み込みは、その直前の位置からになります。つまり、
このフィールドの読み込みとしては、これ以降には進みません。
次のレコード行に進むには、行の空読みが必要です。
・改行の直前または、入力終端から、バイト列を読み込み始めた場合、
このフィールドの値は、null になります。
・入力バイト列内に 0 値のバイトがあっても、特別な扱いはしません。
しかし、これは文字列内で0終端になるので、注意が必要です。
用例:
// CSV のテスト用のテキストをファイルに書き込む
file = ::File.Open( "Test.csv", "out" );
if( file == null )
'Error!( "Failed to open file!\n" );
file.Write( ##\
123,456,789,0
"AAA","B B B","C,C,C"
"xxx""YYY""xxx","""ZZZ"""
あいうえお, イロハ ,"春, 夏,
秋, 冬",**End**
## );
file.Close();
// このテストファイル内の各 CSV フィールドを読み込んで、
// 鉤括弧で囲ってプリントする
file = ::File.Open( "Test.csv", "in" );
if( file == null )
'Error!( "Failed to open file!\n" );
field'CSV; // 各 CSV フィールドの読み込み用
term'LINE; // 各行終端(改行)の空読み用
for( file.Seek(0) ; ! file.IsEnd() ; file.Read( term ) )
{
while( file.Read( field ) >= 0 && field != null )
print "「" : field : "」" : -;
print ; // 1レコードごとに改行
}
file.Close();
このスクリプトを実行すると、次の通りプリントされます。
「123」「456」「789」「0」
「AAA」「B B B」「C,C,C」
「xxx"YYY"xxx」「"ZZZ"」
「あいうえお」「 イロハ 」「春, 夏,
秋, 冬」「**End**」
T'SSV 空白区切り文字列
機能: 対象 T に「 空白区切り文字列 」を示す入出力形式を設定します。
説明: この入出力形式は、次の対象に設定可能です。
・文字列の定数または変数
・数値(整数、実数)の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
その値を 'ssv 変換 した文字列と、その後に半角空白1字を付加した
バイト列が、出力されます。
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、
この変数には、入力バイト列から、次のようにして読み込まれた
SSVフィールドの値(文字列)が、設定されます。
・本フィールドの区切りは空白ですが、これは半角空白とタブです。
・この区切りとしての空白は、複数個連続しても、1個と同じです。
・入力バイト列の開始位置にある最初の空白部は読み飛ばします。
・SSVフィールドとしての実体は、最初の非空白部からです。
・このフィールドの最初の文字が
二重引用符の場合、「二重引用符フィールド」として扱います。
それ以外の場合、「通常フィールド」として扱います。
・二重引用符フィールドの場合:
・この囲い内は、通常文字として扱います。
・この囲い内に、改行があっても構いません。
・この囲い内の連続した2つの二重引用符は、1つの二重引用符の
通常文字として扱います。
・この囲いの左右端の二重引用符は、フィールド値には含みません。
・この囲い内が空の時、このフィールド値は、空文字列になります。
・この囲いの後に、通常文字があると、それ以降は、通常フィールド
として扱います。
・通常フィールドの場合:
・半角空白、タブ、改行は、含めません。
・途中に二重引用符があっても通常文字として扱いにします。
・このフィールドが、空フィールドになることはありません。
・区切りの空白は、勿論、フィールド値には含まれません。
・入力バイト列の読み込みは、区切りの空白、改行、入力終端の
どれかの直前で、終了します。いずれの場合でも、
次回の読み込みは、その位置からになります。
・入力バイト列の読み込みが、改行で終了した場合、
このフィールドの読み込みとしては、これ以降には進みません。
次のレコード行に進むには、行の空読みが必要です。
・改行の直前または、入力終端から、バイト列を読み込み始めた場合、
このフィールド値は、null になります。
・入力バイト列内に 0 値のバイトがあっても、特別な扱いはしません。
しかし、これは文字列内で0終端になるので、注意が必要です。
用例:
// SSV のテスト用のテキストをバッファに書き込む
bf = ::Buffer();
bf.Write( ##\
123 456 789 0
"AAA" "B B B" " C, C, C "
"xxx""YYY""xxx" """ZZZ"""
あいうえお イロハ "春 夏
秋 冬" **End**
## );
// 各 SSV フィールドを読み込んで、鉤括弧で囲ってプリント
field'SSV;
term'LINE;
for( bf.Seek(0) ; ! bf.IsEnd() ; bf.Read( term ) )
{
while( bf.Read( field ) >= 0 && field != null )
print "「" : field : "」" : -;
print ; // 1レコードごとに改行
}
このスクリプトを実行すると、次の通りプリントされます。
「123」「456」「789」「0」
「AAA」「B B B」「 C, C, C 」
「xxx"YYY"xxx」「"ZZZ"」
「あいうえお」「イロハ」「春 夏
秋 冬」「**End**」
T'KC(n) n 字の一般文字列
機能: 対象 T に「 n 字の一般文字列 」を示す入出力形式を設定します。
説明: この字数 n は、バイト数ではなく、文字の個数になります。
この場合、半角文字でも全角文字でも、1字にカウントします。
Shift-JIS コードの場合、半角文字は1バイト、全角文字は2バイト
になるので、字数が同じでも、バイト数が同じになるとは限りません。
対象の文字が全て半角の時は、字数とバイト数は同じになります。
この入出力形式は、次の対象に設定可能です。
・文字列の定数または変数
・数値(整数、実数)の定数または変数
・空箱(この箱は本形式設定後、空文字列の箱に変わります)
対象の箱が不在の場合、空文字列の箱として新規に生成します。
n の有効範囲は、0 〜 1000 です。
n が省略時、n = 1 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
次のように、各データ型に応じて、そのバイト列が出力されます。
・文字列の場合、その文字列の字数が、
n 未満の時、その文字列の先頭から n 字分
n 以上の時、その文字列の全部分
・整数値 → 固定長バイト列(10進表記)
・実数値 → 固定長バイト列(f型表記)
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、入力バイト列から、次のようにして読み込まれた文字列が
設定されます。
・入力バイト列の開始位置から n 字分の文字列が読み込めれば、
この文字列がこの入力値になります。
・入力バイト列の開始位置から入力終端までに、n 字分の文字列が
なければ、その間の文字列がこの入力値になります。
・読み込みが入力終端から始まった場合、この入力値は、null に
なります。これによって、入力終端の終端を検知できます。
・入力バイト列内に 0 値のバイトがあっても、特別な扱いはしません。
しかし、これは文字列内で0終端になるので、注意が必要です。
●バイナリーデータ(固定長)形式
数値を表わす固定長のバイナリーデータの形式には、いろいろありますが、
次の各リレー型関数は、そのデータ型の入出力形式を設定します。
関数名 | データ型 | バイト数 |
'BYTE | 8 ビット整数(符号有り) | 1 バイト |
'UBYTE | 8 ビット整数(符号無し) | 1 バイト |
'SHORT | 16 ビット整数(符号有り) | 2 バイト |
'USHORT | 16 ビット整数(符号無し) | 2 バイト |
'LONG | 32 ビット整数(符号有り) | 4 バイト |
'ULONG | 32 ビット整数(符号無し) | 4 バイト |
'FLOAT | 32 ビット浮動小数点数 | 4 バイト |
'DOUBLE | 64 ビット浮動小数点数 | 8 バイト |
これらのリレー型関数が、この用途に使われる場合、その対象は必須ですが、
引数は無しにしないといけません。ちなみに、引数があると、純粋配列の生成
になってしまいます。⇒ 注意事項
これらのバイナリーデータの入出力形式は、次の対象に設定可能です。
・数値(整数、実数)の定数または変数
・文字列の定数または変数
・空箱(この箱は、本形式の設定によって、次のように変わります。
整数型の形式を設定すると、0 値の整数箱に変わります。
実数型の形式を設定すると、0 値の実数箱に変わります。)
対象の箱が不在の場合、
整数型の形式では、0 値の整数箱として新規に生成します。
実数型の形式では、0 値の実数箱として新規に生成します。
これらのリレー型関数の返値は、次のようになります。
・対象が箱の時、その箱への参照
・対象が実値の時、その値自身
・対象が不正な時、null
これらのバイナリーデータ形式が設定された変数/定数が、出力関数の引数に
指定された場合、次のバイト列が出力されます。
整数型の形式の場合:
・整数値 → 各データ型の整数値
・実数値 → 各データ型の整数値
・文字列 → 'int(10) 変換した整数値 → 各データ型の整数値
実数型の形式の場合:
・実数値 → 各データ型の実数値
・整数値 → 各データ型の実数値
・文字列 → 'float 変換した実数値 → 各データ型の実数値
これらのバイナリーデータ形式が設定された変数が、入力関数の引数に指定
された場合、この変数には、入力バイト列から、次のようにして読み込まれた
数値が設定されます。
・整数型の形式の場合、入力バイト列から、そのデータ型のバイト分を
読み込んで、そのバイト列に対応する整数値を、本言語の整数形式に
変換して、その箱に代入します。
・実数型の形式の場合、入力バイト列から、そのデータ型のバイト分を
読み込んで、そのバイト列に対応する実数値を、本言語の実数形式に
変換して、その箱に代入します。
・入力バイト列から、各データ型のバイト分を読み込めない場合、つまり、
入力終端までの残りが、そのバイト数に満たない場合、その箱には、
null を代入します。
●ビットフィールド(固定長)形式
ビット構成を意識した入出力を行なうには、ビットフィールドの入出力形式を
使うと便利です。
例えば、16-bit (2-byte) のバイナリーデータ内に、色の RGB を各 5-bit ずつ
割り当てて使用する場合、次のようになります。
Color'BIT_FIELD(2); // 2-byte のビットフィールド箱を生成
Color.Blue 'BIT(5); // 最初の 5-bit を「青」に割り当て
Color.Green'BIT(5); // 次 の 5-bit を「緑」に割り当て
Color.Red 'BIT(5); // 最後の 5-bit を「赤」に割り当て
これで、Color 箱を入出力する時には、2-byte のバイト列になり、その中の
ビット構成は、RGB 各 5-bit ずつになります。なお、'BIT_FIELD と 'BIT の
リレー型関数については、後程説明します。
ビットフィールド内では、下位ビットから上位ビットの方向へ、各ビット列が
割り当てられていきます。上例の場合、16-bit 内の LSB(最下位ビット)を
Bit-0、以降順に、Bit-1, Bit2, ... として、MSB(最上位ビット)を、Bit-15
とすると、
Bit-0〜4: 青, Bit-5〜9: 緑, Bit-10〜14: 赤, Bit-15: 未使用
のように割り当てられています。
ビットフィールドの箱は、それを示す入出力形式が設定されている複合箱に
なります。また、その中の各ビットの箱(上例では、Blue, Green, Red )も、
通常の箱ですが、それを示す入出力形式が設定されています。そのため、
各ビットの箱は、それに値を代入したり、演算式で使用することができます。
例えば、上例の各ビット箱に、値を代入するには、次のようになります。
Color.Blue = 24; // = 0b11000
Color.Green = 14; // = 0b01110
Color.Red = 21; // = 0b10101
ちなみに、これは、次のようにしても構いません。
Color = { 24, 14, 21 };
ビットフィールドを出力する場合、通常通り行なえます。例えば、上例の
Color を、バッファに出力する場合、次のようになります。
bf = ::Buffer();
bf.Write( Color );
ちなみに、これを、USHORT の入出力形式で読み出して、プリントする場合、
次のようになります。
bf.Seek(0);
bf.Read( U'USHORT );
print U'b(16);
これを実行すると、次の通りプリントされます。
0101010111011000
ビットフィールドを入力する場合も、通常通り行なえます。
例えば、先程のバッファに、USHORT の形式で値を書き込んで、Color に
読み出す場合、次のようになります。
bf.Seek(0);
bf.Write( 0b0`11111`00000`10101'USHORT );
bf.Seek(0);
bf.Read( Color );
print Color.Red'b(5), Color.Green'b(5), Color.Blue'b(5);
ちなみに、これを実行すると、次の通りプリントされます。
11111, 00000, 10101
次に、ビットフィールドに関連するリレー型関数について、説明します。
T'BIT_FIELD(n) n バイトのビットフィールド
機能: 対象 T に「 n バイトのビットフィールド」を示す入出力形式を
設定します。
説明: この入出力形式は、基本的に、複合箱にのみ設定可能です。
この入出力形式の設定時に、その対象が複合箱でない場合、
次のようになります。
対象の箱が不在の場合、空の複合箱として新規に生成します。
対象の箱が単一箱の場合、その箱を空の複合箱に変えます。
但し、数値/文字列/空以外の単一箱には、設定できません。
なお、対象の箱が複合箱の場合、その中身は変わりません。
但し、ファイル/バッファの箱には、設定できません。
バイト数 n の有効範囲は、1 〜 1000 です。
n が省略時、n = 1 として扱います。
返値: 対象が箱の時、その箱への参照を返します。
対象が不正な時、null を返します。
補説: この入出力形式が設定された箱(ビットフィールド箱)が、
入出力関数の引数に指定された場合、実際にその入出力の対象になる
のは、この箱自身ではなく、この箱内の直下にある「ビット規定箱」
になります。このビット規定箱というのは、'BIT, 'SBIT, 'UBIT の
どれかの入出力形式が設定された箱のことです。この箱のデータ型は、
通常、整数ですが、実数でも構いません。実数の場合は、整数に変換
されて使用されます。それ以外のデータ型は、不可です。
なお、ビットフィールド箱内に、ビット規定箱以外の箱があっても
構いませんが、入出力の対象にはなりません。
出力: ビットフィールド箱が、出力関数の引数に指定された場合、
この箱内にある各「ビット規定箱」のビット列が、次のようにして、
n バイトに合成されて、出力されます。
・ビット規定箱のビット列は、その箱の整数値の 32 ビットのうち、
下位部分の規定数分のビット列になります。つまり、その箱で
規定されているビット数を k とすると、Bit(k-1) 〜 Bit0 の
部分のビット列になります。
・各「ビット規定箱」のビット列は、n バイト内の下位ビットから
上位ビットの方向に、順に埋められていきます。
・n バイト内で余ったビットには、0 が埋められます。
・n バイトを超過したビットは、無視されます。
入力: ビットフィールド箱が、入力関数の引数に指定された場合、
入力バイト列から n バイト分が読み込まれて、
各「ビット規定箱」に、次のようにして、分配されます。
・各「ビット規定箱」で規定されているビット数分のビットは、
入力された n バイト内の下位ビットから上位ビットの方向に、
順に取得されていきます。
・このように取得された各ビット列は、整数値に変換されて、
各ビット規定箱に代入されます。
・このビット列から整数値(32-bit)への変換では、
そのビット規定箱で規定されている符号の有無に依存します。
符号無しの場合、そのビット列は、整数値の 32-bit のうちの
下位のビット部分になり、残りの上位のビット部分は、すべて
0 になります。
符号有りの場合、そのビット列は、整数値の 32-bit のうちの
下位のビット部分になり、残りの上位のビット部分は、すべて、
そのビット列の最上位ビットと同じになります。
・n バイトのビットでは、ビットフィールド箱内のすべての
「ビット規定箱」のビットを賄いきれなかった場合、
不足分のビットはすべて 0 になります。
なお、もし、入力バイト列から n バイト分を読み込めなければ、
その入力関数は、エラーになりますが、ビットフィールド箱は、
現状が維持されます。
T'BIT(n) or T'UBIT(n) n ビット整数値(符号無し)
機能: 対象 T に「 n ビット整数値(符号無し)」を示す入出力形式を
設定します。
説明: 'BIT と 'UBIT は、名前が違うだけで、機能は同じです。ちなみに、
'UBIT の U は、unsigned の意味です。'UBIT は、'SBIT との対比で、
符号無しを明示したい場合に使います。
この入出力形式は、通常、ビットフィールド箱( 'BIT_FIELD の
入出力形式が設定された箱)内の箱に設定します。この場合、
その箱のデータ型は、通常、整数にします。実数でも構いませんが、
入出力時に有効なのはそれが整数に変換された値です。なお、
数値以外のデータ型は、この場合には使えません。
ビットフィールド箱内でない場合、次の対象に設定可能です。
・数値(整数、実数)の変数または定数
・文字列の変数または定数
・空箱(この箱は本形式設定後、0 値の整数箱に変わります)
いずれの場合でも、対象の箱が不在なら、その箱を 0 値の整数箱
として新規に生成します。
ビット数 n の有効範囲は、1 〜 32 です。
n が省略時、n = 1 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
補説: ビットフィールドの入出力を行なう場合、入出力関数の引数には、
本形式が設定された変数/定数を直接指定するのではなく、通常は、
ビットフィールド箱を指定します。この場合の動作については、
'BIT_FIELD で説明しています。
以下では、入出力関数の引数自身に直接、本形式が設定されている
(比較的特殊な)場合について、説明します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
その値が整数なら、その下位 n ビットを保持するのに必要な最少の
バイト列が、下位バイトから上位バイトの順に出力されます。その際、
バイト列のビット数が n よりも多い場合、余ったビットは全て 0 に
なります。一方、その引数の箱の値が整数でなければ、それを整数に
変換した値が同様に出力されます。
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、次のような整数値が代入されます。まず、n ビットを確保
するのに必要な最少のバイト列が読み込まれます。このバイト列の
下位 n ビットが、その整数値の下位 n ビットになります。その整数
値の残りの上位ビットは、全て 0 になります。
T'SBIT(n) n ビット整数値(符号有り)
機能: 対象 T に「 n ビット整数値(符号有り)」を示す入出力形式を
設定します。
説明: 'SBIT の S は、signed の意味です。
この入出力形式は、通常、ビットフィールド箱( 'BIT_FIELD の
入出力形式が設定された箱)内の箱に設定します。この場合、
その箱のデータ型は、通常、整数にします。実数でも構いませんが、
入出力時に有効なのはそれが整数に変換された値です。なお、
数値以外のデータ型は、この場合には使えません。
ビットフィールド箱内でない場合、次の対象に設定可能です。
・数値(整数、実数)の変数または定数
・文字列の変数または定数
・空箱(この箱は本形式設定後、0 値の整数箱に変わります)
いずれの場合でも、対象の箱が不在なら、その箱を 0 値の整数箱
として新規に生成します。
ビット数 n の有効範囲は、1 〜 32 です。
n が省略時、n = 1 として扱います。
返値: 対象が箱の時、その箱への参照、実値の時、その値自身を返します。
対象が不正な時、null を返します。
補説: ビットフィールドの入出力を行なう場合、入出力関数の引数には、
本形式が設定された変数/定数を直接指定するのではなく、通常は、
ビットフィールド箱を指定します。この場合の動作については、
'BIT_FIELD で説明しています。
以下では、入出力関数の引数自身に直接、本形式が設定されている
(比較的特殊な)場合について、説明します。
出力: 本形式が設定された変数/定数が、出力関数の引数に指定された場合、
その値が整数なら、その下位 n ビットを保持するのに必要な最少の
バイト列が、下位バイトから上位バイトの順に出力されます。その際、
バイト列のビット数が n よりも多い場合、余ったビットは全て 0 に
なります。一方、その引数の箱の値が整数でなければ、それを整数に
変換した値が同様に出力されます。
入力: 本形式が設定された変数が、入力関数の引数に指定された場合、この
変数には、次のような整数値が代入されます。まず、n ビットを確保
するのに必要な最少のバイト列が読み込まれます。このバイト列の
下位 n ビットが、その整数値の下位 n ビットになります。その整数
値の残りのビットは全て、n ビットのうちの最上位ビットと同じ値に
なります。
●構造体(レコード)形式
構造体は、通常、「構造体設定文」の実行によって、設定します。
'STRUCT というリレー型関数を使えば、これと同様のことが行なえます。
例えば、構造体設定文で、
POINT ::= { .X 'LONG; .Y 'LONG; }
として、設定した構造体は、'STRUCT を使って、次のように設定できます。
POINT 'STRUCT;
POINT.X 'LONG;
POINT.Y 'LONG;
この場合、どちらの方法で、構造体を設定しても、結果は同じです。
構造体の入出力に関しては、「構造体」の章の「入出力時のバイナリー構成」
で説明しています。
●入出力形式の設定解除/変更
箱に設定した入出力形式は、'attr というリレー型関数を使って、任意に
解除することができます。例えば、
A 'STRUCT;
を実行すると、箱 A は、構造体の箱になりますが、その後で、
A 'attr(0);
を実行すると、箱 A は、通常の「箱を入れる箱」になります。
箱に設定した入出力形式は、再設定すれば、任意に変更することができます。
例えば、
X 'SHORT;
を実行すると、箱 X には、「 16 ビット整数(符号有り)」の形式が設定
されますが、その後で、
X 'UBYTE;
を実行すると、箱 X は、「 8 ビット整数(符号無し)」の形式の設定に
更新されます。
●ファイル/バッファ相互間の入出力
今までの説明では、ファイルやバッファの入出力対象が、数値や文字列の
変数/定数、あるいは、構造体やビットフィールドでしたが、
ファイルやバッファを入出力対象にすることもできます。例えば、ファイル
からバッファへデータを読み出すとか、ファイルAに別のファイルBのデータ
を書き込むとかも、直接できます。
ファイルやバッファを入出力対象にする場合、そのファイルやバッファの
インスタンスに、入出力形式を設定します。ちなみに、ファイルやバッファ
のクラスには、入出力形式を設定することはできません。
ファイルやバッファのインスタンスに入出力形式を設定した場合、その動作
は、各形式に応じて、次のようになります。なお、その動作は、入出力形式を
変数/定数に設定した場合とは、かなり違っているところもあります。
'C(n), 'I(n), 'X(n), 'F(n), 'BIT_FIELD(n) の場合、各形式の意味とは
無関係に、n バイト分のデータを入出力します。この場合、n は、整数値の
上限まで指定できます。
'LINE, 'LN, 'CSV, 'SSV, 'KC(n) の場合、各形式の意味に応じて、その分
のバイト列を入出力します。なお、'KC の n の有効範囲は通常通りです。
'SBYTE(n), 'UBYTE(n), 'SHORT(n), 'USHORT(n), 'LONG(n), 'ULONG(n),
'SGLFLT(n), 'DBLFLT(n) の場合、その形式のサイズ × n バイト分のデータを
入出力します。例えば、'LONG(10) では、4 x 10 = 40 バイトを入出力します。
このバイト数は、整数値の上限まで有効です。
なお、この形式では、純粋配列と同様に、引数を、( n, m, ... ) のように
多次元で指定しても構いません。その場合、その形式のサイズ × 全要素数 の
バイトを入出力します。例えば、'SHORT(5,10) では、2 x 5 x 10 = 100 バイト
を入出力します。
'BIT(n), 'UBIT(n), 'SBIT(n) の場合、n ビットに必要な最小限のバイト数
を入出力します。例えば、'BIT(10) では、2 バイトを入出力します。
このビット数は、整数値の上限まで有効です。
デフォールトでは、ファイルやバッファのインスタンスには、入出力形式は、
設定されていません。また、'attr(0) で、それらの入出力形式の設定を解除
することができます。
入出力形式が設定されていない場合、入力側のファイル/バッファの現時点
での入力開始位置から入力終端までの全バイト列が読み込まれて、出力側の
ファイル/バッファに書き込まれます。
この関連の例を、以下に示します。
次のスクリプトは、"File.txt" というファイルを開いて、その全内容を、
バッファに読み出して使うところです。
buff = ::Buffer();
file = ::File.Open( "File.txt", "in" );
if( file == null )
'Error!( "Failed to open file!\n" );
file.Read( buff );
file.Close();
buff.Seek(0);
・・・・・
次のスクリプトは、バッファに 0 〜 999 の 16-bit 整数値を 1000 個 書き
込んで、そのうちの最初の 800 個のデータを、"File.bin" というファイルに
書き込みます。ちなみに、このファイルのサイズは、2 x 800 = 1600 バイトに
なります。
bf = ::Buffer();
for( i = 0 ; i < 1000 ; i++ )
bf.Write( i'SHORT );
bf.Seek(0);
fl = ::File.Open( "File.bin", "out" );
if( fl == null )
'Error!( "Failed to open file!\n" );
fl.Write( bf'SHORT( 800 ) );
fl.Close();
●純粋配列の入出力
純粋配列の箱には、入出力形式を設定できません。しかし、
純粋配列自身を、入出力関数の引数に指定することはできます。その場合、
その純粋配列の全データが、入出力の対象になります。
例えば、先程の例で作成した "File.bin" というファイルの後半のデータ
(800 バイト) を、10 x 40 の2次元の USHORT の純粋配列に読み出す場合、
次のようになります。
A'USHORT( 10, 40 );
file = ::File.Open( "File.bin", "in" );
if( file == null )
'Error!( "Failed to open file!\n" );
file.Seek( 800 );
file.Read( A );
print A(0,0), A(0,1), "...", A(9,38), A(9,39);
ちなみに、これを実行すると、次の通りプリントされます。
400, 401, ..., 798, 799
なお、純粋配列の要素は、通常の数値と同様なので、それに入出力形式を
設定して、入出力関数の引数に指定することができます。
●入出力形式なしでの入出力
入力関数の引数の箱(純粋配列以外)に入出力形式が設定されていない場合、
何をどのように入力するのか判断できないので、その入力はエラーになります。
出力関数の引数に入出力形式が設定されていない場合、その引数の内容に応じて、
次のように出力されます。
整数値: 'LONG と同様にして出力されます。
実数値: 'DBLFLT と同様にして出力されます。
文字列: その文字列自身が出力されます。
ファイル、バッファ、純粋配列に関しては、既に説明した通りです。
その他: とりあえず無視されます(現状、エラーにはなりません)。
●メモリーブロックに対する入出力
今まで述べた入出力形式は、通常の大半の用途に対応できますが、直接メモリーの
アドレスとサイズを指定する必要がある入出力には不向きです。このような入出力を
行なう場合は、'MEMBLK というリレー型関数を使います。
この関数を使った例を以下に示します。この例では、"File.bin" というファイル
から、A という純粋配列のインデックスが 24 の要素のアドレスへ、80 バイト分の
データを読み出してきています。
A'UBYTE( 1000 );
file = ::File.Open( "File.bin", "in" );
file.Read( 'MEMBLK( A(24)'addr, 80 ) );
'MEMBLK が返す値は、変数に代入したり、関数の引数や返値に使うこともできます。
例えば、次の例は冗長ですが、前の例と同じことを行ないます。
function F( fname, mblk )
{
file = ::File.Open( fname, "in" );
file.Read( mblk );
}
A'UBYTE( 1000 );
F( "File.bin", 'MEMBLK( A(24)'addr, 80 ) );
余談ですが、上の2例では、ファイルをオープンした後、クローズしていませんが、
File クラスのインスタンス file が破棄される時、つまり、第1例では、スクリプト
の終了直前、第2例では、関数 F を抜ける時に、自動的にクローズされます。
●注意事項
構造体、ビットフィールド、ファイル/バッファのインスタンスに関しては、
既に述べた通りですが、それ以外の複合箱では、基本的に、
'C, 'I, 'X, 'F, 'LINE, 'LN, 'CSV, 'SSV, 'KC
'SBYTE, 'UBYTE, 'SHORT, 'USHORT, 'LONG, 'ULONG, 'SGLFLT, 'DBLFLT
'BIT, 'UBIT, 'SBIT
の入出力形式は、使えません。
単一箱や定数の(複製)代入では、その代入元の入出力形式は、その代入先
にコピーされない仕様になっています。例えば、
A 'LONG;
B = A;
C = 123 'SHORT;
としても、変数 B, C には、入出力形式が設定されていません。このような
仕様になっているのは、この代入では、必ずしも入出力形式のコピーが必要
でないことと、その分の処理を省いて、少しでも実行速度を上げるためです。
複合箱の(複製)代入では、その代入元の入出力形式は、その代入先に全て
コピーされます。例えば、
Point'STRUCT; Point.X'LONG; Point.Y'LONG;
P = Point;
Color'BIT_FIELD(2);
Color.Blue'BIT(5); Color.Green'BIT(5); Color.Red 'BIT(5);
Q = Color;
とすると、箱 P, Q は、それぞれ、Point 構造体、Color ビットフィールドと
同じ構造で同じ入出力形式が設定されています。