Столкнулся с проблемой вытащить некоторые числа из строки. Тем много - но нормального решения так и не нашел. Написал свою функцию. Прошу привести примеры на Regexp для достижения такого же результата, чтобы убедится- есть ли смысл доделывать дальше свою функцию.
Поверь, RegExp будут лучше, но надо уметь писать паттерны (шаблоны).... Ты пишешь - вытащить некоторые числа из строки: "Price 1 000 000.25 руб 2 500,325$ aed....1754-0025" какие числа? Я вот, например, не понял, какое именно число ты хочешь достать из строки (какую группу чисел) - все? 1-ю? 2-ю? 3-ю?
где 3 числа в твоём файле? можно более конкретно - номера ячеек, либо пальцами набери на клавиатуре, какие 3 числа ты вытащил? Я из этой строки "Price 1 000 000.25 руб 2 500,325$ aed....1754-0025" - могу вытащить 23 числа
Цитата
Marat Ta написал: Просто привык к более простому и понятному алгоритму решения задач.
Function exec(t, n)
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "([\d\s]+)([-\.,]+)(\d+)"
Set s = .Execute(t)(n - 1)
End With
exec = CDec(s.SubMatches(0) & Application.DecimalSeparator & s.SubMatches(2))
End Function
Marat Ta, 1. buchlotnik тоже может ) 2. Как видишь RegExp - универсальнее 3. как я и сказал - тут главное уметь писать паттерны/шаблоны... 4. сравни длину своей функции и код от buchlotnik
P.S. buchlotnik - спасибо, не дадим в обиду RegExp)
Function iDigits(cell$)
Dim j As Integer
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "[\d ]+\.[\d]+(?= руб)"
If .test(cell) Then
Set iDigits = .Execute(cell)(0)
iDigits = CDbl(Replace(Replace(iDigits, " ", ""), ".", ","))
Else
iDigits = ""
End If
End With
End Function
New написал: первый вариант мне больше нравился...
он под десятичную запятую, а мало ли что у кого в системе; да и вообще писал в лоб - логику примера не сразу уловил, так что лучше удали, не учи ТС-а плохому... )))
сравнивать RegExp с вашим макросом, это как водку сравнивать с пивом. очевидно, что водка гораздо эффективнее. а если вот это: Price 1 000 000.25 руб 2 500,325$ aed....1754-0025 записать так: Price 1 000 000.25 руб 2 500,325$ aed....1754-0025 + 3руб или -47 3 или -47 это не числа? (судя по названию темы, пытаемся из текста извлечь числа))
Код
Function GetNum$(t, n)
Dim re, ms, s$
Set re = CreateObject("VBScript.RegExp"): re.Global = True
re.Pattern = "(-*\d[0-9 ]*)(\D{0,1})(\d)*": Set ms = re.Execute(t)
If ms.Count < n Then Exit Function Else Set ms = ms(n - 1)
If IsEmpty(ms.Submatches(2)) Then s = ms.Submatches(0) _
Else s = Replace(ms, ms.Submatches(1), Application.DecimalSeparator)
GetNum = Replace(s, " ", "")
End Function
New, на самом деле, ответ уже был в функции RealNum (нужно было просто добавить нужные разделители). Просто для проверки знаний форумчан...) Да и в функции, взятой из https://www.planetaexcel.ru/techniques/7/4844 все подробно описано.
теперь можно потестировать мою функцию и писать сюда, какое число не было извлечено из текста или было извлечено не правильно т.е. выкладывать сюда текст, в котором произошла ошибка в работе функции сразу хочу сказать что в тексте 1 2 3 4 5 будет извлечено ОДНО число 12345 спасибо
Фишка ещё в том, что хоть регулярки и универсальнее, конкретную задачу функция ТС-а решала быстрее почти в три раза, но что было в приоритете скорость или универсальность мы может быть узнаем через месяц, спасибо дядя модератор за наше счастливое детство ))))
Соблюдение правил форума не освобождает от модераторского произвола
как раз за месяц автор темы возможно сможет заточить свою функцию до тех возможностей извлечения чисел из текста, которые уже работают в функции из #15
buchlotnik написал: конкретную задачу функция ТС-а решала быстрее почти в три раза
Привет, Михаил. А у меня наоборот получилось - регулярки в два раза быстрее
Цитата
1,058594 0,53125
Скрытый текст
Код
Public Function GetNumber(ByVal fromText As String) As Double
If FReg Is Nothing Then
Set FReg = New VBScript_RegExp_55.RegExp
FReg.Global = True
End If
FReg.Pattern = "[\.\-]"
fromText = FReg.Replace(fromText, ",")
FReg.Pattern = "(?:\d+ ?)+,\d+"
GetNumber = CDbl(FReg.Execute(fromText)(0).Value)
End Function
Public Sub Test()
Const repeatCount As Long = 100000
Dim v As Double, i As Long, t As Single, s As String
s = "Price 1 000 000.25 руб 2 500,325$ aed....1754-0025 "
t = Timer
For i = 1 To repeatCount
v = IsDec(s)
Next
Debug.Print Timer - t
t = Timer
For i = 1 To repeatCount
v = GetNumber(s)
Next
Debug.Print Timer - t
End Sub
Function exec(t, n)
If FReg Is Nothing Then
Set FReg = New VBScript_RegExp_55.RegExp
FReg.Global = True
End If
With FReg
.Pattern = "([\d\s]+)([-\.,]+)(\d+)"
Set s = .Execute(t)(n - 1)
End With
exec = CDec(s.SubMatches(0) & Application.DecimalSeparator & s.SubMatches(2))
End Function
и при небольшом допиле вашего кода:
Код
Public Function GetNumber(ByVal fromText As String, n) As Double
If FReg Is Nothing Then
Set FReg = New VBScript_RegExp_55.RegExp
FReg.Global = True
End If
FReg.Pattern = "[\.\-]"
fromText = FReg.Replace(fromText, ",")
FReg.Pattern = "(?:\d+ ?)+,\d+"
GetNumber = CDbl(FReg.Execute(fromText)(n - 1).Value)
End Function
…при Option Explicit и большом вызове RE-функций, я делаю так:
Код
Public Function GetNumber(ByVal fromText As String, n) As Double
Static FReg as RegExp, fStatic as Boolean
If Not fStatic Then
fStatic=True
Set FReg = New RegExp
FReg.Global = True
FReg.Pattern = "([\d\s]+)([-\.,]+)(\d+)"
End If
Попробуйте так - должно быть заметно быстрее при частом вызове функции в цикле Про зверя VBScript_RegExp_55 и особенности в сравнении с "обычным" RegExp - не слышал
надёжнее использовать Application.International(xlDecimalSeparator) или Mid$(1/2,2,1)и тоже в Static засунуть, чтобы не вычислять при каждом вызове (я так и не понял - вошло или нет в сборку, так что просто инфа)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Option Explicit
Option Private Module
'====================================================================================================
Sub Test()
Dim x, s$, t!, n&
s = "Price 1 000 000.25 ??? 2 500,325$ aed....1754-0025 "
t = Timer
For n = 1 To 100000
' x = AVG(s) '8.8 sec
x = AVG2(s) '0.58 sec
' x = AVG3(s) '0.32 sec
Next n
Debug.Print Format(Timer - t, "0.00 sec"), x, TypeName(x)
End Sub
'====================================================================================================
Public Function AVG(ByVal tx$) As Double
Dim RE As RegExp
If RE Is Nothing Then
Set RE = New RegExp
RE.Global = True
End If
RE.Pattern = "[\.\-]"
tx = RE.Replace(tx, ",")
RE.Pattern = "(?:\d+ ?)+,\d+"
AVG = CDbl(RE.Execute(tx)(0).Value)
End Function
'====================================================================================================
Public Function AVG2(ByVal tx$) As Double
Static RE As RegExp, fStatic As Boolean
If Not fStatic Then
fStatic = True
Set RE = New RegExp: RE.Global = True
End If
RE.Pattern = "[\.\-]"
tx = RE.Replace(tx, ",")
RE.Pattern = "(?:\d+ ?)+,\d+"
AVG2 = CDbl(RE.Execute(tx)(0).Value)
End Function
'====================================================================================================
Public Function AVG3(ByVal tx$) As Double
Static RE1 As RegExp, RE2 As RegExp, fStatic As Boolean
If Not fStatic Then
fStatic = True
Set RE1 = New RegExp: RE1.Global = True: RE1.Pattern = "[\.\-]"
Set RE2 = New RegExp: RE2.Pattern = "(?:\d+ ?)+,\d+"
End If
tx = RE1.Replace(tx, ",")
AVG3 = CDbl(RE2.Execute(tx)(0).Value)
End Function
'====================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄