高速文字列連結

解決


CASEY  2004-08-09 08:47:09  No: 85024  IP: [192.*.*.*]

いつも参考にさせていただいております。

以下を実行すると遅いです。
配列数が多くなればなるほど、あたりまえですが遅くなります。
C言語のポインタのような、何か高速に文字列を連結することはできないものでしょうか?
お願いいたします

具体的な内容は以下の通り
データをバイト配列(文字コードが入っています)へ格納
これを文字列にする
その後に文字コードをSJISに変更していこうと思っています。

※入力文字コードはJISやSJIS、UNICODEなどさまざまです

今現在のプログラム

        'With ProgressBar1
        '    .Min = 0
        '    .Max = lngSize
        '    For i = 0 To lngSize - 1 Step 1
        '        strConve = strConve & Chr(bytArray(i))
        '        .Value = i
        '        DoEvents
        '    Next i
        'End With
        'Debug.Print strConve

編集    削除
いわし  2004-08-09 15:44:51  No: 85025  IP: [192.*.*.*]

Mid ステートメント はいかが?

編集    削除
CASEY  2004-08-09 19:14:16  No: 85026  IP: [192.*.*.*]

MIDは文字の切り出しだと思うのですが。。。
念のために調べたんですが知識不足でわかりませんでした。
もう少し詳しく教えていただけませんか?

編集    削除
ねろ  2004-08-09 20:28:08  No: 85027  IP: [192.*.*.*]

VBの場合文字列が長くなると、連結にかなりの時間がかかります。
strConve = strConve & Chr(bytArray(i))
の代わりに
strConve = "A" & "A"
とやって時間を比べてみて下さい。
もしこれで時間がかなり早くなるのであれば、
時間がかかるのは、配列が多い為ではなく、文字列が
長くなり連結に時間がかかる為です。
この場合、文字列の長さを500〜1000位にして「Join」
で連結するとかなり高速になります。

編集    削除
nanashi  2004-08-09 20:46:06  No: 85028  IP: [192.*.*.*]

Mid関数を使う場合は下記の方法で試してみてください。


With ProgressBar1
    .Min = 0
    .Max = lngSize
    
    strConve = Space$(lngSize)
    
    For i = 0 To lngSize - 1 Step 1
        Mid$(strConve, i + 1, 1) = Chr$(bytArray(i))
        .Value = i
        DoEvents
    Next i
End With

Debug.Print strConve

編集    削除
CASEY  2004-08-09 21:08:32  No: 85029  IP: [192.*.*.*]

ありがとうございます。
nanashiさんのを実験してみました。
処理速度が後半落ちることなく実行できました。

ねろさんのjoinを使い方法とどちらが早いでしょうか。
joinの使い方を調べているのですが、併用したほうがいいのか
どちらか一方のほうがいいのかいかがでしょうか?

贅沢かもしれませんが高速になったとはいえ、最低でも今の二倍の速さがほしいです。(これを再帰的に実行していく必要があるので)
無理なものでしょうか・・・
strConv(バイト配列 , vbunicode)はなぜあんなに高速に処理できるのでしょうか?
愚問だと思いますがまったくわからないので教えてください。
お願いします。

(VB6SP6/WinXPPro/PenIII 600/256M)

編集    削除
CASEY  2004-08-09 21:14:20  No: 85030  IP: [192.*.*.*]

今現在はこうなっています。

        With ProgressBar1
            .Min = 0
            .Max = lngSize
            
            strConve = Space$(lngSize) 
            For i = 0 To lngSize Step 1
                Mid$(strConve, i + 1, 1) = Chr$(bytArray(i))
                .Value = i
                Label5.Caption = Format(i, "#,###,###") & "/" _
                               & Format(lngSize, "#,###,###") & "[" _
                               & Format(Int((i / lngSize) * 100), "@@@") & "%]"
                DoEvents
            Next i
        End With
        Debug.Print strConve

編集    削除
ねろ  2004-08-10 00:00:09  No: 85031  IP: [192.*.*.*]

そのコードで一番時間のかかっている部分は、
>.Value
>Label5.Caption = Format(i, "#,###,###") & "/" _
>& Format(lngSize, "#,###,###") & "[" _
>& Format(Int((i / lngSize) * 100), "@@@") & "%]"
>DoEvents
ですよ。

速くやるならプログレスバーは不要です。
If (i / 100 = i \ 100) Then
      .Value = i
      Label5.Caption = Format(i, "#,###,###") & "/" _
      & Format(lngSize, "#,###,###") & "[" _
      & Format(Int((i / lngSize) * 100), "@@@") & "%]"      
      DoEvents
End If
どうしてもと言うなら、この位のことをやらなくては速くくならない。
表示は一番最後に一回だけの方がいい。

Joinでやるなら
For i = 0 To lngsize
     s(i) = Chr(bytArray(i))
Next
strConve = Join(s, "")
プログレスバーは入りません。
ですがどちらが速いかは条件次第です。

編集    削除
CASEY  2004-08-10 11:04:43  No: 85032  IP: [192.*.*.*]

ねろさん、皆様ありがとうございます!!!
比較にならないほど高速になりました!!!
本当にありがとうございます。

もうひとつ疑問が出てしまったのですが、このまま質問してもよろしいでしょうか?

ねろさんのコードに置き換えて実行したところ、Debug.Printでは正常にstrConveが表示されるのですが、ためしにテキストボックスに表示したところ、今までは正常に表示されていたものが、一行程度でそれ以降が表示されなくなりました。
(過去ログからテキストボックスの制限については理解しています)

改行コード(なのかわかりませんが)なども何か手を加えてやる必要があるのでしょうか?
実際には表示せずに処理をしていくので問題なのですが気になってしまって・・・
よろしくお願いします。
現在のコードを以下に記します。

        With ProgressBar1
            .Min = 0
            .Max = lngSize
            
            strConve = Space$(lngSize)  
            For i = 0 To lngSize - 1 Step 1
                Mid$(strConve, i + 1, 1) = Chr$(bytArray(i))
                If (i / 100 = i \ 100) Then
                      .Value = i
                      Label5.Caption = Format(i, "#,###,###") & "/" _
                                     & Format(lngSize, "#,###,###") & "[" _
                                     & Format(Int((i / lngSize) * 100), "@@@") & "%]"
                      DoEvents
                End If
            Next i
        End With
        Debug.Print strConve
        'txtHTTPData.Text = strConve

それと切れてしまう例は(例としてHTML)、以下が一行に表示されて切れます。(貼り付けたら勝手に改行されました。)
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=Shift_JIS">
<meta name="robots" content="noarchive">
<meta name="description" content="

編集    削除
CASEY  2004-08-10 11:05:58  No: 85033  IP: [192.*.*.*]

訂正

問題なのですが

問題ないと思うのですが

編集    削除
tz  2004-08-10 18:07:07  No: 85034  IP: [192.*.*.*]

文字列バッファクラスです。
使い方は----より下を"StringBuffer.cls"として保存し、
プロジェクトに追加してください。あとは「使用例)」を参照してください。
バグがあったらごめんなさい。
---------------------------------------------
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "StringBuffer"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
' @(s)
' 文字列バッファクラス
' 使用例)
'| Dim sb As StringBuffer
'| Set sb = New StringBuffer
'| Call sb.Append("abcde")
'| Call sb.Append("123")
'| Call MsgBox(sb.ToString)
'

Option Explicit

Private Const DEF_BUF_SIZE  As Long = 128   '' デフォルトのバッファサイズ
Private m_Text              As String       '' 文字列
Private m_Length            As Long         '' 文字列長
Private m_CapacityIncrement As Long         '' バッファ増加量
Private m_Capacity          As Long         '' バッファ容量

' @(e)
'
' 機能      : コンストラクタ
'
Private Sub Class_Initialize()
    Call Init(DEF_BUF_SIZE)     '' 初期化処理呼び出し
End Sub

' @(f)
'
' 機能      : 初期化
'
' 引き数    : ARG1 - [in ]文字列の長さ
'
' 機能説明  :
'
' 備考      :
'
Public Sub Init(ByVal lInitialCapacity As Long)
    m_Length = 0                                '' 文字列長初期化
    
    m_CapacityIncrement = lInitialCapacity      '' 同時に確保する文字数初期化
    If m_CapacityIncrement < 1 Then             '' 1未満ならデフォルト値設定
        m_CapacityIncrement = DEF_BUF_SIZE
    End If
    
    m_Text = String$(m_CapacityIncrement, 0)    '' 文字列初期化
    m_Capacity = m_CapacityIncrement            '' バッファ長初期化
End Sub

' @(f)
'
' 機能      : 文字列追加
'
' 引き数    : ARG1 - [in ]追加する文字列
'
' 機能説明  : 文字列バッファに文字列を追加する
'
Public Sub Append(ByRef sAddStr As String)
    Dim lAddStrLen  As Long     ' 追加する文字列長

    lAddStrLen = Len(sAddStr)
    
    '' 現在確保しているバッファ長で足りない?
    If m_Capacity < (m_Length + lAddStrLen) Then
        ''' バッファ確保
        m_CapacityIncrement = m_CapacityIncrement * 2
        m_Text = m_Text & String$(m_CapacityIncrement, 0)
        m_Capacity = m_Capacity + m_CapacityIncrement
    End If
    '' 文字列追加
    Mid$(m_Text, m_Length + 1, lAddStrLen) = sAddStr
    '' 文字列長設定
    m_Length = m_Length + lAddStrLen
End Sub

' @(f)
'
' 機能      : 文字列取得
'
' 返り値    : 文字列
'
Public Property Get Text() As String
    Text = Left$(m_Text, m_Length)
End Property

' @(f)
'
' 機能      : 文字列設定
'
' 引き数    : ARG1 - 設定する文字列
'
Public Property Let Text(ByRef sNewText As String)
    m_Text = sNewText
    m_Length = Len(m_Text)
    m_Capacity = m_Length
End Property

' @(f)
'
' 機能      : 文字列クリア
'
Public Sub Clear()
    m_Text = ""
    m_Length = 0
    m_Capacity = 0
End Sub

' @(f)
'
' 機能      : 文字列取得
'
' 返り値    : 文字列
'
Public Property Get ToString() As String
    ToString = Left$(m_Text, m_Length)
End Property

' @(f)
'
' 機能      : 文字列バッファの容量取得
'
' 返り値    : 文字列バッファの現在の容量
'
Public Property Get Capacity() As Long
    Capacity = m_Capacity
End Property

編集    削除
CASEY  2004-08-11 12:53:07  No: 85035  IP: [192.*.*.*]

ありがとうございました!!

皆様のおかげで見違えるほど高速になりました!
VBでも、作り方によってぜんぜん違うんですね。
これからも勉強していきたいと思います。

ありがとうございました。

編集    削除