- PR -

VBでCSV読み込み & 書き出しの処理速度向上について

投稿者投稿内容
七誌
会議室デビュー日: 2007/05/15
投稿数: 2
投稿日時: 2007-05-15 12:38
いつも拝見させて頂いております。
今回初めて投稿させて頂きました。

下記環境

Windows XP(CPU:2GHz メモリ:512MB)
Visual Basic 2005 Express Edition

において、CSVファイルからデータを読み込み、読み込み行を更に分解して再度CSV
ファイルに書き出すというプログラムを作成しております。
事情があって、不本意ながら上記仕様のプログラムを作成せざるを得ない状況です。

本題ですが、下記プログラムを実行すると、確かに上記仕様の処理を行ってくれる
のですが、5Mバイト程度のCSVファイルを処理しようとすると、処理速度が極端に
落ち、処理が終了するまで30分程の時間を要してしまいます。プログラマとしては
初歩的な質問でお恥ずかしいのですが、処理速度UPの方法をどなたかご教授して頂
けないでしょうか?

***一部抜粋***

Private Structure CsvDat
Dim year As String '年
Dim month As String '月
Dim day As String '日
Dim time As String '時
Dim value1 As String '値1
Dim value2 As String '値2
End Structure
Private sd() As CsvDat
Private spt() As String

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click


Dim n As Integer
Dim line As String
Dim temp() As String


Dim sr1 As New System.IO.StreamReader("読み込みファイル名", System.Text.Encoding.Default)
Dim sw1 As New System.IO.StreamWriter("書き出しファイル名", False)
'ファイルの最後までループ
n = 0
Do Until sr1.Peek() = -1
line = ""
temp = Split(sr1.ReadLine(), ",")
ReDim Preserve sd(n)

spt = Split(temp(0), "/") '年月日分解
sd(n).year = spt(2) '年
sd(n).day = spt(1) '月
sd(n).month = spt(0) '日
sd(n).time = temp(1)
sd(n).value1 = temp(2)
sd(n).value2 = temp(3)
'取得結果を書き出し
line = sd(n).year & "," & sd(n).month & "," & sd(n).day & "," & sd(n).time & "," & sd(n).value1 & "," & sd(n).value2
sw1.WriteLine(line)

n += 1
Loop
sr1.Close() 'ファイルを閉じる
sw1.Close()

end Sub
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2007-05-15 13:02
コード:

ReDim Preserve sd(n)



sdをループの外で使うんですか?
使わないなら不要な変数だし、使うならコレクションを使ったほうがいいです。

[ メッセージ編集済み 編集者: burton999 編集日時 2007-05-15 13:03 ]
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2007-05-15 13:07
諸農です。

「sd」が何をする機能を持っているのか判りませんが、最初にReadToEnd()で読み込みテキストの行数を取得してからsdの配列要素数を決めれば良いんじゃないでしょうか。
ただ「sd」を一時的にワークとして使用しているのなら、わざわざ配列にする必要もないような気がしますが。。

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
mio
ぬし
会議室デビュー日: 2005/08/25
投稿数: 734
お住まい・勤務地: 神奈川県
投稿日時: 2007-05-15 13:18
最初に調べないにしても、せめて倍倍くらいには、しましょう。
たとえば初期値100で、101行目が来たら200に、201行目が来たら400に、と。
…という処理を、コレクションなら勝手にやってくれるわけですが。
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-05-15 13:22
他の方も指摘されていますが、ループごとに ReDim Preserve するのは
ヒープメモリの確保が頻繁に発生しますので、非常に効率が悪いです。
後続処理でこのデータを使用しないのならば、配列にいれない方がよいと思います。
後続で使用するため、どうしても保持する必要があるのならコレクションを遣いましょう。

引用:

sd(n).year & "," & sd(n).month & "," & sd(n).day & "," & sd(n).time & "," & sd(n).value1 & "," & sd(n).value2
sw1.WriteLine(line)


この文字列の結合も、String.Format を使ったほうが速いし、見やすくなると思いますよ。
ぽぴ王子
ぬし
会議室デビュー日: 2006/03/24
投稿数: 475
お住まい・勤務地: お住まい:城・勤務地:城
投稿日時: 2007-05-15 13:25
皆さん指摘されていますが、変数 sd について何も言及されていないので、
後で利用するために配列を確保しているのかそれとも単にワークとして使
用しているのかが不明ですね。
言及されていないこともあって、私にはワークとして使用しているように見
えました。

ワークとして使用しているのであれば、burton999 さんが書かれている
ように不要な変数ですし、使わずに済むのであればそちらの方がいいでしょ
うね。
時間がかかるのは、ループの中で Redim しているためだと思います。

とりあえずループの中をこんな感じにしてみればいいんじゃないでしょうか。

コード:

    temp = Split(sr1.ReadLine(), ",")
    Dim params As New List(Of String)
    params.AddRange(Split(temp(0), "/"))    ' 年月日分解
    params.Add(temp(1))
    params.Add(temp(2))
    params.Add(temp(3))

    ' 取得結果を書き出し
    sw1.WriteLine(String.Join(",", params.ToArray()))


もし後で使うために取っているというのであれば、それも皆さんがすでに書
かれているようにコレクションを使うと良いと思います。
_________________
ぽぴ王子@わんくま同盟
ぽぴ王子の人生プログラミング中 / ぽぴンち。
にのさん
会議室デビュー日: 2005/02/17
投稿数: 2
投稿日時: 2007-05-15 13:30
CSVを扱うならTextFieldParserを使ってみてはどうでしょう。
http://www.atmarkit.co.jp/fdotnet/dotnettips/487csvparser/csvparser.html
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2007-05-15 13:44
引用:

七誌さんの書き込み (2007-05-15 12:38) より:
本題ですが、下記プログラムを実行すると、確かに上記仕様の処理を行ってくれる
のですが、5Mバイト程度のCSVファイルを処理しようとすると、処理速度が極端に
落ち、処理が終了するまで30分程の時間を要してしまいます。


タスクマネージャーでその間のCPU使用率はどうなっているでしょうか。100%(CPUの数によっては50%や25%)ならば、配列処理が遅いのでしょう。
もし100%よりずっと低ければディスクの読み書きのどちらかまたは両方が遅いのでしょう。APIに対してバッファーサイズの指定ができるはずなので、多めにしてはどうでしょうか。また、読み書きも、読み、と書き、に分けて計測してみることをお勧めします。

--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}

スキルアップ/キャリアアップ(JOB@IT)