Помним, что Val некорректно преобразует, если число в виде текста содержит в качестве десятичного разделителя запятую Преобразуем двойным отрицанием, а если ошибка, то преобразуем с помощью Val (для данных из примера) Региональные и Excel'евские настройки не учитывались. Офис на русской локали. Инфа под аватаром
Код
Код
Option Explicit
Option Private Module
'====================================================================================================
Sub Transform()
Dim x, vD, vC, sD$, sC$, d#, t!, n&
Const nMax& = 10000000 ' 10 mln
sD = "0.5": vD = sD ' D = Dot
sC = "0,5": vC = sC ' C = Comma
' проверяем CDbl, двойное отрицание и присвоение числовой переменной на скорость (только "0,5", потому что "0.5" вызовет ошибку)
' CDbl
t = Timer
For n = 1 To nMax
x = CDbl(sC)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = CDbl(sC)"
t = Timer
For n = 1 To nMax
x = CDbl(vC)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = CDbl(vC)"
' --
t = Timer
For n = 1 To nMax
x = --sC
Next n
Debug.Print Timer - t, x, TypeName(x), "x = --sC"
t = Timer
For n = 1 To nMax
x = --vC
Next n
Debug.Print Timer - t, x, TypeName(x), "x = --vC"
' d#
t = Timer
For n = 1 To nMax
d# = sC
Next n
Debug.Print Timer - t, x, TypeName(x), "d# = sC"
t = Timer
For n = 1 To nMax
d# = vC
Next n
Debug.Print Timer - t, d, TypeName(x), "d# = vC"
' проверяем Val на скорость ("0.5" преобразует корректно в 0.5, а "0,5" преобразует в 0, т.к. запятая не является числовым символом)
t = Timer
For n = 1 To nMax
x = Val(sD)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = Val(sD)"
t = Timer
For n = 1 To nMax
x = Val(vD)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = Val(vD)"
t = Timer
For n = 1 To nMax
x = Val(sC)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = Val(sC)"
t = Timer
For n = 1 To nMax
x = Val(vC)
Next n
Debug.Print Timer - t, x, TypeName(x), "x = Val(vC)"
End Sub
'====================================================================================================
Первый вариант некорректно отработает из-за нюансов функции Val() Второй вариант отрабатывает корректно
Используется пошаговый выход: после каждого преобразования производится попытка получения числа с последующим выходом — это будет работать чуть медленнее, чем полная обработка и только одно итоговое преобразование в число, зато даст выигрыш на данных, не требующих большой обработки Учтена частота встречающихся типов данных "чисел-как-текст" (личный опыт) и сначала проверяются более частые
1. Работает до 10ти (!!!) раз медленнее первого варианта 2. Использует библиотеку BedvitCOM от bedvit'а (можно заменить штатными, но будет медленнее)
Код
Код
Option Explicit
Option Private Module
'====================================================================================================
Public BV As New BedvitCOM.VBA ' https://bedvit.ru/xll/
'====================================================================================================
Sub Transform()
Dim x, sD$, sC$, t!, n&
Const nMax& = 1000000 ' 1 mln
sD = "0.5" ' D = Dot
sC = "0,5" ' C = Comma
t = Timer
For n = 1 To nMax
x = sD: PRDX_ToNumeric x
Next n
Debug.Print "sD", Timer - t, TypeName(x), x
t = Timer
For n = 1 To nMax
x = sC: PRDX_ToNumeric x
Next n
Debug.Print "sC", Timer - t, TypeName(x), x
End Sub
'====================================================================================================
'====================================================================================================
' Функция пытается преобразовать аргумент в число и возвращает итог попытки
Function PRDX_ToNumeric(v, Optional AllowExp As Boolean, Optional MsgTrue As Boolean, Optional MsgFalse As Boolean) As Boolean
Dim x$, v2$, tmp, l&, i&, f As Boolean
Static DScur$, DSoth$, fStatic As Boolean
If Not fStatic Then
fStatic = True: DScur = Application.International(xlDecimalSeparator)
If DScur = "," Then DSoth = "." Else DSoth = ","
End If
If v = 0 Then GoTo yes
l = Len(v): If l = 0 Then v = 0: GoTo yes ' вычисляем длину переменной. Если нолевая, то преобразуем в 0 и выходим
If l > 20 Then GoTo no ' если длина переменной больше 20, то выходим
If AllowExp Then x = v Else x = LCase$(v)
On Error Resume Next: tmp = --x: On Error GoTo 0: If tmp <> Empty Then GoTo nxExp ' пробуем преобразовать и переходим к проверке экспоненциального вида, если получилось
v2 = BV.Replace(x, DSoth, DScur) ' заменяем "другой" десятичный разделитель на текущий
If v2 <> x Then ' если замена была произведена …
x = v2
On Error Resume Next: tmp = --x: On Error GoTo 0: If tmp <> Empty Then GoTo nxExp ' пробуем преобразовать
End If
v2 = BV.FilterUnicodeChar(x, , " ") ' удаляем неразрывные и обычные пробелы
If v2 <> x Then ' если замена была произведена …
x = v2
On Error Resume Next: tmp = --x: On Error GoTo 0: If tmp <> Empty Then GoTo nxExp ' пробуем преобразовать
End If
GoTo no
nxExp: ' проверка экспоненцеального вида
If Not AllowExp Then
f = (BV.InStr(x, "e") <> 0)
If Not f Then f = (BV.InStr(x, "d") <> 0)
If f Then
If MsgFalse Then MsgBox "The Value" & vbLf & vbLf & v & vbLf & vbLf & "looks like the EXPONENTIAL!", vbExclamation, "ToNumeric"
Exit Function
End If
End If
yes: If MsgTrue Then MsgBox "The Value" & vbLf & vbLf & v & vbLf & vbLf & "now is Numeric!", vbExclamation, "ToNumeric"
PRDX_ToNumeric = True: v = tmp: Exit Function
no: If MsgFalse Then MsgBox "The Value" & vbLf & vbLf & v & vbLf & vbLf & "CAN'T be transform to Number!", vbExclamation, "ToNumeric"
End Function
'====================================================================================================
'====================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, сложно сравнить, когда у тебя готовый инструмент для работы с данными НА ЛИСТЕ. То есть ты берёшь, данные, обрабатываешь и выгружаешь обратно, а меня интересует только обработка
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, конечно буду - отрыв будет очень сильный, вангую Я больше хотел бы алгоритмы сравнить
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄