Swiftのスタンダードライブラリに、テキストのストリーム処理をする機能がほしいです。 テキストのストリーム処理というのは、例えば、 CSVファイルを全てメモリに読み込まずに、 少しずつファイルから読み込みながら1レコードずつパースするようなことです。
実際に私は仕事でそのようなプログラムをよく書いています。 映像を解析した時系列の座標データなどを、 その時系列に基づいて再生する処理などで使います。 データ量が多く、データが時間的に長いので、 全てメモリに読み込まずに、少しずつ処理しています。
このような処理をする場合、以下のような機能単位が必要です。 簡単のためにUTF-8だけ考えます。
- ファイルを少しずつバイト単位で読み込む
- バイトストリームをUTF-8として少しずつデコードする。 これによって、バイトストリームは、 Unicode Codepointのストリームになります。
- Unicode Codepointのストリームを、 書記素クラスタとして少しずつデコードする。 これはSwiftのCharacter型のことです。
また、テキストの形式によっては、先読みや巻き戻した必要なので、 これらのステップで位置情報とシーク機能が必要です。
これらを実装するのが、現状の標準ライブラリでは困難です。 一つずつ説明します。
(1) これについては、Foundationに近いものがあります。 NSInputStreamとNSFileHandleです。
InputStream(Swift)/NSInputStreamについてですが、 このクラスは無駄に機能がややこしくて、 使いづらいし理解するのが難しいです。
https://developer.apple.com/documentation/foundation/inputstream
RunLoopと連携して動作する機能などと統合されていますが、 そのあたりの制御はDispatchQueueで自前で管理したいです。
シーク機能が無いため、 パースにおいて先読みが必要なテキストの処理で使えません。
FileHandle(Swift)/NSFileHandleについてですが、 エラーハンドリングがObjectiveCの例外になっているので、 Swiftから制御できないため全く使えません。
https://developer.apple.com/documentation/foundation/filehandle/1413916-readdata
(2) これについては、標準ライブラリに近いものがあります。 Unicode.UTF8.decodeです。
https://developer.apple.com/documentation/swift/unicode/utf8/2907346-decode
しかし、これはポジションについての情報を返してくれません。 正しいポジションがわからないと、 後段の処理でUnicode Codepoint単位でシークしたい場合に、 それがバイト単位でどの場所だったのかがわからないため、 実装できません。
(3) これについては、頑張ればStringで実装できます。
StringにUnicodeScalarをappendしていき、
.characters
プロパティが2文字以上返す場合について、
先頭の1文字を切り出すという処理を書きます。
最後にストリームの終わりが来た時には、
残っている.characters
を読み出します。
このように、一応実現はできるのですが、
実装するのは難しいと思うし、
このアルゴリズムを思いつくのも簡単なことではないと思います。
それに、一応実現ができるというだけで、
おそらくStringの内部処理との兼ね合いで、
パフォーマンス的に非効率な気がしています。
このような状況であっても、 私はちゃんとしたテキスト処理を実装したかったので、 必要なものを自作しました。
https://github.com/omochi/StringStream/tree/master/Sources/StringStream
(1)についてはCのfopen
ファミリーをラップしました。
https://github.com/omochi/StringStream/blob/master/Sources/StringStream/FileHandle.swift
(2)については、UTF8のデコーダを自作しました。
https://github.com/omochi/StringStream/blob/master/Sources/StringStream/UTF8Reader.swift
(3)については、上述した処理を実装しました。
https://github.com/omochi/StringStream/blob/master/Sources/StringStream/CharacterReader.swift
自作したことでとりあえず業務上は問題ないのですが、 このようなテキストのストリーム処理は珍しいものではないと思うので、 標準ライブラリにあっても良いと思います。
みなさんはどう思いますか?