【问题标题】:Converting Numbers Stored as Text to Numbers with For Each Statement使用 For Each 语句将存储为文本的数字转换为数字
【发布时间】:2017-03-23 22:53:13
【问题描述】:

我正在尝试将存储在文本中的数字转换为多个工作表上的数字。我的问题是我拼凑的代码似乎花费了过多的时间。我正在使用循环遍历必要的工作表和范围的 For Each 语句。它不会使 Excel 崩溃,它只是看似永远运行。

Sub ConvertTextToNumber()
    Application.ScreenUpdating = False
    Dim WshtNames As Variant
    Dim WshtNameCrnt As Variant
    Dim r As Range

    WshtNames = Array("Financial Data", "Site Data ", "Org Data", "Program Data")

    For Each WshtNameCrnt In WshtNames
    On Error Resume Next
        For Each r In Worksheets(WshtNameCrnt).UsedRange.SpecialCells(xlCellTypeConstants)
            If IsNumeric(r) Then r.Value = Val(r.Value)
        Next
    Next
    Application.ScreenUpdating = False
End Sub

当我停止运行脚本并单击“调试”时,它似乎被第一个 Next 语句所吸引。我认为我用来转换值的方法比必要的时间更密集,因此在多张纸上运行它甚至更糟。

我愿意接受任何和所有让这个过程运行得更快的建议。提前致谢!

【问题讨论】:

  • 查看使用的最后一行和最后一列,将所有内容加载到数组中,进行更改,然后将值放回原处。它会加快速度。因为它是你循环通过每个单元测试它并改变它。这将需要很长时间。数组会以指数方式加速它。
  • 这肯定会帮助我想象。我知道我的数据不会超过某个点,我的更新对我有用。但是,如果我有时间,我会更新代码以简单地找到最后一行和最后一列并将其发布在这里。谢谢!
  • 不要把它放在问题中,而是把新代码作为答案,就这样。
  • 对不起!我很新,还没有机会提供我自己的答案。感谢您的提醒!

标签: vba excel


【解决方案1】:

试试下面的代码。我使用了索引号,而不是尝试使用变体遍历数组。我可能是错的,但我认为 For Each 仅适用于集合。如果我错了,请有人纠正我。 (编辑:我确实弄错了。For Each 在这里工作得很好。)

无论如何,数组上的索引号是最佳做法。

我还删除了您的 Resume Next 并妥善处理。我强烈建议不要使用 Resume Next。我想不出任何 Resume Next 不能被好的逻辑所取代的事件。

Sub ConvertTextToNumber()
    Application.ScreenUpdating = False

    ' These two statements should further improve processing time.
    ' The first prevents formulas from calculating. The second prevents
    ' any background events from firing (mostly for Event triggered macros).
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False
    Dim WshtNames As Variant
    Dim i as Long
    Dim r As Range

    WshtNames = Array("Financial Data", "Site Data ", "Org Data", "Program Data")

    ' When looping over an array use an index number.
    ' I this case, 'i' will go from the lowest range of the array
    ' all the way through to the highest range of the array.
    For i = LBound(WshtNames) to Ubound(WshtNames)
        'On Error Resume Next ' It is best to catch the errors, dont just skip them.
        If Not Worksheets(WshtNames(i)) Is Nothing Then
            For Each r In Worksheets(WshtNames(i)).UsedRange.SpecialCells(xlCellTypeConstants)
                ' No need to check for an empty string here since
                ' IsNumeric() will return false for non-numbers.
                If IsNumeric(r) Then r.Value = Val(r.Value)
            Next
        Else
            ' Put your error handling in here, or you can just skip it
            ' I tend to use debug.print just to keep track.
            Debug.Print WshtNames(i) & " doesn't exist."
        End If
    Next
    Application.ScreenUpdating = True

    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
End Sub

【讨论】:

  • 我在没有将计算置于手动和禁用事件中的情况下进行了尝试,它花了同样长的时间,但加上这两个,它现在运行大约 30 秒,比光年好很多。谢谢你的帮助!另外,我保留了 Debug.Print 选项。我通常不使用它,但这是针对最终用户的,因此向他们提供有关问题的对话是很好的建议。
  • 很高兴听到这个消息!计算事件可能是主要的性能提升。请记住,您的最终用户可能永远不会看到 debug.print,除非他们也在编写 VBA。您可以通过使用消息框来克服这个问题,但是您的工作表名称是硬编码的,所以这不太理想。
【解决方案2】:

我最初使用 Brandon 的答案,但根据他的建议,我研究了使用数组来存储值并在内存中进行更改。以下是我现在使用的更新代码:

Sub ConvertTextToNumber()
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False
    Dim WshtNames As Variant
    Dim DataRange As Variant
    Dim r As Range
    Dim i As Long
    Dim lrow As Long
    Dim lcol As Integer
    Dim MyVar

    WshtNames = Array("Financial Data", "Site Data ", "Org Data", "Program Data")
    DataRange = Range("A1:FZ6000").Formula

    For lrow = 1 To 6000
        For lcol = 1 To 156
        MyVar = DataRange(lrow, lcol)
        If IsNumeric(MyVar) Then
            MyVar = Val(MyVar)
            DataRange(lrow, lcol) = MyVar
        End If
        Next lcol
    Next lrow
    Range("A1:FZ6000").Formula = DataRange

    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
End Sub

这对我有用,因为我知道我的工作表永远不会超出我根据数据性质选择的范围。这有效地将我的计算时间减少到约 2 秒。感谢大家的投入和愉快的编码!

【讨论】:

    【解决方案3】:

    这是因为您的循环实际上是遍历每个单元格。最好对空白值做一些检查

     IF r.Value <> "" or r.value <> vbnullstring then
    

    【讨论】:

    • "" 和vbNullstring是一样的,所以最好使用'If r.Value vbNullString then'。
    • @BrandonBarney 实际上不是。空值与空白是两个不同的东西,处理方式也不同。对于 excel 尤其如此。
    • 空白和“”是两个不同的东西。 vbNullString 是“”的常量表达式。您甚至可以使用即时窗口检查两者之间的相等性(Debug.Print "" = vbNullString 返回 True)。现在,空格字符是一个完全不同的故事,当然处理方式也不同,但“”不是空格。您可能对此感兴趣:stackoverflow.com/questions/32435320/….
    • @BrandonBarney 伙计,我正要发布一张照片来证明我的观点,而你不得不走更高的路毁了我的时刻。 :)
    • OP 在这里,实际上我一直在阅读此交流,因为我一直在测试两种提议的解决方案。 '""' 和 'vbNullString' 之间的区别当然很有趣。不过,在我的困境中,这个解决方案似乎完全符合我的做法,而实际上并没有花费更少的时间。我想我可能只需要跳过进行此更改。感谢您的尝试!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多