## 9.5 ファイルの位置決め

入出力ストリームのファイル位置決め演算のサポートは、各ストリームの文字の符号化によって異なります。ASCII や UNICODE などの固定幅符号化の場合、ファイルストリームクラスには、'C' stdio による位置決め演算子セットよりも充実した、位置決め演算子の完全セットが用意されています。JIS など可変幅で状態依存符号化のオプションには、さらに制約があります。これらのより複雑な符号化スキーマの位置決め演算子は、「先頭までシーク」、「末尾までシーク」、「位置を指定してシーク」のいずれかです。最後のオプションの場合は、その位置に移動して保存してからシークを実行します。可変幅や状態依存文字符号化の符号化スキーマで、ストリーム上の任意のオフセットをシークしても、ファイル位置には無効です。符号化スキーマに関係なくファイルストリームの位置をシークする方法の例を、次に示します。

>     int main(int argc, char argv[])
>     {
>       ofstream fs("foo.out");
>       fs << "Anyone";                        //1
>       ofstream::pos_type p = fs.tellp();     //2
>       fs << " remember J.P. Patches?";       //3
>       fs.seekp(p);                           //4

>       return 0;
>     }

//1 | 文字を出力して、後でシークバックする任意の位置までファイル位置を移動します。
//2 | `tellp()` 関数で現在のファイル位置を保存します。
//3 | さらにテキストを出力し、それと共にファイル位置を移動します。
//4 | 最後に、保存した位置までシークバックします。

可変幅や状態依存文字符号化スキーマのストリームで、任意の位置 (ファイルの先頭や末尾以外の位置) までシークするには、これ以外の方法はありません。この方法は、固定幅符号化スキーマにも適用することができます。

上記の例は、2 つの出力ファイル位置決め関数 `tellp` および `seekp` の 1 バージョンの内の 1 つを利用したものです。ofstream には、もう 1 つのバージョンの `seekp` 関数があり、'C' stdio `fseek` 関数と非常によく似た方法で任意のオフセットをシークすることができます。この関数は、どのような ofstream でも、その先頭や末尾をシークすることができます。また、固定幅文字符号化スキーマの ofstream 内の任意の場所をシークすることができます。次に例を示します。

>     ofstream fs("foo.out");
>     fs << "Anyone remember J.P. Patches?";
>     fs.seekp(-2,ios_base::cur);             //1
>     fs.seekp(0,ios_base::beg);              //2

//1 | 2 文字分シークバックします。`Patches` の `s` に移動します。
//2 | ファイルの先頭までシークします。

この関数の最初のパラメータは、`ofstream::off_type` です。2 番目は 3 つの定数の内の 1 つであり、シークの開始位置を示します。これら 3 つの値は、'C' stdio 関数 `fseek` で利用できる 3 種類のシークに対応しています。これらは基底クラス `ios_base` で定義します。次の表に、この `seekp` のバージョンで実行できる 3 種類のシークを示します。

#### 表 13 -- seekp で可能なシーク

シークの種類 | seekp の引数 | 'C' stdio の等価
--- | --- | ---
ファイルの先頭からシーク | `ios_base::beg` | `SEEK_SET`
ファイルの末尾からシーク | `ios_base::end` | `SEEK_END`
現在の位置からシーク | `ios_base::cur` | `SEEK_CUR`

例で示したように、シークの種類 `ios_base::beg` のオフセットとして `0` を渡すと、ファイルの先頭をシークします。同様に、`ios_base::end` に `0` を指定すると、ファイルの末尾をシークします。この関数は、シーク演算後に現在の位置を返すので、`ios_base::cur` に `0` を渡すと、移動せずに現在のファイル位置を得ることができます。これは、 `tellp()` 関数の呼び出しと同じ結果になります。

ifstream クラスには同じ関数セットが用意されていますが、名前が少し異なります。すなわち、`tellp()` の代わりに `tellg()`、`seekp(...)` の代わりに `seekg(...)` となっています。この特殊化された命名スキーマの理由は、fstream クラスでは、入力ストリームと出力ストリームを別々に操作できるように、両方の関数セットが用意されているためです。

### 9.5.1 入出力アーキテクチャーにおける位置決めのはたらき

iostream クラス定義では、シーク関数が定義されていません。その代わりに、`tellp` と `seekp` の場合は basic_ostream、`tellg` と `seekg` の場合は basic_istream のように基底クラスから呼び出します。これらの関数は、ストリームバッファで仮想関数を呼び出します。シーク機能は、この仮想関数に実装されています。ofstream、ifstream、fstream のシーク関数では、filebuf で seekoff と seekpos が呼び出されます。コードは次のようになります。

>     basic_ofstream
>     basic_ostream::seekp(pos)
>         -> basic_streambuf::pubseekpos(pos)
>             ->(virtual) basic_filebuf::seekpos(pos)

ストリームバッファで仮想関数を呼び出すと、バッファ操作と入出力を文字列の書式設定から完全に分離することができます。シーク演算を行う上でこのことを知っておく必要はありませんが、ストリームバッファのサブクラスが必要になったときに重要になります。

Copyright (c) 1998, Rogue Wave Software, Inc.
このマニュアルに関する誤りのご指摘やご質問は、電子メールにてお送りください。

OEM リリース, 1998 年 6 月