質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

Q&A

解決済

3回答

23446閲覧

複数テキストファイルを高速読み取り

Mitsuhiko.S

総合スコア15

C#

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

0グッド

0クリップ

投稿2015/05/05 13:57

編集2015/05/05 13:58

現在ある階層にある全てのテキストファイルの中身を読み取るコードを書いています。
その階層にはテキストファイルしかなく、そのファイルパスは全て取得できているとします。

現状では、StreamReaderクラスのReadLineを用いてファイルを読み取り、それをループさせて複数ファイルを読み取っています。
ただこの方法だとどうしても速度的に遅くなってしまいます。一つ一つのファイル自体は大した容量ではありません(最大でも200KB程度)が、ファイル数が100個近くあります。

そこで、もし高速で複数のテキストファイルを読み取る方法があれば教えて頂けませんか。

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

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

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

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

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

guest

回答3

0

こんにちは。

ippeiさんの回答に

SSDであれば並列化すればした分だけ早くなると思います。

とあったので、

並列

処理する方法について、
(試してないので、just an ideaで恐縮なのですが、)
以下のような方法を思いつきました。

(1)対象の約100個のテキストファイルの名前を

001.txt, 002.txt .. 099.txt, 100.txt, ... 1XX.txt

というように番号をつける。

番号でなくても、何らかの方法で、対象の約100個のファイルを
均等な個数のグループに分けて、そのグループに対して
以下の(2)のような起動の仕方ができればOKです。

(2)C#で開発するファイル処理プログラムの実行ファイルを仮に

readfiles.exe

とし、以下のようなコマンドライン引数を取れるようにする。

readfiles [開始ファイル番号] [終了ファイル番号]

例)readfiles 021 030 - - - 021.txt から030.txtまでを処理する。

(3)以下のような内容のバッチファイルを作成して実行

lang

1@echo off 2start readfiles 001 020 3start readfiles 021 040 4start readfiles 041 060 5start readfiles 061 080 6start readfiles 081 100 7start readfiles 101 1XX

このreadfiles自体の最適化は、htsignさんご回答の方法を使うなどで
もちろん行うとして、上記のようなバッチで20個のファイルを1個ずつ
処理していくコマンドを5個と、XX個のファイルを処理するコマンド1個
の計6個のコマンドをバックグラウンドで走らせることで高速化を図ります。
(もちろん、10個のファイルを処理するコマンド10+α個並行に、でも
よいのですが)

参考になれば幸いです。

投稿2015/05/05 16:37

編集2015/05/05 16:59
jun68ykt

総合スコア9058

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

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

Mitsuhiko.S

2015/05/05 22:41

ベストアンサーでだいぶ悩んだのですが、何分まだ僕がコマンドラインをあまり扱ったことがなかったので、今回はhtsignさんをベストアンサーにいたしました。これからykt68さんに教えていただいた方法も勉強して実装してみようと思います。ありがとうございました!
guest

0

ベストアンサー

実装にもよるかと思いますが、System.IO.FileクラスのReadAllTextメソッドで一気に読み取る方が速かったです。

lang

1string[] files = Directory.GetFiles(Path.GetTempPath(), "*.log"); 2var sizes = new List<int>(); 3var sw = Stopwatch.StartNew(); 4 5foreach (var file in files) 6{ 7 try 8 { 9 var text = File.ReadAllText(file); 10 sizes.Add(text.Length); 11 if (sizes.Count >= 1000) break; 12 } 13 catch (IOException) { } 14} 15Console.WriteLine(sw.ElapsedMilliseconds + "ms"); 16Console.WriteLine("filecounts: {0}, avg size: {1:F2}", sizes.Count, sizes.Average());

出力は

5767ms
filecount: 1000, avg size: 792024.91

でした。
続いて読み取り部分を

lang

1var text = new StringBuilder(); 2using (var sr = new StreamReader(file)) 3{ 4 while (!sr.EndOfStream) 5 { 6 text.AppendLine(sr.ReadLine()); 7 } 8}

としたバージョンで測ってみましたが、

6571ms
filecount: 1000, avg size: 792051.48

このような感じでした。
ファイルサイズが違ってるのは…なんかミスってるんでしょうね。文字コードあたりの問題でしょうか。
ともあれReadAllTextの方が速く、これはそれぞれ10回試行しても逆転しませんでした。

ファイルサイズ200KB程度のファイルが100個未満しかないのに無視できないほどの時間がかかるのだとしたら、もしかするとStringBuilderクラスを使っていないということはありませんか?
ちなみに、ご存じかもしれませんがStreamReaderクラスにも一気に読み取るReadToEndメソッドがあります。

投稿2015/05/05 15:58

編集2015/05/05 19:52
htsign

総合スコア870

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

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

Mitsuhiko.S

2015/05/05 22:38

仰る通りStringBuilderクラスは使用しておりませんでした。 早速使わせていただきます。 ありがとうございました!
guest

0

200KBが100ファイルだと20MBなので格納先がHDDの場合、読み込みを並列化するとやや遅くなりそうですが、SSDであれば並列化すればした分だけ早くなると思います。

投稿2015/05/05 15:54

編集2015/05/06 02:49
ippei

総合スコア89

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

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

Mitsuhiko.S

2015/05/05 22:43

貴重なご意見ありがとうございました。取り敢えずhtsignさんの方法で実装しますが、また時間のあるときに並列処理も実装したいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問