|
■No88236 (ユウタ さん) に返信 > エクセルから取得したRangeに対して、 > forを使って各要素を1つ1つ1次配列に格納することは可能なのですが、 > やや時間がかかるため、ここも短縮したいという趣旨でした。
速度面が問題になるなら、COM Interop な Excel オートメーションを用いるのではなく、 ExcelDataReader や EPPlus を使う方法に変更してみるとか。
ひとまず Excel オートメーションのままで実装するとした場合、 先のように .Value で複数範囲を一括で読み取るようにすることで、 アウトプロセスである Excel との通信回数が、その 1 回だけで済むようになります。
そのため、繰り返し読みだす方法に比べれば大幅な高速化になります。
これが C# ではなく、Excel VBA 内での読み取りであったのなら、 繰り返し読み取るようにしても、そこまで遅くはならないのですけれども(インプロセスなので)。
とはいえ一括読み取りの際に、対象セル数があまりに膨大になる場合は 転送効率が急激に落ちることがあります。 その場合は複数回のブロックに分けて読み取るようにします。 (5000セル程度ならば通常は問題無いはず)
> また、なぜ1次配列かというと、 > 配列の各要素の値をforを使って取得する場合、 > 2次配列で取得するのと1次配列で取得するのでは、 > 1次配列の方が圧倒的に速いためです。
1 次元化せずにそのまま 2 次元のまま扱うと、どの程度のタイムロスになるのですか? あるいは、2 次元配列から 1 次元配列への詰め替える場合、どの程度がかかるのでしょうか?
1 次元配列は、IL レベルで専用命令(ldelem.*)が揃っていることもあり、 確かに高速ではありますが、今回、そこまで“圧倒的”な差が付くのでしょうか。
実際に測定したわけではないですが、個人的な経験から言えば、 「Value で 2 次元配列にまとめて読み取る方法」と 「1 セルずつ for で読み取る方法」との処理時間差に比べれば、 『1 次元配列へのアクセス時間』と『2 次元配列へのアクセス時間』の時間差は 相対的には小さいものであると思っているのですが…。
> やはりforで配列に格納していく方法になるのですね。
5000行×1列や 1行×5000列 といったパターンなら、 for よりも foreach の方が楽ですね。
> 一発で変換する命令があればよいのですが…。
.Cast<>.ToArray() するという手はあります。 詰め替えているという点では同じですが。
// [0..3, 0..0] な object 型の 2 次元配列を object[,] obj2dArray = { { "AB" }, { 12.0 }, { null }, { DateTime.UtcNow } };
// [0..3] な string 型の 1 次元配列に変換 string[] str1dArray = obj2dArray.Cast<object>().Select(o => o != null ? o.ToString() : null).ToArray();
// [1..5000, 1..1] な object 型の 2 次元配列を obj2dArray = (object[,])Array.CreateInstance(typeof(object), new int[] { 5000, 1 }, new int[] { 1, 1 }); obj2dArray[1, 1] = "AB"; obj2dArray[2, 1] = 12.0; obj2dArray[3, 1] = null; obj2dArray[4, 1] = DateTime.UtcNow;
// [0..4999] な string 型の 1 次元配列に変換 str1dArray = obj2dArray.Cast<object>().Select(o => o != null ? o.ToString() : null).ToArray();
> >WebSurferさん > ADO.NET + ACEプロバイダ > 初めて聞きましたので、調べてみます。
この方法だと、Excel が入っていない環境でも読み書きできるというメリットがあります。 幾つかの制限はあるものの、要件次第では Excel オートメーションより高速な場合さえありますね。
ただし取得される結果は DataTable もしくは DataReader になりますので、 最終的に 1 次元配列であることを望むのであれば、 やはり LINQ なり for/foreach なりでの詰め替えは必要ですね。
あるいは ADO.NET + ACE プロバイダの代わりに、 ADO + ACE プロバイダの組み合わせにすれば、 ADODB.Recordset に受け取り、 // object[,] array2D = recordset.GetRows(); string[] strArray1D = recordset.GetString(ADODB.StringFormatEnum.adClipString, 5000, "\t", "\r\n").Split(new string[] { "\r\n" }, StringSplitOptions.None); のようにするという手が使えるかもしれません。
|