【Delphi】TStreamReader / TStreamWriter の使い方

# はじめに
皆さんは Delphi でのテキストファイルの読み書きに何を使ってますか？TStringList ですか？TFileStremですか？昔ながらの Read(ln) / Write(ln) でしょうか？

今回は Unicode 版 Delphi であればもれなく使える **TStreamReader** と **TStreamWriter** のお話です。

# TStreamReader / TStreamWriter
.NET 互換の TStreamReader / TStreamWriter は Delphi 2009 で実装されました。

 - [system.io.streamreader (learn.microsoft.com)](https://learn.microsoft.com/ja-jp/dotnet/api/system.io.streamreader)
 - [system.io.streamwriter (learn.microsoft.com)](https://learn.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter)
 - [System.Classes.TStreamReader (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader)
 - [System.Classes.TStreamWriter (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter) 

TStreamReader / TStreamWriter は TStringList と違ってバッファをメモリに溜め込まないので大きなファイルを処理するのに向いています。

.NET 由来ではありますが Pascal の伝統的なファイル操作に似ている所もあって、実際に使ってみると意外と馴染みます。

## 基本的な TStreamReader / TStreamWriter の使い方

### ■ TStreamWriter の基本的な使い方

TStreamWriter の方から説明します。最も簡単な使い方は次のようになります。実行すると UTF-8 のテキストファイルが生成されます。

```pascal
uses
  ..., Classes;

var
  Writer: TStreamWriter;
begin
  Writer := TStreamWriter.Create('hello.txt'); // 上書きモード
  try
    Writer.WriteLine('Hello,');
    Writer.WriteLine('world.');
  finally
    Writer.Free;
  end;
end;
```

#### TStreamWriter のコンストラクタ (パラメータとしてファイル名を受け付ける)

| パラメータ | #1 | #2 | #3 | #4 |
|:---|:---|:---|:---|:---|
| 名前 | Filename | Append | Encoding | BufferSize |
| 型 | string | Boolean | TEncoding | Integer |
| デフォルト | (なし) | False | TEncoding.UTF-8 | 4096 [^1] |

 - [TStreamWriter.Create() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Create)
 
コンストラクタ `Create()` の 2 番目のパラメータとして True を指定すると追記モードになります (デフォルトで False です)。

```pascal
  // 追記モード, UTF-8
  Writer := TStreamWriter.Create('hello.txt', True); 
```

コンストラクタ `Create()` の 3 番目のパラメータとして  TEncoding を渡して文字コードを指定する事もできます。

```pascal
  // 上書モード, UTF-8
  Writer := TStreamWriter.Create('hello.txt', False, TEncoding.UTF8); 
```

注意点ですが、上書きモード (正確にはストリームポインタがファイルの先頭にある状態) かつ Encoding パラメータに TEncoding.UTF8 が指定された場合には BOM が書き込まれます。つまり、<font color="crimson">次の 2 つは同等ではありません</font>。

```pascal
  // 上書きモード, UTF-8 (BOM なし UTF-8)
  Writer := TStreamWriter.Create('hello.txt'); 
  
  // 上書きモード, UTF-8 (BOM あり UTF-8)
  Writer := TStreamWriter.Create('hello.txt', False, TEncoding.UTF8); 
```

ストリームの先頭へ移動すればこの問題を回避できます。ストリームのサイズを 0 に設定しないと、何も書き込まれなかった場合に 3 バイトのファイルができてしまいます。

```pascal
  // 上書きモード, UTF-8 (BOM あり UTF-8 だけど BOM を書き込まない)
  Writer := TStreamWriter.Create('hello.txt', False, TEncoding.UTF8); 
  Writer.BaseStream.Seek(0, TSeekOrigin.soBeginning); // ストリームの先頭に移動
  Writer.BaseStream.Size := 0;                        // ストリームのサイズを 0 にする
```  

逆に BOM を書き込まないコンストラクタを使っている状況で BOM を書き込むには次のようにします。

```pascal
var
  Writer: TStreamWriter;
  BOM: TArray<Byte>;
begin
  // 上書きモード, UTF-8 (BOM なし UTF-8 だけど BOM を書き込む)
  Writer := TStreamWriter.Create('hello.txt'); 
  BOM := Writer.Encoding.GetPreamble;              // 文字エンコーディングのプリアンブルを取得
  Writer.BaseStream.WriteBuffer(BOM, Length(BOM)); // プリアンブルを書き込む
  ...
```

#### TStreamWriter のプロパティ

| プロパティ | 型 | デフォルト | 説明 |
|:---|:---|:---|:---|
| [AutoFlush](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.AutoFlush) | Boolean | True | 自動でフラッシュ (ファイルへの書き出し) するか？ |
| [BaseStream](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.BaseStream) | TStream | 書き込みに使われている TStream | 書き込みに使われている TStream |
| [Encoding](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Encoding) | TEncoding | TEncoding.UTF8 | 文字エンコーディング |
| [NewLine](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.NewLine)  | string | sLineBreak | 改行文字 |

#### TStreamWriter のメソッド

| メソッド | 説明 |
|:---|:---|
| [Close()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Close) | TStreamWriter を閉じる。書き込みバッファにデータがあればフラッシュされる。このメソッドはデストラクタで呼ばれ、明示的に呼んでも TStreamWriter のインスタンスは破棄されない |
| [Flush()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Flush) | フラッシュ (ファイルへの書き出し) を行う。AutoFlush プロパティが True の場合には呼び出す必要がない |
| [Free()](https://docwiki.embarcadero.com/Libraries/ja/System.TObject.Free) | TStreamWriter を破棄する。デストラクタで Close() が呼ばれる |
| [OwnStream()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.OwnStream) | 書き込みに使われている TStream のオーナーを TStreamWriter に設定する|
| [Write()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Write) | データを文字列として書き込む |
| [WriteLine()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.WriteLine) | データを行の終端文字で終わる文字列として書き込む |

### ■ TStreamReader の基本的な使い方

次は TStreamReader です。最も簡単な使い方は次のようになります。実行すると (BOM なし) UTF-8 のテキストファイルを読み込みます。

```pascal
uses
  ..., Classes;

var
  Reader: TStreamReader;
  LineStr: string;
begin
  Reader := TStreamReader.Create('hello.txt');
  try
    while not Reader.EndOfStream do
    begin
      LineStr := Reader.ReadLine;
      ...
    end;
  finally
    Reader.Free;
  end;
end;  
```

#### TStreamReader のコンストラクタ (パラメータとしてファイル名を受け付ける)

| パラメータ | #1 | #2 |
|:---|:---|:---|
| 名前 | Filename | DetectBOM |
| 型 | string | Boolean |
| デフォルト | (なし) | True |

| パラメータ | #1 | #2 | #3 | #4 |
|:---|:---|:---|:---|:---|
| 名前 | Filename | Encoding | DetectBOM | BufferSize |
| 型 | string | TEncoding | Boolean | Integer |
| デフォルト | (なし) | TEncoding.UTF-8 | False | 4096 [^1] |

 - [TStreamReader.Create() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Create)

コンストラクタ `Create()` の DetectBOM パラメータに True を指定すると UTF-8 (BOM あり) を検出して読み込めます。つまり、次のような記述であれば、BOM の有無を気にせず UTF-8 形式のファイルを読み込めます。

```pascal
  // UTF-8, BOM 自動検出
  Reader := TStreamReader.Create('hello.txt', True);
```

第 2 パラメータ Encoding に TEncoding が指定可能なオーバーロードされたコンストラクタがあります。

```pascal
  // 日本語環境では恐らく Shift_JIS
  Reader := TStreamReader.Create('hello.txt', TEncoding.Default);
```

Shift_JIS を強制するには次のようにします。

```pascal
var
  Reader: TStreamReader;
  LineStr: string;
  Enc: TEncoding;
begin
  // Shift_JIS
  Enc := TEncoding.GetEncoding(932);
//Enc := TEncoding.GetEncoding('shift_jis'); // XE2 以降は名前でも OK
  Reader := TStreamReader.Create('hello.txt', Enc);
  try
    while not Reader.EndOfStream do
    begin
      LineStr := Reader.ReadLine;
      Writeln(LineStr);
    end;
  finally
    Reader.Free;
    Enc.Free;
  end;
end;  
```


コンストラクタの Encoding パラメータに TEncoding を指定し、DetectBOM パラメータに True を設定すると UTF-8 (BOM あり) だった場合には UTF-8 (BOM あり) で、それ以外の場合には指定された文字エンコーディングで読み込みます。

```pascal
  // 文字エンコーディング指定, BOM 自動検出
  Reader := TStreamReader.Create('hello.txt', TEncoding.Default, True);
```

パラメータ数によって DetectBOM のデフォルト値が異なる事に注意が必要です(理屈は解りますけれど...)。混乱を防ぐために、パラメータ DetectBOM は明示的に指定した方がいいのかもしれません。

| パラメータ数 | DetectBOM |
|:---|:---|
| パラメータが 1 つ<br>(Filename) | True |
| パラメータが 2 つ<br>(Filename, DetectBOM) | 指定した DetectBOM |
| パラメータが 2 つ<br>(Filename, Encoding) | False |
| パラメータが 3 つ<br>(Filename, Encoding, DetectBOM) | 指定した DetectBOM |
| パラメータが 4 つ<br>(Filename, Encoding, DetectBOM, BufferSize) | 指定した DetectBOM |

:::note warn
Delphi XE7〜10.2 Tokyo で、TEncoding を指定し、かつ DetectBOM を有効にしたコンストラクタを使って BOM あり UTF-8 ファイルを読み込むとエラーになる事があるようです。

根本的な解決方法はありませんが、問題が起こる環境では自前で BOM を判定すればいいだけなので、解っていれば対処は難しくないと思います。

 - [TStreamReaderを利用してUTF-8(BOM付き)ファイルを読み込むと、Unicode 文字のマッピングがターゲットのマルチバイトコードページに存在しません」という例外が発生する (Support Wiki)](https://docwiki.embarcadero.com/Support/ja/TStreamReader%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6UTF-8(BOM%E4%BB%98%E3%81%8D)%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80%E3%81%A8%E3%80%81Unicode_%E6%96%87%E5%AD%97%E3%81%AE%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0%E3%81%8C%E3%82%BF%E3%83%BC%E3%82%B2%E3%83%83%E3%83%88%E3%81%AE%E3%83%9E%E3%83%AB%E3%83%81%E3%83%90%E3%82%A4%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AB%E5%AD%98%E5%9C%A8%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E4%BE%8B%E5%A4%96%E3%81%8C%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B)
:::

**See also:**

 - [System.SysUtils.TEncoding (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.TEncoding)

#### TStreamReader のプロパティ

| プロパティ | 型 | デフォルト | 説明 |
|:---|:---|:---|:---|
| [BaseStream](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.BaseStream) | TStream | 読み込みに使われている TStream | 読み込みに使われている TStream |
| [CurrentEncoding](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.CurrentEncoding) | TEncoding | - |  現在の文字エンコーディング |
| [EndOfStream](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.EndOfStream)  | Boolean  | -| ストリームの終端ならば True |

#### TStreamReader のメソッド

| メソッド | 説明 |
|:---|:---|
| [Close()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Close) | TStreamReader を閉じる。このメソッドはデストラクタで呼ばれ、明示的に呼んでも TStreamReader のインスタンスは破棄されない |
| [DiscardBufferedData()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.DiscardBufferedData) | バッファされているすべてのデータを破棄する |
| [Free()](https://docwiki.embarcadero.com/Libraries/ja/System.TObject.Free) | TStreamReader を破棄する。デストラクタで Close() が呼ばれる |
| [OwnStream()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.OwnStream) [^4]| 読み込みに使われている TStream のオーナーを TStreamReader に設定する|
| [Peek()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Peek) | ストリームポインタを変更せずに、次の文字を取得する。ストリーム終端ならば -1 が返る |
| [Read()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Read) | ストリームポインタを変更し、次の文字を取得する。ストリーム終端ならば -1 が返る |
| [ReadBlock()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.ReadBlock) | 一連の文字を読み込む |
| [ReadLine()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.ReadLine) | 1 行分の文字列を読み込む |
| [ReadToEnd()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.ReadToEnd) | 行末までの文字列を読み込む |
| [Rewind()](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Rewind) [^2] | バッファされているすべてのデータを破棄し、ストリームポインタをストリームの先頭へ移動する |

:::note
Rewind() は古いバージョンには存在しませんが、次のコードと同等です。
```pascal
  Reader.DiscardBufferedData;
  Reader.BaseStream.Seek(0, TSeekOrigin.soBeginning);
```
:::

### ■ コンストラクタに TStream を指定する場合
TStreamReader / TStreamWriter には TStream をパラメータとして受け付けるオーバーロードされたコンストラクタがあります。

#### TStreamWriter のコンストラクタ (パラメータとして TStream を受け付ける)

| パラメータ | #1 | #2 | #3 |
|:---|:---|:---|:---|
| 名前 | Stream | Encoding | BufferSize |
| 型 | TStream | TEncoding | Integer |
| デフォルト | (なし) | TEncoding.UTF-8 | 4096 [^1] |

 - [TStreamWriter.Create() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamWriter.Create)

#### TStreamReader のコンストラクタ (パラメータとして TStream を受け付ける)

| パラメータ | #1 | #2 |
|:---|:---|:---|
| 名前 | Stream | DetectBOM |
| 型 | TStream | Boolean |
| デフォルト | (なし) | True |

| パラメータ | #1 | #2 | #3 | #4 |
|:---|:---|:---|:---|:---|
| 名前 | Stream | Encoding | DetectBOM | BufferSize |
| 型 | TStream | TEncoding | Boolean | Integer |
| デフォルト | (なし) | TEncoding.UTF-8 | False | 4096 [^1] |

 - [TStreamReader.Create() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStreamReader.Create)

パラメータに TStream が渡された場合、デフォルトでは TStreamReader / TStreamWriter を破棄しても TStream は破棄されません。

例えば IOUtils.TFile [^3] を使ってファイルストリームを作って渡す場合には、ファイルストリームも破棄する必要があります。

```pascal
uses
  ..., IOUtils;

...  

  Writer := TStreamWriter.Create(TFile.Create('hello.txt'));
  try
    Writer.WriteLine('Hello,');
    Writer.WriteLine('world.');
  finally
    Writer.BaseStream.Free; // コンストラクタに渡された TStream を破棄
    Writer.Free;
  end;
```

コンストラクタに渡された TStream のオーナーを OwnStream() [^4] を用いて TStreamReader / TStreamWriter へと変更すれば TStreamReader / TStreamWriter が破棄されたと同時に破棄する事ができます。

```pascal
uses
  ..., IOUtils;

...  

  Writer := TStreamWriter.Create(TFile.Create('hello.txt'));
  try
    Writer.OwnStream; // Stream のオーナーを TStreamWriter に
    Writer.WriteLine('Hello,');
    Writer.WriteLine('world.');
  finally
    Writer.Free; // デストラクタで Close() が呼ばれ、Stream が破棄される。
  end;          
```

Delphi 2009 には IOUtils が存在しないので TFile が使えません。代わりに TFileStream を使って下さい。10.1 Berlin 以降だとバッファ付きの TBufferedFileStream [^5] も使えます。

```pascal
  // for Delphi 2009
  Writer := TStreamWriter.Create(TFileStream.Create('hello.txt', fmCreate or fmOpenWrite));
  try
    Writer.WriteLine('Hello,');
    Writer.WriteLine('world.');
  finally
    Writer.BaseStream.Free;
    Writer.Free;
  end;
```


**See also:**

 - [System.IOUtils.TFile (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.IOUtils.TFile)
 - [System.Classes.TFileStream (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TFileStream)
 - [System.Classes.TBufferedFileStream (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TBufferedFileStream)
 
### ■ ファイルへの書き出しタイミング

小さなデータの書き込みが大量に行われる場合には、`AutoFlush` プロパティを False に設定し、任意のタイミングで `Flush()` を使ってファイルに書き出す方がパフォーマンスの向上につながります。

```pascal
uses
  ..., Classes;

var
  Writer: TStreamWriter;
begin
  Writer := TStreamWriter.Create('hello.txt'); // 上書きモード
  try
    Writer.AutoFlush := False; // 自動フラッシュしない

    // 処理

    Writer.Flush;              // ファイルに書き出し
  finally
    Writer.Free;               // デストラクタにより Close() が呼び出され、
  end;                         // Flush() が実行される
end;
```

:::note
AutoFlush = False の時、一度も Flush() を呼び出さなかったとしても、デストラクタで Close() が呼ばれ暗黙的に Flush() が実行されます。
:::

扱うファイルにもよりますが、AutoFlush = True の場合でも、BufferSize を大きくするとパフォーマンスが向上する事があります。但し、コンストラクタにパラメータを 4 つ指定するため、UTF-8 の場合には BOM に注意する必要があります。 

```pascal
  // 上書きモード, UTF-8 (BOM あり UTF-8), バッファ 8 倍
  Writer := TStreamWriter.Create('hello.txt', False, TEncoding.UTF8, 32768);
  try
    Writer.WriteLine('Hello,');
    Writer.WriteLine('world.');
  finally
    Writer.Free;
  end;
```

ログのようなものは自動フラッシュ (AutoFlush = True) で都度書き出した方がいいとは思いますが...。

### ■ TStreamReader / TStreamWriter を標準入出力に割り当てる (Windows)

次のコードでコンソールアプリケーションで TStreamReader / TStreamWriter を標準入出力に割り当てる事ができます。

```pascal
uses
  ..., Classes, Windows;

var
  Reader: TStreamReader;
  Writer: TStreamWriter;
begin
  Reader := TStreamReader.Create(THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE)));
  Writer := TStreamWriter.Create(THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE)));
  try
    Writer.WriteLine('Hello,world.');
    Reader.ReadLine;
  finally
    Reader.BaseStream.Free;
    Reader.Free;
    Writer.BaseStream.Free;
    Writer.Free;
  end;
end;
```

### ■ TStreamWriter の Write() / WriteLine()

TStreamWriter の Write() / WriteLine() には多くのオーバーライドされたメソッドが存在するため、文字列へと変換する機会はそうそうないかと思います。

```pascal
  // Write()
  procedure Write(Value: Boolean); override;
  procedure Write(Value: Char); override;
  procedure Write(const Value: TCharArray); override;
  procedure Write(Value: Double); override;
  procedure Write(Value: Integer); override;
  procedure Write(Value: Int64); override;
  procedure Write(Value: TObject); override;
  procedure Write(Value: Single); override;
  procedure Write(const Value: string); override;
  procedure Write(Value: Cardinal); override;
  procedure Write(Value: UInt64); override;
  procedure Write(Value: TCharArray; Index, Count: Integer); override;
  // WriteLine()
  procedure WriteLine; override;
  procedure WriteLine(Value: Boolean); override;
  procedure WriteLine(Value: Char); override;
  procedure WriteLine(const Value: TCharArray); override;
  procedure WriteLine(Value: Double); override;
  procedure WriteLine(Value: Integer); override;
  procedure WriteLine(Value: Int64); override;
  procedure WriteLine(Value: TObject); override;
  procedure WriteLine(Value: Single); override;
  procedure WriteLine(const Value: string); override;
  procedure WriteLine(Value: Cardinal); override;
  procedure WriteLine(Value: UInt64); override;
  procedure WriteLine(Value: TCharArray; Index, Count: Integer); override;
```

#### 書式付き Write() / WriteLine()

`Format()` と同じ書式文字列とオープン配列コンストラクタをパラメータとして渡せる Write() / WriteLine() メソッドが用意されています。

```pascal
  // Write()
  procedure Write(const Format: string; Args: array of const); override;
  // WriteLine()
  procedure WriteLine(const Format: string; Args: array of const); override;
```

標準手続きの Write() / Writeln() の可変パラメータと似たような記述が可能となっています。

```pascal
  // Write()
  Writer.Write('%s_%.3d.txt', ['LOG', rev]);
  // WriteLine()
  Writer.WriteLine('%.4d: %s', [Id, Name]);
```

**See also:**

 - [System.SysUtils.Format() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.Format)
 - [(11.1.7.) オープン配列コンストラクタ (Open Array Constructors) (Qiita)](https://qiita.com/ht_deko/items/b93ac03bfee002f17137#1117-%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3%E9%85%8D%E5%88%97%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF-open-array-constructors)

### ■ TStrem.Seek() によるストリームポインタの移動

関連しますが、TStrem.Seek() の 2 番目のパラメータ (Origin) には TSeekOrigin 列挙型を指定して、巨大なファイルでも問題なく移動できるようにすべきです。

具体的には `soBeginning` などではなく、`TSeekOrigin.soBeginning` のように明示します。

```pascal
  Reader.BaseStream.Seek(FileSize, soBeginning); // NG
  Reader.BaseStream.Seek(FileSize, TSeekOrigin.soBeginning); // OK 
```

`TSeekOrigin.` で修飾すると**常に Int64 の方の Seek() が選択される**からです。

```pascal
  function Seek(Offset: Longint; Origin: Word): Longint; overload; virtual;
  function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; virtual;
```

**See also:**

 - [TStream.Seek() (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TStream.Seek)
 - [TSeekOrigin (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.Classes.TSeekOrigin)

# おわりに

TStringList が便利だからと、**なんでもかんでも TStringList でやっているとパフォーマンスの低下につながる**事があります。

ちょっとクセがあったりもしますが、テキストファイルの読み書きを TStreamReader / TStreamWriter に置き換えて高速化が行えないか検討してみてはいかがでしょうか？

:::note
TStreamReader / TStreamWriter の使い方は書籍『OBJECT PASCAL HANDBOOK』でも触れられています。

 - 「18.2 最新のファイルアクセス」,『[OBJECT PASCAL HANDBOOK Delphi 11 Alexandria のための Object Pascal プログラミング完全ガイド](https://www.amazon.co.jp/dp/4877835210)』, pp.599-607
 - 「18.2 最新のファイルアクセス」,『[OBJECT PASCAL HANDBOOK マルチデバイス開発ツール Delphi のためのプログラミング言語完全ガイド](https://www.amazon.co.jp/dp/4877835210)』, pp.541-548
:::

## 余談

本記事のコードは可能な限り古いバージョンでも通るように記述しましたが、Delphi 10.3 以降のインライン変数宣言と型推論を使えばもっと簡潔に書けます。

> 最も簡単な使い方は次のようになります

まぁ、**with** 文を使えばもっと簡単になりますけれど。

```pascal:TStreamWriter
  with TStreamWriter.Create('hello.txt') do
  try
    WriteLine('Hello,');
    WriteLine('world.');
  finally
    Free;
  end;
```

```pascal:TStreamReader
  with TStreamReader.Create('hello.txt') do
  try
    while not EndOfStream do
    begin
      var LineStr := ReadLine;
      ...
    end;
  finally
    Free;
  end;
```

**See also:**

 - [標準ルーチンと入出力 (Qiita)](https://docwiki.embarcadero.com/RADStudio/ja/%E6%A8%99%E6%BA%96%E3%83%AB%E3%83%BC%E3%83%81%E3%83%B3%E3%81%A8%E5%85%A5%E5%87%BA%E5%8A%9B)
 - [<12> テキストファイルの入出力 (標準 Pascal 範囲内での Delphi 入門) (Qiita)](https://qiita.com/ht_deko/items/391d6c1a53c7aaa3416f)
 - [Delphi 10.3 Rio で CSV を処理する (Qiita)](https://qiita.com/ht_deko/items/2802677fe1cfaa4d1332)
 - [【Delphi】文字列とファイルと複数行文字列 [小ネタ] (Qiita)](https://qiita.com/ht_deko/items/8a65920ab87e7f65b971)

[^1]: Delphi の古いバージョンではバッファサイズが 1024 でした。
[^2]: `Rewind()` は Delphi 10.3 Rio で実装されました。
[^3]: `IOUtils` や `TFile` は Delphi 2010 で実装されました。
[^4]: `OwnStream()` は Delphi XE で実装されました。
[^5]: 元々 FireDAC 由来 ([FireDAC.Stan.Util.TFDFileStream](https://docwiki.embarcadero.com/Libraries/Seattle/ja/FireDAC.Stan.Util.TFDFileStream)) だったものが 10.1 Berlin で `System.Classes` へと移動されました。