Excel VBA:从数字字段WITHOUT循环中删除非数字字符

时间:2016-08-05 18:47:49

标签: excel-vba vba excel

我已经阅读了一些主题,解释了其中一个可行的方法,但效果会非常慢。解释如下:

https://www.extendoffice.com/documents/excel/651-excel-remove-non-numeric-characters.html

它涉及遍历范围中的每个单元格,然后遍历字段中的字符,如果它们与[0-9]不匹配则删除它们。我想知道是否还有其他任何更有效的建议。

我想到的是将单元格内容加载到数组中,迭代它,并将每个条目拆分成自己的数组以进行迭代。

3 个答案:

答案 0 :(得分:2)

无需 VBA 循环。 excel 公式可以达到您想要的效果。

=NPV(-0.9,,IFERROR(MID(A1,1+LEN(A1)-ROW(OFFSET(A$1,,,LEN(A1))),1)%,""))

这是一个数组公式。您必须按 Ctrl + Shift + Enter

enter image description here

解释

每个字词乘以(1+rate)^n的倒数,其中n是系列中的nth字词。

通过使用不同的费率值,我们可以得到不同的结果。在这种情况下,使用-0.9会向我们提供1 + rate = 1 + -0.9 = 0.1

Result: {0.1;0.01;0.001;0.0001;0.00001}
Inverse of above: {10;100;1000;10000;100000}
Also NPV skips text values which contributes to the above

免责声明:我没有想出这个公式。我很久以前就看过这个公式,只是爱上了它。从那以后它就成了我数据库的一部分。

答案 1 :(得分:2)

对于VBA方面(注意循环),我决定满足自己对几种不同方法的性能的好奇心。所有这些都将范围拉入阵列并在其中进行处理。由于读取和写入单个单元格值的开销,链接的文章将被任何中的速度所扼杀。

对于第一种方法,我优化了linked article" a bit"中的代码:

Private Sub MidMethod(values() As Variant)
    Dim r As Long, c As Long, i As Long
    Dim temp As String, output As String

    For r = LBound(values, 1) To UBound(values, 1)
         For c = LBound(values, 2) To UBound(values, 2)
            output = vbNullString
            For i = 1 To Len(values(r, c))
                temp = Mid$(values(r, c), i, 1)
                If temp Like "[0-9]" Then
                    output = output & temp
                End If
            Next
            values(r, c) = output
         Next
    Next
End Sub

对于我使用RegExp.Replace的第二种方法:

Private Sub RegexMethod(values() As Variant)
    Dim r As Long, c As Long, i As Long

    With New RegExp
        .Pattern = "[^0-9]"
        .MultiLine = True
        .Global = True
        For r = LBound(values, 1) To UBound(values, 1)
             For c = LBound(values, 2) To UBound(values, 2)
                values(r, c) = .Replace(values(r, c), vbNullString)
             Next
        Next
    End With
End Sub

最后,对于最后一种方法,我使用了Byte数组:

Private Sub ByteArrayMethod(values() As Variant)
    Dim r As Long, c As Long, i As Long
    Dim chars() As Byte

    For r = LBound(values, 1) To UBound(values, 1)
         For c = LBound(values, 2) To UBound(values, 2)
            chars = values(r, c)
            values(r, c) = vbNullString
            For i = LBound(chars) To UBound(chars) Step 2
                If chars(i) > 47 And chars(i) < 58 Then
                    values(r, c) = values(r, c) & Chr$(chars(i))
                End If
            Next
         Next
    Next
End Sub

然后我使用此代码对1000个单元格进行基准测试,每个单元格包含25个字母和数字的随机混合:

Private Sub Benchmark()
    Dim data() As Variant, start As Double, i As Long

    start = Timer
    For i = 1 To 5000
        data = ActiveSheet.Range("A1:J100").Value
        MidMethod data
    Next
    Debug.Print "Mid: " & Timer - start

    start = Timer
    For i = 1 To 5000
        data = ActiveSheet.Range("A1:J100").Value
        RegexMethod data
    Next
    Debug.Print "Regex: " & Timer - start

    start = Timer
    For i = 1 To 5000
        data = ActiveSheet.Range("A1:J100").Value
        ByteArrayMethod data
    Next
    Debug.Print "Byte(): " & Timer - start

End Sub

结果并非令人惊讶 - Regex方法 到目前为止 最快(但它们都不是我所称的&# 34;快速&#34):

Mid: 24.3359375 
Regex: 8.31640625 
Byte(): 22.5625

请注意,我不知道这与@ SiddharthRout的酷公式方法相比如何,因为我无法通过我的测试工具运行它。 www.extendoffice.com代码也可能仍在运行,所以我没有测试它。

答案 2 :(得分:0)

使用正则表达式(您需要在工具参考下的Microsoft VBScript正则表达式库5.5):

Public Function GetNumericValue(range)
    Set myRegExp = New RegExp
    myRegExp.IgnoreCase = True
    myRegExp.Global = True
    myRegExp.Pattern = "[\D]"

    GetNumericValue = myRegExp.Replace(range.value, "")
End Function
相关问题