前のページ次のページ上に戻るホーム BREW C++ ライブラリ & GUI フレームワーク & XML ミドルウェア : SophiaFramework UNIVERSE 5.0

19.1. ストリーム

ストリームは、以下の 6 種類があります。

表 19.1. ストリーム

データの種類 入力ストリーム 出力ストリーム
文字列 ストリーム ( AChar 型文字列用 ) SFXAnsiStringStreamReader SFXAnsiStringStreamWriter
文字列 ストリーム ( WChar 型文字列用 ) SFXWideStringStreamReader SFXWideStringStreamWriter
バイナリ ストリーム( バイナリ列用 ) SFXBinaryStreamReader SFXBinaryStreamWriter
[Note] AChar と WChar について

BREW には、シングルバイト文字またはマルチバイト文字を表す char 型と、 BREW 独自の 2 バイト文字を表す AECHAR 型があります。 SophiaFramewok では、これら 2 種類の文字型をそれぞれ AChar 型、WChar 型として定義しています。

文字列を表すクラスには、AChar 型の文字で構成される SFXAnsiString と、 WChar 型の文字で構成される SFXWideString の 2 種類があります。

19.1.1. 文字列ストリーム

文字列ストリームは、文字列を読み書きするためのストリームです。

例 19.1. 読み込む文字列 ( 16 進数 )

61 62 63 64 00 65 66 67 68 00 69 6A 6B 6C

例 19.2. 文字列の読み込み

SFXAnsiStringStreamReader reader;

SFXAnsiString string;

reader > string; // '\0' までのデータを文字列として取得する  string = "abcd"
reader > string; // 次の '\0' までを取得する                 string = "efgh"
reader > string; // 末尾までを取得する                       string = "ijkl"
reader > string; // 末尾までを取得する                       string = 空

'\0' が見つからない場合は、終端までを文字列とします。

例 19.3. 文字列の書き込み

SFXAnsiStringStreamWriter writer;
SFXAnsiString string("abcd");

writer < string; // "abcd" を書き込む
writer < "efgh"; // "efgh" を書き込む
writer < string; // "abcd" を書き込む

結果は "abcdefghabcd" となります。結果の文字列に '\0' は挿入されません。

例 19.4. '\0' の書き込み

writer < ends;

19.1.2. バイナリ ストリーム

バイナリ ストリームは、バイナリ列を読み書きするストリームです。

例 19.5. 読み込むバイナリ列 ( 16 進数 )

11 22 33 44 12 34 56 78 11 22 33 44 55

例 19.6. バイナリ列の読み込み

SFXBinaryStreamReader reader;  // 入力バイナリ ストリーム

// reader の取得部分は省略

// バイナリ列をリトル エンディアンとして解釈する
reader > little;

UInt32 v;

reader > v; // 先頭から 4 バイトを UInt32 として解釈する
            // リトル エンディアンなので、v に 0x78563412 が代入される

// 以降のバイナリ列をビッグ エンディアンとして解釈する
reader > big;

reader.ReadUInt32(&v); // 次の 4 バイトを UInt32 として解釈する
                       // ビッグ エンディアンなので v = 0x11223344
                       // reader > v と同じ

UInt08 c;

reader > c; // 先頭から 1 バイトを UInt08 として解釈する : c = 0x55

reader.ReadUInt08(&c); // 先頭から 1 バイトを取得しようとするが、末尾まできたので c は変化しない
                       // この関数はエラーを返す
[Note] 注意
Read?????? 関数はエラー値を返す点がインサータ ( > ) と異なります。

例 19.7. バイナリ列の書き込み

SFXBinaryStreamWriter writer;

// writer の取得部分は省略

// バイナリ列をリトル エンディアンとして解釈する
writer < little;

writer.WriteUInt08(0x22);  // UInt08 として書き込む
writer.WriteUInt16(0x22);  // UInt16 として書き込む
writer.WriteUInt32(0x22);  // UInt32 として書き込む

// writer < 0x22; は曖昧なのでエラー。
// writer < static_cast<UInt08>(0x22); なら可

// 以降のバイナリ列をビッグ エンディアンとして解釈する
writer < big;

writer.WriteUInt32(0x22); // UInt32 として書き込む

writer.Flush();

例 19.8. 書き込まれたバイナリ列 ( 16 進数 )

22 22 00 22 00 00 00 00 00 00 22

19.1.3. バッファと文字列の扱い

SFXBuffer クラスや SFXAnsiString ( SFXWideString ) クラスのインスタンス変数もストリームの引数またはオペランドに指定できます。

19.1.3.1. バッファの読み書き

例 19.9. 読み込むバイナリ列 ( 16 進数 )

11 22 33 44 55 11 22 33 44 55 22

例 19.10. バッファからの読み込み

SFXBuffer buffer;
buffer.SetSize(4); // あらかじめ読み込むサイズを設定しておく必要がある

reader > buffer;   // 最初の 4 バイトを取得する  buffer には 11 22 33 44 ( 16 進数 ) が格納される
reader > buffer;   // 次の 4 バイトを取得する    buffer には 55 11 22 33 ( 16 進数 ) が格納される
reader > buffer;   // エラー                     残りの 2 バイトが読み込まれる訳ではない

buffer にはサイズを指定する必要があります。自動的にバッファが拡張されることはありません。

また、設定したサイズより少ないデータしか残っていない場合は、エラーになります。

例 19.11. バッファへの書き込み

SFXBuffer buffer;
AChar data[] = {0x11, 0x22, 0x33, 0x44, 0x55};

buffer.Set(data, 5);

writer < buffer; // 5 バイトのデータが書き込まれる
writer < buffer; // 5 バイトのデータが書き込まれる

例 19.12. 書き込まれたバイナリ列 ( 16 進数 )

11 22 33 44 55 11 22 33 44 55

19.1.3.2. 文字列の読み書き

例 19.13. 読み込む文字列 ( 16 進数 )

61 62 63 64 00 65 66 67 68 00 69 6A 6B 6C

例 19.14. 文字列から読み込み

SFXAnsiString string;

reader > string; // '\0' までのデータを文字列として取得する  string = "abcd"
reader > string; // 次の '\0' までを取得する                 string = "efgh"
reader > string; // 末尾に '\0' がないのでエラー             string = 変化なし

文字列の読み込みは '\0' をセパレータ ( 区切り ) として、セパレータまでのデータを SFXAnsiString と解釈して読み込みます。'\0' が見つからない場合はエラーとなり、文字列は格納されません。

例 19.15. 文字列の書き込み

SFXAnsiString string("abcd");

writer < string; // 4 バイトのデータと終端 '\0' が書き込まれる
writer < string; // 4 バイトのデータと終端 '\0' が書き込まれる

例 19.16. 書き込まれた文字列 ( 16 進数 )

61 62 63 64 00 61 62 63 64 00

19.1.4. 簡易パーサー

バイナリ ストリームは簡易パーサーとして利用できます。

例えば、

SFXAnsiString data1("ansi string1");
SFXAnsiString data2("ansi string2");
SFXWideString data3("wide string");
SInt32 data4 = -10;
UInt16 data5 = 20;
Bool data6 = true;

SFXBuffer buffer;

AChar temp[] = {0x11, 0x22, 0x33, 0x44, 0x55};

buffer.Set(temp, 5);

で、

writer < data1 < data2 < data3 < data4 < data5 < data6 < buffer;

として書き込んだデータは、

buffer.SetSize(5);

reader > data1 > data2 > data3 > data4 > data5 > data6 > buffer;

として読み込めます。

[Note] 注意
読み込む順番や型を変えると正しく動作しません。

19.1.5. ストリーム バッファ

入出力ストリームのバッファは、ストレージから読み込んだデータ分だけ自動的に拡張されます。

メモリを節約するには、ストリーム バッファのサイズを固定にして、複数回に分けてデータを読み込みます。

例 19.17. 固定長バッファによるファイル入力

SFCError error;  // エラー値
SFXFile file;    // ファイル クラス のインスタンス
SFXAnsiStringStreamReader reader; // ファイル読み込み用ストリーム
SFXAnsiString stringFromFile;     // この変数にデータが読み込まれる
SFXAnsiString tempString;

// 読み込みモードでファイルをオープンする
if ((error = file.OpenReadOnly(SFXPath("/dir1/data.txt"))) == SFERR_NO_ERROR) {

    // ファイル読み込み用ストリームを取得する ( 1024 はバッファ サイズ )
    if ((error = file.GetStreamReader(1024, &reader)) == SFERR_NO_ERROR) {

        while (!reader.Ends()) { // ファイル読み込み用ストリームの終端に到達するまで繰り返す

            // データを読み込む
            // バッファサイズを指定しているので、1024 バイトしか読み込まれない
            if ((error = reader.Fetch()) != SFERR_NO_ERROR) {
                // エラーのとき
                break; 
            }

            // tempString にデータを読み込む
            if ((error = reader.ReadSFXAnsiString(&tempString))
                != SFERR_NO_ERROR) { 
                // エラーのとき
                break;
            }

            // stringFromFile の末尾に tempString を加える
            stringFromFile += tempString;
        }
        
        reader.Release();
    }
    file.Close();
}

上のコードでは、バッファ サイズを 1024 バイトに設定しているので、 1 回の Fetch 関数の呼び出しで 1024 バイトまでのデータしか読み込まれません。 Fetch 関数の呼び出しを何回か繰り返してデータを読み込む必要があります。

例 19.18. 固定長バッファによるファイル出力

SFCError error;  // エラー値
SFXFile file;    // ファイル
SFXAnsiStringStreamWriter writer; // ファイル書き込み用ストリーム
SFXAnsiString string("abcdefghijklmnopqrstuvwxyz");  // 書き込むデータ
ACharConstPtr p = string.GetBuffer();                // 文字列を指すポインター
ACharConstPtr endOfString = p + string.GetLength();  // 文字列の末尾

SInt32 bufferSize = 1024;

// 読み書きモードでファイルをオープンする
if ((error = file.OpenReadWrite(SFXPath("/dir1/data.txt")))
    == SFERR_NO_ERROR) {

    // ファイル書き込み用ストリームを取得する
    if ((error = file.GetStreamWriter(bufferSize, &writer))
        == SFERR_NO_ERROR) {

        for (; p < endOfString; p += bufferSize) {

            // 書き込むサイズ
            SInt32 size = (endOfString - p < bufferSize) ? endOfString - p : bufferSize;

            // 文字列を書き込む
            if ((error = writer.Write(p, size)) != SFERR_NO_ERROR) {
                break; // エラー
            }
            // 実際にファイルにデータを書き込む
            if ((error = writer.Flush()) != SFERR_NO_ERROR) { 
                // エラーのとき
                break;
            }
        }
        writer.Release();
    }
    file.Close();
}

例 19.19. 固定長バッファによる TCP ソケット通信

class MyClass {
private:
    SFXTCPSocket _socket;                // TCP ソケットクラス
    SFXAnsiStringStreamReader  _reader;  // データ受信用ストリーム
    SFXAnsiStringStreamWriter  _writer;  // データ送信用ストリーム
    SFXAnsiString _writingString;        // 送信する文字列
    ACharConstPtr _p;
    ACharConstPtr _end;
    SInt32 _bufferSize;
    SFXAnsiString _readingString;        // 受信する文字列
public:
    Void Start(Void);
    CALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    CALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
    CALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
};

Void MyClass::Start(Void)
{
    // 送信する文字列
    _writingString = "GET / HTTP/1.1\r\n\r\n";
    _bufferSize = 1024;

    // 初期処理
    _socket.Open();

    // 接続開始 ( 接続の完了は、OnConnect 関数に通知される )
    _socket.Connect(SFXSocketAddress("www.example.com:80"), CALLBACK_FUNCTION(OnConnect));
}

// 接続の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    // データ受信用ストリームを取得する
    _socket.GetStreamReader(1024, &_reader);

    // データ送信用ストリームを取得する
    _socket.GetStreamWriter(_bufferSize, &_writer);

    _p = _writingString.GetBuffer();
    _end = _p + _writingString.GetLength();

    SInt32 size = (_end - _p < _bufferSize) ? _end - _p : _bufferSize;
    // データ送信用ストリームにデータを書き込む
    _writer.Write(_p, size);

    _p += size;

    // 実際にデータを送信する ( データ送信の完了は、OnFlush 関数に通知される )
    _writer.Flush(CALLBACK_FUNCTION(OnFlush));
}

// データ送信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    if (_p < _end) { // 送信するものが残っているとき
        // 再度、データ送信用ストリームにデータを書き込む

        SInt32 size = (_end - _p < _bufferSize) ? _end - _p : _bufferSize;
        // データ送信用ストリームにデータを書き込む
        _writer.Write(_p, size);

        _p += size;

        // 実際にデータを送信する ( データ送信の完了は、OnFlush 関数に通知される )
        _writer.Flush(CALLBACK_FUNCTION(OnFlush));
    }
    else { // データの受信

        // データを受信する ( データ受信の完了は、OnFetch 関数に通知される )
        _reader.Fetch(CALLBACK_FUNCTION(OnFetch));
        
        // ここで _reader.Read 関数を呼び出すとエラーになる
    }
}

// データ受信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString temp;

    // 受信したデータを temp 変数に格納する
    _reader.ReadSFXAnsiString(&temp);

    _readingString += temp;

    if (!_reader.Ends()) { // データ受信用ストリームの終端でないとき

        // データを受信する ( データ受信の完了は、OnFetch 関数に通知される)
        _reader.Fetch(CALLBACK_FUNCTION(OnFetch));

    }
}

サイズの大きなデータは、何回か繰り返し Fetch 関数を呼び出して読み込み(受信)ます。 そのたびにコールバック関数(OnFetch 関数)が呼び出されます。

また、サイズの大きなデータも、何回か繰り返し Flush 関数を呼び出して書き込み(送信)ます。 そのたびにコールバック関数(OnFlush 関数)が呼び出されます。

19.1.6. コールバックのタイミング

Fetch / Flush 関数で登録したコールバック関数が呼び出されるタイミングは、データの終端まで読み込んだときか、ストリーム バッファにデータが無くなったときです。

SetTrigger 関数を使うとそれ以外のタイミング(トリガー)を設定できます。

例 19.20. TCP ソケット通信で、改行文字 "\r\n" が現れるたびにコールバック関数が呼び出されるようにトリガーを設定する

// 一部抜粋、エラー処理は省略

// 接続の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    // データ受信用ストリームを取得する
    _socket.GetStreamReader(&_reader);

    // "\r\n"を読み込んだタイミングでコールバック関数が呼び出されるようにトリガーを設定する
    _reader.SetTrigger("\r\n", 2);

    // データを受信する ( データ受信の完了は、OnFetch 関数に通知される )
    _reader.Fetch(CALLBACK_FUNCTION(OnFetch));
}

// データ受信の完了が通知されるコールバック関数
CALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    // SetTrigger で指定したトリガーがこのコールバック関数に通知されたかどうか
    // (この場合のトリガー: 改行文字が現れたとき)
    if (_reader.Triggers()) {
        TRACE("by CRLF");
    }

    // 受信した文字列を string 変数に読み込む
    _reader.ReadSFXAnsiString(&string);

    while (!_reader.Ends()) { // データ受信用ストリームの終端でないかぎり
        // データ受信をする ( データ受信の完了は、OnFetch 関数に通知される )
        _reader.Fetch(CALLBACK_FUNCTION(OnFetch));
    }
}
[Note] 注意
指定した文字数を読み込む、或いは書き込む度にコールバック関数を呼び出すような設定も可能です。