如果在大型数据集上使用Excel VBA循环执行速度非常慢,然后崩溃
我不是一名开发人员,但我在这里和那里读了一些能够理解它的一些。 这可能是我面对的一个简单的问题,但我似乎无法弄清楚。 所以谢谢你帮助我!
我在Google的帮助下写了一个简短的脚本,该脚本可以将CSV导出转换为可读格式。 它应该做更多的事情,但我已经面临性能问题,只是为了使一些条目可读。
以下是我到目前为止:
Sub MagicButton_Click() 'Find the last non-empty cell in column A Dim LastRow As Long LastRow = Range("A" & Rows.Count).End(xlUp).Row 'Set Variables to work with the cell content Dim CellContent As String Dim CellContentArr As Variant 'Set looping variables Dim i, j As Integer Dim FirstRow As Integer Dim FirstCol As Integer Dim ActiveCol As Integer Dim itm As Variant FirstRow = 1 FirstCol = 2 Dim x, y As String 'Loop (1) through all rows For i = FirstRow To LastRow 'Save cell content to string CellContent = ActiveSheet.Cells(i, 1).Text 'Split string into array CellContentArr = Split(CellContent, "{") 'Reset column ActiveCol = FirstCol 'Loop (2) through the array For Each itm In CellContentArr 'Remove quotations and other symbols itm = Application.WorksheetFunction.Clean(itm) itm = Replace(itm, """", "") 'This is the part that creates performance issues 'For j = 1 To Len(itm) ' x = Mid(itm, j, 1) ' If x Like "[AZ,az,0-9 :.-]" Then ' y = y & x ' End If 'Next j 'itm = y 'y = "" 'Write each item in array to an individual cells within the same row ActiveSheet.Cells(i, ActiveCol) = itm ActiveCol = ActiveCol + 1 Next itm Next i End Sub
当我testing~10行时,整个脚本工作正常。 当在整个220行上使用它时,它变得没有反应并最终崩溃。
在脚本中,我评论了导致此性能问题的原因。 我猜这是因为有三个循环。 第三个循环遍历string中的每个字符,以检查它是否为允许的字符,然后保留或删除它。
我可以做些什么来提高性能,或者至less使Excel能够不响应?
旁注:它应该在Mac和Windows上都可以工作。 我不知道如果正则expression式会有更好的性能来过滤不需要的字符,但我也不知道是否有可能用于Mac和Windows。
三件事 – 你需要禁用screenupdating,你需要更好地声明variables。 不要像“Dim a,b,c,d,e as Integer”那样做,因为只有最后一个是整数,其他的是不同的。 最后但并非最不重要,不要在VBA中使用Integer,但这不是你的问题。
这应该工作得更快:
Sub MagicButton_Click() 'Find the last non-empty cell in column A Dim LastRow As Long LastRow = Range("A" & Rows.Count).End(xlUp).Row 'Set Variables to work with the cell content Dim CellContent As String Dim CellContentArr As Variant 'Set looping variables Dim i As Long dim j as Long Dim FirstRow As Long Dim FirstCol As Long Dim ActiveCol As Long Dim itm As Variant FirstRow = 1 FirstCol = 2 Dim x as string dim y As String call onstart 'Loop (1) through all rows For i = FirstRow To LastRow 'Save cell content to string CellContent = ActiveSheet.Cells(i, 1).Text 'Split string into array CellContentArr = Split(CellContent, "{") 'Reset column ActiveCol = FirstCol 'Loop (2) through the array For Each itm In CellContentArr 'Remove quotations and other symbols itm = Application.WorksheetFunction.Clean(itm) itm = Replace(itm, """", "") 'This is the part that creates performance issues 'For j = 1 To Len(itm) ' x = Mid(itm, j, 1) ' If x Like "[AZ,az,0-9 :.-]" Then ' y = y & x ' End If 'Next j 'itm = y 'y = "" 'Write each item in array to an individual cells within the same row ActiveSheet.Cells(i, ActiveCol) = itm ActiveCol = ActiveCol + 1 Next itm Next i call onend End Sub Public Sub OnStart() Application.AskToUpdateLinks = False Application.ScreenUpdating = False Application.Calculation = xlAutomatic Application.EnableEvents = False Application.DisplayAlerts = False End Sub Public Sub OnEnd() Application.DisplayAlerts = True Application.ScreenUpdating = True Application.EnableEvents = True Application.StatusBar = False Application.AskToUpdateLinks = True End Sub
已经给出的答案将是你的代码的很好的调整。 但是,这可能有更好的方法。
首先,将一个范围读入一个数组并操作所产生的数组显然比逐个读取更快。
其次,如果你正在迭代你的数组中的每个字符,并用一个大括号指定一个新列来检查特定项目,那么你能不能在一次迭代中完成这一切。 先拆分和清理似乎有点多余。
总而言之,你的代码可以像这样简单:
Dim lastCell As Range Dim v As Variant Dim r As Long Dim c As Long Dim i As Integer Dim output() As String Dim b() As Byte 'Read the values into an array With ThisWorkbook.Worksheets("Sheet1") Set lastCell = .Cells(.Rows.Count, "A").End(xlUp) v = .Range(.Cells(1, "A"), lastCell).Value2 End With ReDim output(1 To UBound(v, 1), 1 To 1) 'Loop through the array rows and characters For r = 1 To UBound(v, 1) c = 1 'Convert item to byte array - just personal preference, you could iterate a string b = StrConv(v(r, 1), vbFromUnicode) For i = 0 To UBound(b) Select Case b(i) Case 45, 46, 58, 65 To 90, 97 To 122, 48 To 57 '-, :, ., AZ, az, 0-9 output(r, c) = output(r, c) & Chr(b(i)) Case 123 '{ 'add a column and expand output array if necessary If Len(output(r, c)) > 0 Then c = c + 1 If c > UBound(output, 2) Then ReDim Preserve output(1 To UBound(v, 1), 1 To c) End If End If Case Else 'skip it End Select Next Next 'Write item to worksheet ThisWorkbook.Worksheets("Sheet1").Cells(1, "B") _ .Resize(UBound(output, 1), UBound(output, 2)).Value = output
任务列表
- 将源范围复制到数组中
- 清洁arrays
- 将数组复制回源范围
- 使用TextToColumns将数据拆分成多个列
Sub MagicButton_Click2() 昏暗arData Dim LastRow As Long,i As Integer Dim dataRange As Range LastRow = Range(“A”&rowS.Count).End(xlUp).Row Set dataRange = Range(Cells(1,1),Cells(LastRow,1)) arData = dataRange.value 对于i = 1到UBound(arData) arData(i,1)= AlphaNumericOnly(CStr(arData(i,1))) 下一个 dataRange.value = arData dataRange.TextToColumns目标:=范围(“A1”),数据types:= xlDelimited,_ TextQualifier:= xlDoubleQuote,ConsecutiveDelimiter:= False,Tab:= True,_ 分号:= False,逗号:= False,空格:= False,其他:= True,OtherChar _ :=“{”,TrailingMinusNumbers:= True 结束小组 'http://stackoverflow.com/questions/15723672/how-to-remove-all-non-alphanumeric-characters-from-a-string-except-period-and-sp 函数AlphaNumericOnly(strSource作为string)作为string 昏暗我作为整数 Dim strResult As String 对于i = 1到Len(strSource) selectCase Asc(Mid(strSource,i,1)) 情况48到57,65到90,97到123:“包括32,如果你想包括空间,我加123包括{ strResult = strResult&Mid(strSource,i,1) 结束select 下一个 AlphaNumericOnly = strResult 结束function