【UiPath】巨大なCSVファイルをどうやって読み込むか

# はじめに

- 本記事では、UiPath で巨大なCSVファイルをどう読み込むかについての筆者の実験・考察を扱います。
- 記事の内容は、個人の見解または確認結果であり、UiPath の公式見解ではありません。
- 製品仕様や参考画像は 23.10 バージョンのもので構成しています。

# 巨大なCSVを読み込むとどうなる？

32bit のアプリケーションでは一般的に問題なく動作するメモリの上限は2GBちょっとです。64bit だと4～5GB。
このため、巨大なCSVファイルを一気に読み込んだりすると
***メモリ不足でシステムエラーが発生します。***

![巨大なCSV読み込み15-0_レガシ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/222a90e1-1cc4-072e-82c4-e8a7d2e380fc.jpeg)

## どのくらいのボリュームで落ちるの？

A～AB（28）列で、平均10文字程度の半角英数字が入力されている前提で
200万レコード、ファイルサイズで550MB程度で落ちます。
500MB以上のCSVを読み込ませながらタスクマネジャーをみていただくとよいのですが、
私の手元では2900MBくらいまではロボットくんも頑張れました。

![巨大なCSV読み込み14-1_レガシ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/7531e39f-0d9c-dd08-abb8-f4c57c46c890.jpeg)

## 参考：メモリの消費量

|レコード数| メモリの消費量|
|:---:|:---:|
| 40万 | 1000MB |
| 120万 | 2200MB |
| 140万 | 2500MB |
| 180万 | 2900MB |

# どうやってメモリ不足のエラーを回避すればいい
## 方法1：プロセスを分離してみる

![巨大なCSV読み込み16.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/53743352-a45e-8d8e-62aa-4196abbcefab.jpeg)

メモリ消費の大きい箇所があれば、そこをサブワークフローで切り出し、
***「ワークフロー呼び出し」の分離オプションをONにして実行する方法。***

![巨大なCSV読み込み16-1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/74c4c549-61a6-50a1-85fd-d662de9ef522.jpeg)

こちらは***ロボット全体でメモリ消費が大きい場合に有効***ですが、そもそも巨大なCSVの読み込みにおいては効果が限定的となります。

## 方法2：「Windows」プロジェクトにコンバートする

https://docs.uipath.com/ja/studio/standalone/2023.4/user-guide/about-the-windows-legacy-compatibility

v21.10 以降、新しいランタイムプロジェクト（NET6のプロジェクト）として「Windows」プロジェクトが登場し、従来のプロジェクトが「Windows - レガシ」プロジェクトとなりました。
「Windows」プロジェクトは 64bit のランタイムとなるため、上でエラーとなっていた200万レコードのCSVも問題なく処理ができます。

![巨大なCSV読み込み17-1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/15a173b6-3041-bf4d-cdd9-9c8487330c06.jpeg)

標準パッケージのみで構成されているワークフローの場合、Windowsプロジェクトへのコンバートは比較的容易なため、メモリを多く消費するロボットは移行を検討いただいてもよいかもしれません。
ただし、この場合も***5GB程度がメモリの上限となる***ため、それ以上消費するファイルに対しては有効な方法ではありません。

## 方法3：ストリームリーダーで1行ずつ処理する

![ストリームリーダー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/93d00067-d4bd-8e26-3e3d-e6160a7ae7d1.jpeg)
![ストリームリーダー2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/7fc3f8a2-1360-708f-4357-fcc5603b07ad.jpeg)

```
# ストリームリーダーの使い方の補足
# ストリームリーダーの生成
ストリームリーダー = New StreamReader(New FileStream(ログファイルパス, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))

# ストリームライターの生成
ストリームライター = New System.IO.StreamWriter("中間ファイル.csv",True)

# 繰り返しの中で1行読み込む
レコード = ストリームリーダー.ReadLine

# メソッド呼び出し
ストリームライター.Write(レコード)
```

1行ずつレコードを読み込むので、メモリあふれの心配がなくなるのと、実装も容易なので、解決手段として採用したくなりますが、200万レコードでおこなうと***ものすごく時間がかかってしまいます。。。***
手元の動作をみるに1時間でも終わらなそう？（最終アウトプットの半分のサイズを出力するのに30分かかっている）

## 方法4：CSVファイルの分割・読み込み

![ファイル分割.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/bfa59b03-d867-e998-43f7-7c0035d686f5.jpeg)
![ファイル分割2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/9da6fc7e-4248-5edb-9403-827d8971fe1a.jpeg)
![ファイル分割3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/5f3dae09-763b-7b80-f1f5-12b272c85ac9.jpeg)

```
# PowerShellスクリプトでファイルを分割
"$i=1; Get-Content HugeCsvSample.csv -ReadCount 200000 | % { Out-File HugeCsvSample_$i.csv -InputObject $_; $i++ }"
```
::: note
参考：フィルターの設定
![ファイル分割4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/c3740d37-8bce-afeb-bfb5-6ce41732e809.jpeg)
:::

これだけで、***先のストリームリーダーと同じアウトプットを2分程度で得られるようになります。***
が、しかしファイルを分割するため一括で検索できないのが難点。

## 方法5：File クラスで読み込む

![Fileで読み込み.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/dc9c570e-d399-6aa4-b16d-45caa7302a6a.jpeg)

```
# File クラスで読み込み
lines = File.ReadLines("HugeCsvSample.csv").where(Function(line) line.Contains("131"))

# メソッド呼び出し
System.IO.File.WriteAllLines("中間ファイル.csv",lines)
```
![Fileで読み込み2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/978a8fc5-67ec-c959-b1cc-3ce4f35d0481.jpeg)

***10秒です！！！実装の容易性と速さで秀でており、Where句の条件に関係なくパフォーマンスも出ます！***

# さいごに

いかがでしたでしょうか。
筆者の主観で利用条件を挙げるなら
- たまに大きいファイルを読むことがあるが、100～300MB程度である　→　方法1（ワークフロー呼び出しの分離オプションを使ってみる）
- 恒常的に巨大ファイルが出現し、内容もまちまち　→　方法2（「Windows」プロジェクトにコンバート）
- 巨大ファイルの読込部分のソース改修可能　→　方法5（File クラスで読み込む）
です！

巨大なデータファイルの読み込みでメモリ不足に遭遇した方の少しでもお役に立てば幸いです。
最後までお読みいただきありがとうございます(・ω・)ノ