【问题标题】:Speeding up my code for string text replacement加快我的代码以进行字符串文本替换
【发布时间】:2022-01-14 11:30:20
【问题描述】:

作为对我之前的问题的补充,我得到了答复,并且帮助我在超快的时间内完成了项目,我现在有一个类似的项目,原始代码已经可以运行并且运行良好,但是,我有两列,其中包含以下内容:

K 列 - ws 和服务器的名称及其 IP 地址以逗号分隔 L 列 - 只是 IP 地址

在每批名称和 IP 或只是 IP 中,由于各种原因,我对出现在最终输出中的范围不感兴趣。

很遗憾,我无法给出字符串的真实示例,但以下是您可能会发现的示例:

K - Mike [10.6.3.4], Mike1 [10.2.3.4], Mike3 [10.165.75.3]....... L - 10.6.3.4、10.2.3.4、10.165.75.3…………

L中的IP与K中的IP相同,但顺序不一定相同。

这没关系。

我编写了以下子代码,它遍历 excel ws 中的所有行,读取字符串,然后写出相同的字符串,但没有我不想要的范围(你会看到那些是 10.6 和 192.168。

如您所见,我为一行中的第一个字符串编写了一个循环,然后根据列为同一行中的第二个字符串编写了第二个循环,我清除了训练逗号(我不知道如何设置将保存“干净”字符串的新数组的结束长度,所以我得到一些我必须清理的额外逗号)然后我转到下一行直到结束。

效果很好。

我只是觉得它可以做得更快,更清洁,以及一个循环而不是两个循环,以及可能不需要清理额外的逗号。

感谢任何人可能拥有的任何 cmets 或修复程序。

同样,它工作正常,但如果你明白我的意思,它会感觉“不干净”。

'Sub to remove unwanted IP ranges from both columns
Sub CleanNamesIPsfromList()

    Const Col_Names = "J"
    Const Col_IPs = "K"
    Const ROW_NamesIPs = 5 'First line of Names and IP's

    Dim wb As Workbook
    Dim wsMain As Worksheet
    Dim arHoldNames() As String, arHoldIPs() As String
    Dim arPutNames() As String, arPutIPs() As String
    Dim strHoldNames As String, strHoldIPs As String
    Dim txt106 As String, txt192168 As String
    Dim rn As Long, rhn As Long, ri As Long, rhi As Long, lastrowN As Long, lastrowI As Long, i As Long
    
    txt106 = "10.6."
    txt192168 = "192.168."

    Set wb = ActiveWorkbook
    With wb
        Set wsMain = .Sheets("Main")
    End With

    With wsMain
        lastrowN = .Cells(.Rows.Count, Col_Names).End(xlUp).Row
        lastrowI = .Cells(.Rows.Count, Col_IPs).End(xlUp).Row
        If lastrowN < ROW_NamesIPs Or lastrowI < ROW_NamesIPs Then
            MsgBox "No text found in Columns " & Col_Names & " " & Col_IPs, vbCritical
            Exit Sub
        End If

        'Run through whole ws row by row in both columns
        For i = ROW_NamesIPs To lastrowN
            rhn = 0
            'Load list of names into string holder
            strHoldNames = .Cells(i, Col_Names).Value
            'Split the string into the holding array based on the comma
            arHoldNames = Split(strHoldNames, ",")
            'Loop through the array to find those names not containg unwanted data
            ReDim arPutNames(0 To UBound(arHoldNames))
            For rn = 0 To UBound(arHoldNames)
                If InStr(1, arHoldNames(rn), txt106, vbTextCompare) > 0 Or InStr(1, arHoldNames(rn), txt192168, vbTextCompare) > 0 Then
                    'If it contains unwanted string, increment rn to next array cell and move on
                    rn = rn + 1
                Else
                    'I want this string so put in new array holder and increment array counter
                    arPutNames(rhn) = arHoldNames(rn)
                    rhn = rhn + 1
                End If
            Next 'Loop through array holding origional string
            'I have what I want, so put it back in relevant cell in ws
            .Cells(i, Col_Names).Value = Join(arPutNames, ",")
            'Remove all trailing commas
            While Right(.Cells(i, Col_Names).Value, 1) = ","
                .Cells(i, Col_Names).Value = Left(.Cells(i, Col_Names).Value, Len(.Cells(i, Col_Names).Value) - 1)
            Wend

            rhi = 0
            'Load list of names into string holder
            strHoldIPs = .Cells(i, Col_IPs).Value
            'Split the string into the holding array based on the comma
            arHoldIPs = Split(strHoldIPs, ",")
            'Loop through the array to find those names not containg unwanted data
            ReDim arPutIPs(0 To UBound(arHoldIPs))
            For ri = 0 To UBound(arHoldIPs)
                If InStr(1, arHoldIPs(ri), txt106, vbTextCompare) > 0 Or InStr(1, arHoldIPs(ri), txt192168, vbTextCompare) > 0 Then
                    'If it contains unwanted string, increment ri to next array cell and move on
                    ri = ri + 1
                Else
                    'I want this string so put in new array holder and increment array counter
                    arPutIPs(rhi) = arHoldIPs(ri)
                    rhi = rhi + 1
                End If
            Next 'Loop through array holding origional string
            'I have what I want, so put it back in relevant cell in ws
            .Cells(i, Col_IPs).Value = Join(arPutIPs, ",")
            'Remove all trailing commas
            While Right(.Cells(i, Col_IPs).Value, 1) = ","
                .Cells(i, Col_IPs).Value = Left(.Cells(i, Col_IPs).Value, Len(.Cells(i, Col_IPs).Value) - 1)
            Wend
        
        Next 'Move to next row and repeat untill end
    End With 'End main

End Sub

新代码

好的,基于将所有数据读入数组,我写了这段代码(当然是借用betters的)。

    Dim Lastcell As Range
    Set LastCell = .Range("K5:L5").End(xlDown)
    
    'capture all of the data at once with a range-array copy
    Dim arHoldAllNames As String, ArHoldAllIPs As String
    arHoldAllNames = .Range("K5", LastCell).Value
    ArHoldAllIPs = .Range("L5", LastCell).Value

    for i = 0 to UBound(arHoldAllNames, 1)
        'Code for Names
    Next
    for i = 0 to UBound(ArHoldAllIPs, 1)
        'Code for IPs
    Next

    temp = 0
    For i = 5 To LastCell
        .Cells(i, Col_Names).Value = arHoldAllNames(temp)
        .Cells(i, Col_IPs).Value = ArHoldAllIPs(temp)
        i = i + 1
        temp = temp + 1
    Next

这就是你们的意思吗?

另外,我发现我可以将一系列数据读入一个数组,这无疑加快了速度。

那就是这段代码:

    arHoldAllNames = .Range("K5", LastCell).Value
    arHoldAllIPs = .Range("L5", LastCell).Value

然后我可以反过来做吗:

.Range("K5", LastCell).Value = arHoldAllNames 
.Range("L5", LastCell).Value = arHoldAllIPs

并让 ws 中的所有单元格与数组中的内容一起传播?

还是我还需要做 For 循环:

    temp = 0
    For i = 5 To LastCell
        .Cells(i, Col_Names).Value = arHoldAllNames(temp)
        .Cells(i, Col_IPs).Value = ArHoldAllIPs(temp)
        i = i + 1
        temp = temp + 1
    Next

【问题讨论】:

  • 一个很大的改进是将工作表中的所有值放入一个数组中,在数组中进行所有更改,然后将数组放回工作表中。我已经看到我的项目的运行时间因该更改而减少了 90%。
  • 我的工作表可以有 7000 到 20000 行。并不是说数组无法容纳它。所以你是说,将两列中的所有数据读入两个单独的数组,然后对这些数组而不是 ws 进行工作?
  • 具体来说,使用范围数组复制。见这里stackoverflow.com/questions/19164840/…。如dim data as variant: data = Range("A1:D10000"): data(r,c) = ... : Range("A1:D10000") = data。因此,将所有值(您需要的)一次复制到一个变量数组中,更改数组中的值,然后将它们一次全部复制回电子表格单元格。它快得多..
  • 这个 (stackoverflow.com/questions/68130319/…) 可能更容易阅读。具体看前几行和最后几行代码。
  • 好的,我看到您实际上已经回答了所有问题@RBarryYoung,并且还对代码进行了更改。我稍后会试一试,如果成功了,我会告诉你的:)

标签: arrays excel vba string


【解决方案1】:

好的。

有了给出的想法,以及 RBarryYoung 编写的代码帮助,以及一个简单的更改,以消除我在将字符串从主数组读取到小型工作数组时遇到的下标超出范围错误,代码如下跟随。

'Sub to remove unwanted IP ranges from both columns
Sub CleanNamesIPsfromList()

    Const Col_Names = "J"
    Const Col_IPs = "K"
    Const StrtRow = 5 'First line of Names and IP's

    Dim wb As Workbook
    Dim wsMain As Worksheet
    Dim arHoldNames() As String, arHoldIPs() As String
    Dim arPutNames() As String, arPutIPs() As String
    Dim strHoldNames As String, strHoldIPs As String
    Dim txt106 As String, txt192168 As String
    Dim rn As Long, rhn As Long, ri As Long, rhi As Long, lastrowN As Long, lastrowI As Long, i As Long, RlLstRw As Long
    Dim NamCel As Variant, IpCal As Variant
    
    txt106 = "10.6."
    txt192168 = "192.168."

    Set wb = ActiveWorkbook
    With wb
        Set wsMain = .Sheets("Main")
    End With
    
    With wsMain
        lastrowN = .Cells(.Rows.Count, Col_Names).End(xlUp).Row
        lastrowI = .Cells(.Rows.Count, Col_IPs).End(xlUp).Row
        If lastrowN < StrtRow Or lastrowI < StrtRow Then
            MsgBox "No text found in Columns " & Col_Names & " " & Col_IPs, vbCritical
            Exit Sub
        End If
        
        ' Range-array copy the data into VBA
        NamCel = .Range(Col_Names & StrtRow & ":" & Col_Names & lastrowN)
        IpCel = .Range(Col_IPs & StrtRow & ":" & Col_IPs & lastrowI)

        'Run through whole ws row by row in both columns
        RlLstRw = lastrowN - 4
        For i = 1 To RlLstRw
            rhn = 0
            'Load list of names into string holder
            'strHoldNames = NamCel(i, 1)
            strHoldNames = NamCel(i, 1)
            'Split the string into the holding array based on the comma
            arHoldNames = Split(strHoldNames, ",")
            'Loop through the array to find those names not containg unwanted data
            ReDim arPutNames(0 To UBound(arHoldNames))
            For rn = 0 To UBound(arHoldNames)
                If InStr(1, arHoldNames(rn), txt106, vbTextCompare) > 0 Or InStr(1, arHoldNames(rn), txt192168, vbTextCompare) > 0 Then
                    'If it contains unwanted string, increment rn to next array cell and move on
                    rn = rn + 1
                Else
                    'I want this string so put in new array holder and increment array counter
                    arPutNames(rhn) = arHoldNames(rn)
                    rhn = rhn + 1
                End If
            Next 'Loop through array holding origional string
            'I have what I want, so put it back in relevant cell in ws
            NamCel(i, 1) = Join(arPutNames, ",")
            'Remove all trailing commas
            While Right(NamCel(i, 1), 1) = ","
                NamCel(i, 1) = Left(NamCel(i, 1), Len(NamCel(i, 1)) - 1)
            Wend

            rhi = 0
            'Load list of names into string holder
            strHoldIPs = IpCel(i, 1)
            'Split the string into the holding array based on the comma
            arHoldIPs = Split(strHoldIPs, ",")
            'Loop through the array to find those names not containg unwanted data
            ReDim arPutIPs(0 To UBound(arHoldIPs))
            For ri = 0 To UBound(arHoldIPs)
                If InStr(1, arHoldIPs(ri), txt106, vbTextCompare) > 0 Or InStr(1, arHoldIPs(ri), txt192168, vbTextCompare) > 0 Then
                    'If it contains unwanted string, increment ri to next array cell and move on
                    ri = ri + 1
                Else
                    'I want this string so put in new array holder and increment array counter
                    arPutIPs(rhi) = arHoldIPs(ri)
                    rhi = rhi + 1
                End If
            Next 'Loop through array holding origional string
            'I have what I want, so put it back in relevant cell in ws
            IpCel(i, 1) = Join(arPutIPs, ",")
            'Remove all trailing commas
            While Right(.Cells(i, Col_IPs).Value, 1) = ","
                IpCel(i, 1) = Left(IpCel(i, 1), Len(IpCel(i, 1)) - 1)
            Wend
        
        Next 'Move to next row and repeat untill end
    
        ' Range-array copy the data back out to Excel
        .Range(Col_Names & StrtRow & ":" & Col_Names & lastrowN) = NamCel
        .Range(Col_IPs & StrtRow & ":" & Col_IPs & lastrowI) = IpCel
    End With 'End main

End Sub

谢谢大家。一如既往的好帮手。

脚本在我的笔记本电脑上运行速度快了大约 120 倍,有 8,323 行(完成这一切需要 2 秒多)。所以在我看来,它会更快。

【讨论】:

    【解决方案2】:

    这是一个使用 Range-Array 复制的示例:

    'Sub to remove unwanted IP ranges from both columns
    Sub CleanNamesIPsfromList()
    
        Const Col_Names = "J"
        Const Col_IPs = "K"
        Const ROW_NamesIPs = 5 'First line of Names and IP's
    
        Dim wb As Workbook
        Dim wsMain As Worksheet
        Dim arHoldNames() As String, arHoldIPs() As String
        Dim arPutNames() As String, arPutIPs() As String
        Dim strHoldNames As String, strHoldIPs As String
        Dim txt106 As String, txt192168 As String
        Dim rn As Long, rhn As Long, ri As Long, rhi As Long, lastrowN As Long, lastrowI As Long, i As Long
        
        txt106 = "10.6."
        txt192168 = "192.168."
    
        Set wb = ActiveWorkbook
        With wb
            Set wsMain = .Sheets("Main")
        End With
        
        Dim NamCel As Variant
        Dim IpCal As Variant
    
        With wsMain
            lastrowN = .Cells(.Rows.Count, Col_Names).End(xlUp).Row
            lastrowI = .Cells(.Rows.Count, Col_IPs).End(xlUp).Row
            If lastrowN < ROW_NamesIPs Or lastrowI < ROW_NamesIPs Then
                MsgBox "No text found in Columns " & Col_Names & " " & Col_IPs, vbCritical
                Exit Sub
            End If
            
            ' Range-array copy the data into VBA
            NamCel = .Range(Col_Names & "1:" & Col_Names & lastrowN)
            IpCel = .Range(Col_IPs & "1:" & Col_IPs & lastrowI)
    
            'Run through whole ws row by row in both columns
            For i = ROW_NamesIPs To lastrowN
                rhn = 0
                'Load list of names into string holder
                'strHoldNames = .Cells(i, Col_Names).Value
                strHoldNames = NamCel(i, 1)
                'Split the string into the holding array based on the comma
                arHoldNames = Split(strHoldNames, ",")
                'Loop through the array to find those names not containg unwanted data
                ReDim arPutNames(0 To UBound(arHoldNames))
                For rn = 0 To UBound(arHoldNames)
                    If InStr(1, arHoldNames(rn), txt106, vbTextCompare) > 0 Or InStr(1, arHoldNames(rn), txt192168, vbTextCompare) > 0 Then
                        'If it contains unwanted string, increment rn to next array cell and move on
                        rn = rn + 1
                    Else
                        'I want this string so put in new array holder and increment array counter
                        arPutNames(rhn) = arHoldNames(rn)
                        rhn = rhn + 1
                    End If
                Next 'Loop through array holding origional string
                'I have what I want, so put it back in relevant cell in ws
                '.Cells(i, Col_Names).Value = Join(arPutNames, ",")
                NamCel(i, 1) = Join(arPutNames, ",")
                'Remove all trailing commas
                While Right(NamCel(i, 1), 1) = ","
                    NamCel(i, 1) = Left(NamCel(i, 1), Len(NamCel(i, 1)) - 1)
                Wend
    
                rhi = 0
                'Load list of names into string holder
                strHoldIPs = IpCel(i, 1)
                'Split the string into the holding array based on the comma
                arHoldIPs = Split(strHoldIPs, ",")
                'Loop through the array to find those names not containg unwanted data
                ReDim arPutIPs(0 To UBound(arHoldIPs))
                For ri = 0 To UBound(arHoldIPs)
                    If InStr(1, arHoldIPs(ri), txt106, vbTextCompare) > 0 Or InStr(1, arHoldIPs(ri), txt192168, vbTextCompare) > 0 Then
                        'If it contains unwanted string, increment ri to next array cell and move on
                        ri = ri + 1
                    Else
                        'I want this string so put in new array holder and increment array counter
                        arPutIPs(rhi) = arHoldIPs(ri)
                        rhi = rhi + 1
                    End If
                Next 'Loop through array holding origional string
                'I have what I want, so put it back in relevant cell in ws
                IpCel(i, 1) = Join(arPutIPs, ",")
                'Remove all trailing commas
                While Right(.Cells(i, Col_IPs).Value, 1) = ","
                    IpCel(i, 1) = Left(IpCel(i, 1), Len(IpCel(i, 1)) - 1)
                Wend
            
            Next 'Move to next row and repeat untill end
        
            ' Range-array copy the data back out to Excel
            .Range(Col_Names & "1:" & Col_Names & lastrowN) = NamCel
            .Range(Col_IPs & "1:" & Col_IPs & lastrowI) = IpCel
        End With 'End main
    
    End Sub
    

    【讨论】:

    • 好的,它的工作速度更快。我修复了一个“下标超出范围”,该死的,它很快:) 我将回答我的问题并将新代码放在那里。我学到了一些新东西,并将在我以后的编码中暗示它。非常感谢@RBarryYoung。标记你的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多