C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: 文字列を高速に処理するには


(過去ログ 16 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■5859 / inTopicNo.1)  文字列を高速に処理するには
  
□投稿者/ もっこく (1回)-(2007/07/24(Tue) 23:46:27)

分類:[VB.NET/VB2005] 


はじめまして、もっこくと申します。
VB2005を6月末ぐらいからはじめました。
お忙しい中申し訳ございませんが、
アドバイスやヒントなど頂けましたら幸いです。

まず、管理者さまへ
当発言が不適切と判断なされた場合は、削除をお願い致します。

<環境>
WindowsXP SP2
VB2005
.NET FRAMEWORK 2.0

<プログラム:入出力>

DataGridViewを使用して、
1レコード=1000バイト(500レコードぐらいあります)の文字列をバイト数指定して、
1レコード1行単位で各セルに展開、また、各セルの文字列を結合して保存するプログラムを作成してみました。
(漢字も含んでいます)これは問題なく?動きます。

<出力は高速OKです>

タイトルをググると、「StringBuilder」がヒットしまして、
1行(複数セル)に散らばった文字を連結し複数レコード分固めて、
高速に出力できることを知り、実際には1セルずつ文字列を結合していた時よりも、
比較にならないほど早い処理性能が得られました。

<入力ファイルの高速展開について>

そこで、ファイルから1レコード読み、指定されたバイト数でセルに展開していく時も、
同様に高速に文字列処理ができたら・・・と思い探してみましたが見つけられませんでした。
つきましては、お忙しい中申し訳ございませんが、
お手すきの際でかまいませんので、ヒントやアドバイス等頂けましたら幸いです。

<補足>
「StringBuilder」の「CopyTo」を用いてやって見ようとしたのですが、
MSDNでは、2つ目のパラメータに「char」を指定しなければならないようで
「""」とかにすると、「ArgumentNullException」という例外が発生してしまうようです。
ページ下部の「使用例」も参照しましたが、不必要な文字を入れなければならないと解釈しています。
(この辺で見当違いと思い、行き詰っています。)

よろしくお願い致します。

以上
引用返信 編集キー/
■5870 / inTopicNo.2)  Re[1]: 文字列を高速に処理するには
□投稿者/ はつね (214回)-(2007/07/25(Wed) 10:42:00)
No5859 (もっこく さん) に返信
> <出力は高速OKです>
> <入力ファイルの高速展開について>

まず、状況を確認させて頂きます。
DataGridViewのセルに入っている文字列をStringBuilderを使って1行単位で連結してファイル出力するところは満足いく速度が出ているけれど、入力ファイルをDataGridViewの各セルに分けて表示する部分の速度に問題があるという事でしょうか。

入力レコード1行をセルに設定する文字列に分割するのは、何か特定の区切り記号があるのでしょうか。それともバイト数などで固定長的に区切るのでしょうか。
特定の区切り記号があるのならば、String.Split メソッドを使うのも手かも知れません。
ただし、手を付け始める前に、レコード読み込み、文字列への分割、DataGridViewへの設定などどのステップで時間が掛かっているのかを把握する事がまずは必要だと思います。

引用返信 編集キー/
■5873 / inTopicNo.3)  Re[1]: 文字列を高速に処理するには
□投稿者/ ぼのぼの (62回)-(2007/07/25(Wed) 11:16:48)
No5859 (もっこく さん) に返信
こんにちは。
> 1レコード=1000バイト(500レコードぐらいあります)の文字列をバイト数指定して、
> 1レコード1行単位で各セルに展開、また、各セルの文字列を結合して保存するプログラムを作成してみました。
> (漢字も含んでいます)これは問題なく?動きます。

ファイルから読み込むときですが、
・漢字も含んでいる
・区切り単位はバイト数指定
・文字コードは決まっている
ということであれば、いきなりStringとして読み込んで処理するのではなく、Byte配列で
読み込み&区切り処理を行って、画面に表示する直前でStringに変換した方が速かったりしませんかね?
#試してないので外してたらごめんなさい。
引用返信 編集キー/
■5907 / inTopicNo.4)  Re[1]: 文字列を高速に処理するには
□投稿者/ もっこく (2回)-(2007/07/25(Wed) 21:15:29)
こんばんは もっこくです。

こんなに早くアドバイスいただけるなんて思ってなかったので・・ありがとうございます。

はつねさま

>まず、状況を確認させて頂きます。
>DataGridViewのセルに入っている文字列をStringBuilderを使って1行単位で連結してファイル出力するところは満足いく速度が出
>ているけれど、入力ファイルをDataGridViewの各セルに分けて表示する部分の速度に問題があるという事でしょうか。

おっしゃるとおりです。

>入力レコード1行をセルに設定する文字列に分割するのは、何か特定の区切り記号があるのでしょうか。それともバイト数などで固>定長的に区切るのでしょうか。

区切り文字はなく、開始バイトと範囲を指定してセルごと値を設定しています。
下記に簡単なイメージを書きます。
(表現力が乏しくうまく伝えられなかったら申し訳ございません)

1レコード:1000バイト ⇒ データが「1234567890東京都・・・・・」というような感じであります。
そこで、セル1には開始1バイト目から10バイトを格納、セル2には開始11バイト目から6バイトというような感じで、
セル=項目ごとに値を設定しています。

>ただし、手を付け始める前に、レコード読み込み、文字列への分割、DataGridViewへの設定などどのステップで時間が掛かっている>のかを把握する事がまずは必要だと思います。

視野が狭く申し訳ございません。おっしゃるとおりです。
「○○○が遅い」から「○○○を早くする」には、まず原因を見定める必要があります。
ご指摘ごもっともです。申し訳ございません。
出来るだけ早く原因の追究をしたいと思います。

>レコード読み込み

は、「System.IO.StreamReader」を使用して「Peek」になるまで読んでいます。

>文字列への分割

は、あらかじめセル上に開始バイトと範囲の値を設定し、
「VBStrings.MidB(1レコードの文字列,開始バイト,範囲)」で開始と範囲を読みながら、500列ループしています。

>DataGridViewへの設定

は、上記切り出した文字列をそのまま格納しています。


ぼのぼのさま

>ファイルから読み込むときですが、
>・漢字も含んでいる
>・区切り単位はバイト数指定
>・文字コードは決まっている
>ということであれば、いきなりStringとして読み込んで処理するのではなく、Byte配列で
>読み込み&区切り処理を行って、画面に表示する直前でStringに変換した方が速かったりしませんかね?

下記のように読み込んで、分割していました。

Dim Buffer As String = hReader.ReadLine()

>#試してないので外してたらごめんなさい。

私の頭の中になかった「こんなやり方もあるぞ」と教えていただきまして、
いいアドバイスありがとうございます。
(まだまだ頭が固くてすみません)

上記を確認し、改めてご報告したいと思います。

また、何かございましたら、
お手すきの際で構いませんので、ご指摘等よろしくお願いいたします。

以上
引用返信 編集キー/
■5927 / inTopicNo.5)  Re[2]: 文字列を高速に処理するには
□投稿者/ ぼのぼの (64回)-(2007/07/26(Thu) 12:59:33)
No5907 (もっこく さん) に返信
> は、「System.IO.StreamReader」を使用して「Peek」になるまで読んでいます。

> 「VBStrings.MidB(1レコードの文字列,開始バイト,範囲)」で開始と範囲を読みながら、500列ループしています。

MSDNに載ってないと思ったら、ひょっとしてこれですか?
http://jeanne.wankuma.com/tips/string/leftb.html

これをそのまま使ってるとすると、扱うファイルはシフトJISですね。

.NETのStringクラスはUnicodeでデータを保持するので、
StreamReaderで読み込んだ時点で、内部でシフトJIS→Unicodeの変換が行われます。
それをMidB内部でバイト数カウントのために再びシフトJISに変換してることになります。
性能を追求するなら、これってなんか無駄な感じがしませんか?

せっかくファイルがシフトJISなんだから、StreamReaderの代わりにFileStreamを使って、
100バイト読む、200バイト読む、700バイト読む、2バイト読み飛ばす(改行コード分)
これを500回繰り返す、みたいにした方がよりダイレクトで無駄が無い感じがしますよね。

StopWatchクラスを使うと部分的な処理の所要時間を簡単に計測できるので、試してみてください。
http://msdn2.microsoft.com/ja-jp/library/system.diagnostics.stopwatch(VS.80).aspx
引用返信 編集キー/
■5953 / inTopicNo.6)  Re[1]: 文字列を高速に処理するには
□投稿者/ もっこく (3回)-(2007/07/26(Thu) 23:34:51)
こんばんは、もっこくです。

>MSDNに載ってないと思ったら、ひょっとしてこれですか?
>http://jeanne.wankuma.com/tips/string/leftb.html

おっしゃるとおりです。すみません、しっかりと理解できていませんでした。

>これをそのまま使ってるとすると、扱うファイルはシフトJISですね。

>.NETのStringクラスはUnicodeでデータを保持するので、
>StreamReaderで読み込んだ時点で、内部でシフトJIS→Unicodeの変換が行われます。
>それをMidB内部でバイト数カウントのために再びシフトJISに変換してることになります。
>性能を追求するなら、これってなんか無駄な感じがしませんか?

ええ、たしかにおっしゃるとおり無駄と思います。

>せっかくファイルがシフトJISなんだから、StreamReaderの代わりにFileStreamを使って、
>100バイト読む、200バイト読む、700バイト読む、2バイト読み飛ばす(改行コード分)
>これを500回繰り返す、みたいにした方がよりダイレクトで無駄が無い感じがしますよね。

>StopWatchクラスを使うと部分的な処理の所要時間を簡単に計測できるので、試してみてください。

申し訳ありません。ちょっとうまくコーディングできていないようなので、
後日、ご報告致します。

お忙しい中、貴重なお時間を頂きまして感謝しております。

以上

引用返信 編集キー/
■5959 / inTopicNo.7)  Re[2]: 文字列を高速に処理するには
□投稿者/ ぼのぼの (65回)-(2007/07/27(Fri) 10:30:42)
No5953 (もっこく さん) に返信
> 申し訳ありません。ちょっとうまくコーディングできていないようなので、
> 後日、ご報告致します。

焦る必要はないので、ゆっくりやってください(^^
Stopwatchを使って、変更前後で何秒から何秒になったか報告して頂けると嬉しいかもです。
Stopwatchは、こんなかんじで使います。

Dim sw As New System.Diagnostics.Stopwatch()
sw.Start()
Using strm As New System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read)
    Dim ba(1000) As Byte
    Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS")
    Dim readCnt As Integer = strm.Read(ba, 0, 1000)
    If readCnt >= 30 Then
        MsgBox(enc.GetString(ba, 0, 10))
        MsgBox(enc.GetString(ba, 10, 20))
    End If
End Using
sw.Stop()
MsgBox(String.Format("{0}秒", sw.ElapsedMilliseconds / 1000))

引用返信 編集キー/
■5962 / inTopicNo.8)  Re[3]: 文字列を高速に処理するには
□投稿者/ まどか (344回)-(2007/07/27(Fri) 11:07:16)
出力との決定的な違いに描画が発生することが挙げられます。
ループの中でコントロールへ設定しているのであれば、処理全体を非表示と表示で囲むだけでも差が出てくると思います。

引用返信 編集キー/
■5995 / inTopicNo.9)  Re[1]: 文字列を高速に処理するには
□投稿者/ もっこく (4回)-(2007/07/28(Sat) 13:11:27)
こんにちは もっこくです。

ぼのぼのさま

>焦る必要はないので、ゆっくりやってください(^^
>Stopwatchを使って、変更前後で何秒から何秒になったか報告して頂けると嬉しいかもです。

ご報告が遅れ申し訳ございません。

<入力データ> : 1レコード=1000バイトを20レコード分

<計測回数 > : 5回流しましたが、大きな差が見えないので、1回分書きます。

<計測内容1> : 「FileStream」で、最初の1000レコードのみ処理し計測。
           (すみません。コーディングの問題で「FileStream」を使用して
            うまく残りの19レコード分処理出来ていません)

<    2> : 「StreamReader」を使用して20レコード分処理し計測。
           (こちらは、1レコード単位に20レコード分処理できています)

<    3> : 行ヘッダーに項番を20レコード分設定(非表示です)する時間を計測。
          (1レコード分セルに設定し、行ヘッダに項番追加を繰り返しています)

*1レコードあたり500列に値を設定 単位は[ms]です。

【FileStream   1レコード       】0.353

【StreamReader  1レコード:行ヘッダ設定】0.385 0.001
【StreamReader  2レコード:行ヘッダ設定】0.426 0.001
【StreamReader  3レコード:行ヘッダ設定】0.483 0.001
【StreamReader  4レコード:行ヘッダ設定】0.522 0.001
【StreamReader  5レコード:行ヘッダ設定】0.591 0.002
【StreamReader  6レコード:行ヘッダ設定】0.633 0.003
【StreamReader  7レコード:行ヘッダ設定】0.670 0.002
【StreamReader  8レコード:行ヘッダ設定】0.714 0.002
【StreamReader  9レコード:行ヘッダ設定】0.775 0.002
【StreamReader 10レコード:行ヘッダ設定】0.802 0.003
【StreamReader 11レコード:行ヘッダ設定】0.877 0.003
【StreamReader 12レコード:行ヘッダ設定】0.911 0.003
【StreamReader 13レコード:行ヘッダ設定】0.969 0.003
【StreamReader 14レコード:行ヘッダ設定】0.999 0.003
【StreamReader 15レコード:行ヘッダ設定】1.054 0.004
【StreamReader 16レコード:行ヘッダ設定】1.079 0.004
【StreamReader 17レコード:行ヘッダ設定】1.163 0.004
【StreamReader 18レコード:行ヘッダ設定】1.174 0.004
【StreamReader 19レコード:行ヘッダ設定】1.242 0.005
【StreamReader 20レコード:行ヘッダ設定】1.248 0.006


まどかさま

>出力との決定的な違いに描画が発生することが挙げられます。
>ループの中でコントロールへ設定しているのであれば、処理全体を非表示と表示で囲むだけでも差が出てくると思います。

アドバイスありがとうございます。

セルに値設定や行ヘッダに項番設定時など表示せずやっています。
大量データを流すと固まったと勘違いしたことがありましたので、
今後プログレスバーを実装してみたいと考えております。

上記確認は、非表示でやっています。

ぼのぼのさま、まどかさま、貴重なお時間を割いて頂きありがとうございます。
コーディングがうまく出来てから再度計測してご報告したいと思っています。

お手すきの際でかまいませんので、ご指摘等お願いいたします。

以上


引用返信 編集キー/
■6006 / inTopicNo.10)  Re[1]: 文字列を高速に処理するには
□投稿者/ もっこく (5回)-(2007/07/29(Sun) 13:17:25)
こんにちは もっこくです。

>(すみません。コーディングの問題で「FileStream」を使用して
> うまく残りの19レコード分処理出来ていません)

こちらを解決し「FileStream」と「StreamReader」で再度計測してみましたので、
ご参考になるかわかりませんが、ご報告いたします。

* 処理件数が少なかったのか大きな差が見えませんでした。

1R 展開 FileStream 0.360秒:StreamReader 0.345秒
1R 展開 FileStream 0.392秒:StreamReader 0.394秒
1R 展開 FileStream 0.441秒:StreamReader 0.438秒
1R 展開 FileStream 0.476秒:StreamReader 0.490秒
1R 展開 FileStream 0.534秒:StreamReader 0.525秒
1R 展開 FileStream 0.595秒:StreamReader 0.582秒
1R 展開 FileStream 0.627秒:StreamReader 0.621秒
1R 展開 FileStream 0.676秒:StreamReader 0.665秒
1R 展開 FileStream 0.716秒:StreamReader 0.717秒
1R 展開 FileStream 0.771秒:StreamReader 0.756秒
1R 展開 FileStream 0.805秒:StreamReader 0.813秒
1R 展開 FileStream 0.854秒:StreamReader 0.858秒
1R 展開 FileStream 0.900秒:StreamReader 0.906秒
1R 展開 FileStream 0.950秒:StreamReader 0.955秒
1R 展開 FileStream 1.001秒:StreamReader 1.012秒
1R 展開 FileStream 1.040秒:StreamReader 1.061秒
1R 展開 FileStream 1.090秒:StreamReader 1.103秒
1R 展開 FileStream 1.135秒:StreamReader 1.201秒
1R 展開 FileStream 1.191秒:StreamReader 1.228秒
1R 展開 FileStream 1.226秒:StreamReader 1.268秒
1R 展開 FileStream 1.271秒:StreamReader 1.304秒
1 処理 FileStream 17.141秒:StreamReader 17.057秒


>.NETのStringクラスはUnicodeでデータを保持するので、
>StreamReaderで読み込んだ時点で、内部でシフトJIS→Unicodeの変換が行われます。
>それをMidB内部でバイト数カウントのために再びシフトJISに変換してることになります。
>性能を追求するなら、これってなんか無駄な感じがしませんか?

以前、ぼのぼのさまがおっしゃったとおり、上記は無駄と思いますので、
「FileStream」でコーディングしていこうと思っています。

今回この掲示板に投稿させていただき、
質問に対する回答だけでなく、こういう事象が発生した時、解決のための観点とか、
いろいろ知らされました。(というか・・・もっこくが無知でした)

この投稿につきましては、解決とさせていただきます。

はつねさま ぼのぼのさま まどかさま

お忙しい中お時間を割いていただきありがとうございます。

最後に、当掲示板の管理者さま、
もっこくにこのような機会を与えくださり、ありがとうございました。

以上

解決済み
引用返信 編集キー/
■6072 / inTopicNo.11)  Re[2]: 文字列を高速に処理するには
□投稿者/ ぼのぼの (66回)-(2007/07/31(Tue) 14:12:46)
No6006 (もっこく さん) に返信
> こちらを解決し「FileStream」と「StreamReader」で再度計測してみましたので、
> ご参考になるかわかりませんが、ご報告いたします。
> 
> * 処理件数が少なかったのか大きな差が見えませんでした。

こんにちは。
既に解決済みになっているようですが、そんなに差がでないもんかなぁと思い、
こっちでも一応試してみたので結果だけ載せときます。

試したソース

    Private Function CreateEmptyTable() As System.Data.DataTable
        Dim tbl As New System.Data.DataTable()
        For i As Integer = 1 To 10
            tbl.Columns.Add(String.Format("列{0:00}", i), GetType(String))
        Next
        Return tbl
    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim tbl As System.Data.DataTable = Me.CreateEmptyTable()
        Dim sw As New System.Diagnostics.Stopwatch()
        sw.Start()
        Using reader As New System.IO.StreamReader("C:\temp\test.txt", System.Text.Encoding.GetEncoding("Shift_JIS"))
            While Not reader.EndOfStream
                Dim s As String = reader.ReadLine()
                Dim row As System.Data.DataRow = tbl.NewRow()
                For i As Integer = 0 To 9
                    row(i) = VBStrings.MidB(s, i * 100 + 1, 100)
                Next
                tbl.Rows.Add(row)
            End While
        End Using
        sw.Stop()
        MsgBox(String.Format("{0}秒", sw.ElapsedMilliseconds / 1000))
        Me.DataGridView1.DataSource = tbl
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim tbl As System.Data.DataTable = Me.CreateEmptyTable()
        Dim sw As New System.Diagnostics.Stopwatch()
        sw.Start()
        Using strm As New System.IO.FileStream("C:\temp\test.txt", IO.FileMode.Open, IO.FileAccess.Read)
            Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS")
            While strm.Position < strm.Length
                Dim b(1000) As Byte
                strm.Read(b, 0, 1000)
                Dim row As System.Data.DataRow = tbl.NewRow()
                For i As Integer = 0 To 9
                    row(i) = enc.GetString(b, i * 100, 100)
                Next
                tbl.Rows.Add(row)
                strm.Read(b, 0, 2) '改行コード分
            End While
        End Using
        sw.Stop()
        MsgBox(String.Format("{0}秒", sw.ElapsedMilliseconds / 1000))
        Me.DataGridView1.DataSource = tbl
    End Sub

test.txtは、1行1000バイトの500行です。

Button1_ClickでMsgBoxに表示された秒数は3回試してそれぞれ0.147, 0.138, 0.145
Button2_ClickでMsgBoxに表示された秒数は3回試してそれぞれ0.049, 0.052, 0.052

はっきりした差はでましたが体感ではStreamReaderでもぜんぜん重さは感じないですね。
ボトルネックは違うところにあったのかなぁ…

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -