Skip to content

Instantly share code, notes, and snippets.

@omochi
Created January 8, 2019 05:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omochi/6f4eb2d157fab309cae4eff5a473e578 to your computer and use it in GitHub Desktop.
Save omochi/6f4eb2d157fab309cae4eff5a473e578 to your computer and use it in GitHub Desktop.

Swiftのスタンダードライブラリに、テキストのストリーム処理をする機能がほしいです。 テキストのストリーム処理というのは、例えば、 CSVファイルを全てメモリに読み込まずに、 少しずつファイルから読み込みながら1レコードずつパースするようなことです。

実際に私は仕事でそのようなプログラムをよく書いています。 映像を解析した時系列の座標データなどを、 その時系列に基づいて再生する処理などで使います。 データ量が多く、データが時間的に長いので、 全てメモリに読み込まずに、少しずつ処理しています。

このような処理をする場合、以下のような機能単位が必要です。 簡単のためにUTF-8だけ考えます。

  1. ファイルを少しずつバイト単位で読み込む
  2. バイトストリームをUTF-8として少しずつデコードする。 これによって、バイトストリームは、 Unicode Codepointのストリームになります。
  3. 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

自作したことでとりあえず業務上は問題ないのですが、 このようなテキストのストリーム処理は珍しいものではないと思うので、 標準ライブラリにあっても良いと思います。

みなさんはどう思いますか?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment