🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

4回答

11182閲覧

C#で多量データを持ったCSVファイルを読み込みソートする

FM88mhmh

総合スコア22

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

2グッド

0クリップ

投稿2019/11/14 05:17

編集2019/11/14 06:15

Visual Studio 2017を使っています。
CSVファイルを読み込んでソートを行ってDataGridView(DGV)に表示するということをしたいのですが、
CSVファイルには約10万件のデータがあります。

処理時間を短縮したいですが何か良い方法はありますでしょうか?

実行した方法二つを載せておきます。
よろしくお願いいたします。

方法1 方法2の4倍の時間がかかりました。
1.csvファイルを読み込む
2.シリアルNo.の重複の確認を行う
3.DGVに表示
2.と3.を繰り返す
4.並べ替え

C#

1void InportcsvToDGV() 2 { 3 int iCount = 0; 4 5 string csvLine; 6 string[] csvData; 7 string[] DGVData; 8 DGVtable.ColumnCount = 10; 9 10 // read csv file 11 if (File.Exists(Inportcsvpath)) 12 { 13 DGVtable.Rows.Clear(); 14 15 StreamReader csvFile = new StreamReader(Inportcsvpath, Encoding.GetEncoding("UTF-8")); 16 do 17 { 18 bool overlap = false; 19 20 // read csv file 21 csvLine = csvFile.ReadLine(); 22 csvData = csvLine.Split(','); 23 24 DGVData = new string[10] 25 { 26 "name", 27 csvData[0], csvData[1], csvData[2], 28 csvData[3], csvData[4], csvData[7], 29 csvData[8], csvData[9], csvData[10] 30 }; 31 32 if (iCount != 0) 33 { 34 for (int i = 0; i < DGVtable.RowCount-1; i++) 35 { 36 if (DGVtable.Rows[i].Cells[4].Value.ToString() == DGVData[4]) 37 overlap = true; 38 } 39 } 40 // display 41 if (overlap == false) 42 DGVtable.Rows.Add(DGVData); 43 44 iCount++; 45 Txtcount.Refresh(); 46 47 } while (csvFile.Peek() != -1); 48 csvFile.Close(); 49 DGVtable.Sort(DGVtable.Columns[4], ListSortDirection.Ascending); 50 } 51 }

方法2
1.csvファイルを読み込む
2.DataTableを作成
3.DataTable作成中に重複削除
4.DGVに表示
5.並べ替え

C#

1void InportcsvToDT() 2 { 3 string csvLine; 4 string[] csvData; 5 6 DataSet Dset = new DataSet(); 7 DataTable table = new DataTable("Table"); 8 9 // read csv file 10 if (File.Exists(Inportcsvpath)) 11 { 12 table.Rows.Clear(); 13 StreamReader csvFile = new StreamReader(Inportcsvpath, Encoding.GetEncoding("UTF-8")); 14 do 15 { 16 // read csv file 17 csvLine = csvFile.ReadLine(); 18 csvData = csvLine.Split(','); 19 20 try 21 { 22 table.Rows.Add( 23 "name", 24 csvData[0], csvData[1], csvData[2], 25 csvData[3], csvData[4], csvData[7], 26 csvData[8], csvData[9], csvData[10] 27 ); 28 } 29 catch (ConstraintException) 30 { 31 errCounter++; 32 TxtLog.Text += csvData[4] + " is already produced.....\r\n"; 33 } 34 } while (csvFile.Peek() != -1); 35 csvFile.Close(); 36 } 37 DGVtable.DataSource = table; 38 DGVtable.Sort(DGVtable.Columns[4], ListSortDirection.Ascending); 39 40 TxtErrCounter.Text = errCounter.ToString(); 41 errCounter = 0; 42 }
hihijiji, dodox86👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/11/14 05:41

質問は何ですか? 方法1は方法2の 4 倍の時間がかかるので問題外であれば、方法2を採用ということで良いのではないですか? 方法2より良い方法がないかというのが質問ですか? ボトルネック(時間がかかる処理)はどこか切り分けることはできているのでしょうか? まだならそれをやってもらえませんか? 実環境でそれができるのは質問者さんしかいませんので。
YAmaGNZ

2019/11/14 05:44

提示されている2つの処理が等価ではありませんがいいのですか?
退会済みユーザー

退会済みユーザー

2019/11/14 05:47

本題とは違う話で何ですが・・・ ファイルを一行ずつ読んで文字列を作り、String.Split メソッドでその文字列を区切るといった方法を取っているようですが、それで問題ないのですか? 改行コードやデリミタがフィールド値の中にあったり、改行コードが異なったりするのに対応する場合、そのような単純な方法は使えません。その場合どのように対応しするかは以下の記事を見てください。 CSV形式のファイルをDataTableや配列等として取得する http://dobon.net/vb/dotnet/file/readcsvfile.html
Zuishin

2019/11/14 06:21

例外で重複削除はいただけないです。
FM88mhmh

2019/11/14 06:22

方法2よりも早く処理が終わるものがあれば教えていただきたいです。 ボトルネックは重複の確認です。 方法1で重複の確認を行わなければ1/10以下の時間で終わりました。 方法2でも同じ感じです。 方法2のほうで現在は進めていて更新部分が方法1に反映されていない部分があります。 申し訳ありません。
hihijiji

2019/11/14 07:05

DataGridView の DataSource に全件放り込んでるみたいですが、 画面が固まったりしませんか?
FM88mhmh

2019/11/14 07:14

DataGridViewに反映する部分はあまり処理に時間はかかっていいないと思います。 1秒もかからないうちに反映できています。
hihijiji

2019/11/14 07:31

ありがとうございます。 WPF の DataGrid で同じようにやろうとすると確実に固まるので気になったのですが、 Windows.Formsはそのあたり高速ですね。
FM88mhmh

2019/11/14 07:37

10000件のデータ (各10~20文字のstring型を10項目保有)で方法2でやったところ 総時間           約2.4秒 CSV読み込みと重複確認   約2.3秒 tableをDGVへ入れる     約0.6秒 ソート           約0.4秒 でした。
workaholist

2019/11/14 08:04

つまりこの質問にDataGridViewは無関係ということですか? DataTableにループで行追加すると時間がかかるがなぜか?という質問が本質でしょうか?
FM88mhmh

2019/11/14 08:11

そうですね。 最初は、何を使えば早くなるのかわからなく、もしかしたら使いこないしていない機能を使えばできるのかもしれないと思い、現状と使っているものを全て書きました。
guest

回答4

0

重複があるたびに例外を送出しているのが遅い原因だと思います。csvData[4] をキーに HashSet や Dictionary を使い、重複を取り除いてからテーブルに入れてください。

HashSet<T> クラス (System.Collections.Generic) | Microsoft Docs

投稿2019/11/14 06:29

Zuishin

総合スコア28669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FM88mhmh

2019/11/14 08:19

ほぼ同じ内容でプログラム記述を載せていただいていたのでもう一つのほうをベストアンサーにさせていただきます。 ご回答ありがとうございます。
guest

0

ソートしてからDataGridViewにバインドしてみてはどうでしょうか。

投稿2019/11/14 05:37

workaholist

総合スコア559

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2019/11/14 06:18 編集

バインドしてからだとコントロールの処理がソート時に入るのでその順番でしょうね。
FM88mhmh

2019/11/14 08:24

dataTableでソートをかけるということでしょうか? 色々調べていたのですが、dataviewを経由しての方法ばかり出てきて dataTable単体では難しそうだったので断念してしまいました。。。。
guest

0

すでに解決済みですし、質問者さんのケースで可能かどうか、早くなるのかどうかは分かりませんが・・・

データソースが SQL Server などの DB であれば、SELECT クエリに DISTINCT と ORDER BY を組み合わせて重複排除とソートを行ったうえで DataTable を作ると思います。

データソースが CSV の場合でも JET(または ACE)と ADO.NET ライブラリを使って SELECT クエリから DataTable を作ることができます。

具体例は以下の記事のサンプルコードの「// Jet Provider を使う場合」とコメントした部分を見てください。

CSV パーサー
http://surferonwww.info/BlogEngine/post/2010/10/28/CSV-parser.aspx

選択肢の一つに加えて検討してみてはいかがでしょう。

【追記】

忘れていましたが上に紹介した記事よりもっといい具体例を思い出しましたので紹介します。同じ Teratail の以下のスレッドの私 SurferOnWww の回答を見てください。

取込むCSVのフォーマットチェック
https://teratail.com/questions/197089

CSV ファイルは、Microsoft のサンプルデータベース Northwind の Products テーブルから SSMS を使ってエクスポートしたものを使います。上に紹介したスレッドの私の回答の画像を見てください。

それに、以下の画像のとおり重複するレコードを追加しておきます。

イメージ説明

上に紹介したスレッドの私の回答のコードの SELECT クエリに DISTINCT を追加して重複排除し、ORDER BY [UnitPrice] を追加して UnitPrice 順に並べ替えてみます。JET は ACE に代えてみました。以下の通りです。

private void Form12_Load(object sender, EventArgs e) { //接続文字列。HDR = Yes で一行目をヘッダーとして扱う // 2019/11/15 Teratail のスレッド https://teratail.com/questions/223119 の検証用に // JET を ACE に変更し、csv ファイルに重複項目を追加し、SELECT クエリに DISTINCT と ORDER BY を追加 string conString = //"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + this.path + ";Extended Properties=\"text;HDR=Yes;FMT=Delimited\""; OleDbConnection con = new OleDbConnection(conString); string commText = "SELECT DISTINCT [ProductID],[ProductName],[UnitPrice],[Discontinued] FROM [" + this.filename + "] ORDER BY [UnitPrice]"; OleDbDataAdapter da = new OleDbDataAdapter(commText, con); DataTable dt = new DataTable(); da.Fill(dt); this.bindingSource1.DataSource = dt; }

結果は以下の通りで、重複は排除され、UnitPrice 順に並べ替えられています。

イメージ説明

投稿2019/11/14 08:34

編集2019/11/15 02:15
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FM88mhmh

2019/11/14 09:30

SQLiteを使う可能性はあったのですが、今回は見送られてしまったので、、、 もし機会があれば比較してみたいと思います!! ご提案ありがとうございます! 二つ目のほうは初めて見たので、また勉強させていただきます。
退会済みユーザー

退会済みユーザー

2019/11/15 01:00

マイナス評価をした人、理由を書こう
退会済みユーザー

退会済みユーザー

2019/11/15 01:33

自分が分からないからという理由でマイナス評価を付ける人がいるようですので、そういう人にもわかるように回答欄に具体例を書いておきます。
guest

0

ベストアンサー

キー項目をHashSet<T>で持って存在チェックを行ってはどうですか?

C#

1string[] lines= File.ReadAllLines(Inportcsvpath, Encoding.GetEncoding("UTF-8")); 2HashSet<string> hash = new HashSet<string>(); 3 4foreach (string line in lines) 5{ 6 string[] datas = line.Split(','); 7 8 if(hash.Contains(datas[3])==false) 9 { 10 hash.Add(datas[3]); 11 //テーブルに追加 12 } 13}

みたいな感じで

投稿2019/11/14 06:30

編集2019/11/14 06:31
YAmaGNZ

総合スコア10469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FM88mhmh

2019/11/14 08:17

時間がかかってしまってすみません。 YAmaGNZ さんの記述を参考にやったところ、 table の Primary key を消して Hashset を追加すると 処理時間が半分になりました。 ありがとうございます!! あまり関係はありませんが、csvの読み込みを1行ずつと全行をやってみたのですが、 ほぼ時間は変わりませんでした。 10000件のデータを3回ずつ回したのですが、誤差が0.05秒以内で約2.4秒でした。 早くなると思っていたので驚きました。 ありがとうございます。
dodox86

2019/11/14 08:34

実用に即した興味深い質問で、私も勉強させてもらいました。ありがとうございます。 > csvの読み込みを1行ずつと全行をやってみたのですが、ほぼ時間は変わりませんでした。 1行ごとの読み込みでもバッファリングされたものから取り出されているはずなので、あらかじめ大きなサイズで一挙に読み込み済みなのでしょうね。
FM88mhmh

2019/11/14 09:42

時間がかからなかったのはそういう仕組みだったんですね! 10万件のデータでやっても変わらなかったのでその通りなのだと思います。 本題の最終結果なのですが、 HashSet で重複削除を行い、 dataTable を使わず直接 dataGridView に入れる方法にすると 10万件が40秒で終わりました。 10万件時間比較 ・重複削除(入れたデータと全比較) -> DataGridviewに表記 -> 並べ替え 21分07秒 ・Datatable作成 -> 重複削除(Primary key) -> DataGridViewに表記 -> 並べ替え 4分40秒 ・Datatable作成 -> 重複削除(HashSet) -> DataGridViewに表記 -> 並べ替え   3分38秒 ・重複削除(HashSet) -> DataGridviewに表記 -> 並べ替え 0分39秒 ・DataGridviewに表記 -> 並べ替え 0分40秒 なせか重複削除したほうが早くなりました。。。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問