bedvit, сильно дополнил пост сверху)) тест сделаю, а поиск без учёта регистра мне не нужен (особо)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit написал: ПРОБЛЕМАБиблиотека очень часто крашится… В основном, когда подключена одновременно и к надстройке (сторонней) и к активной книге
Моя? Нужно понять какая СОМ или XLL. А так же собрать статистику в каких случаях. У себя такого не замечал. Возможно оставить вариант только с настройкой, зачем отдельно подключать к активной книге?
bedvit, не - проблема не ваша. У меня же только))) я пока потестирую только в своей надстройке…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, простейший стенд готов. В первом акте меняем "aa" и "zz" на одиночные символы.
Результаты на 1 млн цикла одной и той же строки: 1. Регулярки: 5,8 сек (кто бы сомневался) 2. Replace4x: 22,5 сек (в данном случае 3 проходов оказалось мало) 3. ReplaceInStr: 28,5 сек (гарантированная замена с проверкой)
Тестовый стенд
Код
Option Explicit
Option Private Module
'====================================================================================================
Const rMax& = 1000000
'====================================================================================================
'====================================================================================================
'====================================================================================================
Sub t()
Dim arr(), txt$, i&, n&, t!
Dim strTest$: strTest$ = [a3].Value2
ReDim arr(rMax - 1)
For i = 0 To UBound(arr)
arr(i) = strTest
Next i
t = Timer
For i = 0 To UBound(arr)
ReplaceRegExp arr(i)
' Replace4x arr(i)
' ReplaceInStr arr(i)
Next i
t = 1000 * (Timer - t)
[a1].Value2 = arr(0)
MsgBox Format$(t, "0 ms")
End Sub
'====================================================================================================
Sub Replace4x(iVal)
Dim n&
For n = 1 To 4
iVal = Replace(iVal, "aa", "a")
iVal = Replace(iVal, "zz", "z")
Next n
End Sub
'====================================================================================================
Sub ReplaceInStr(iVal)
Dim x, dx$, i&
Static arr: If Not IsArray(arr) Then arr = Array("a", "z")
For Each x In arr
dx = x & x
Do While InStr(1, iVal, dx)
iVal = Replace(iVal, dx, x)
Loop
Next x
End Sub
'====================================================================================================
Sub ReplaceRegExp(iVal)
Dim x, dx$, i&
Static arr: If Not IsArray(arr) Then arr = Array("a", "z")
Static RE As RegExp: If RE Is Nothing Then Set RE = New RegExp: RE.Global = True: RE.MultiLine = True
For Each x In arr
RE.Pattern = x & "{2,}"
iVal = RE.Replace(iVal, x)
Next x
End Sub
'====================================================================================================
если обгонять регулярки запарно, то может и не стоит игра свеч…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Моя реализация InStr, тест на 1 млн. символов -10 тыс. итераций. Пока паритет со стандартной. Time InStr 5,367188 сек 1000074 Time bVBA.InStr 5,367188 сек 1000074
Коллеги, как можно улучшить код? (BSTR - это массив символов Юникода) Массив начинается с 0. задача как можно меньше запускать второй цикл.
Код
STDMETHODIMP CVBA::InStr(BSTR StringIn, BSTR StringFind, LONG Start, LONG* Position)
{
LONG iSize = SysStringLen(StringIn);
LONG fSize = SysStringLen(StringFind);
if (Start < 1 || Start > iSize) return E_INVALIDARG;
if (fSize == 0) return E_INVALIDARG;
LONG i = --Start;
LONG j;
for (i; i < iSize; ++i)
{
if (StringIn[i] == StringFind[0])
{
for (j = 1; j < fSize; ++j)
{
if (StringIn[i+j] != StringFind[j]) { break; }
}
if (j == fSize)
{
*Position = i+1;
return S_OK;
}
}
}
*Position = 0;
return S_OK;
}
bedvit, по коду не подскажу, а вот по тесту: может тестить на строке до 500 символов? Это реальная максимальная длина очень длинных строк (за редким исключением). А вообще обычно не более 150 символов… Зачем тестить на том, чего никогда не будет в жизни
// string::find
#include <iostream> // std::cout
#include <string> // std::string
int main ()
{
std::string str ("There are two needles in this haystack with needles.");
std::string str2 ("needle");
// different member versions of find in the same order as above:
std::size_t found = str.find(str2);
if (found!=std::string::npos)
std::cout << "first 'needle' found at: " << found << '\n';
found=str.find("needles are small",found+1,6);
if (found!=std::string::npos)
std::cout << "second 'needle' found at: " << found << '\n';
found=str.find("haystack");
if (found!=std::string::npos)
std::cout << "'haystack' also found at: " << found << '\n';
found=str.find('.');
if (found!=std::string::npos)
std::cout << "Period found at: " << found << '\n';
// let's replace the first needle:
str.replace(str.find(str2),str2.length(),"preposition");
std::cout << str << '\n';
return 0;
}
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, потому как слишком быстрые функции. Удалось обогнать в некоторых тестах стандартную InStr на строках в 1 млн.символов в 5,5 раз, на строках в 500 символов в 4,5 раза. В случаях когда в исходной строке часто встречается символ, с которого начинается строка, которую ищем. Двольно частая ситуация. Видимо не до конца оптимизировали. Тест прилагаю. Загрузил тестовую библу v1.0.0.7.DEBUG.rar
Тайминг 500 символов, 1 млн. итераций: Time InStr 2,98 сек позиция 574 Time bVBA.InStr 0,65 сек позиция 574
Пришлось использовать всеми любимую goto. Код в dll:
Скрытый текст
Код
STDMETHODIMP CVBA::InStr(BSTR StringIn, BSTR StringFind, LONG Start, LONG* Position)
{
LONG iSize = SysStringLen(StringIn);
LONG fSize = SysStringLen(StringFind);
*Position = 0;
if (fSize == 0) return S_OK;
if (Start < 1 || Start > iSize) return S_OK;
for (LONG i = --Start; i < iSize; ++i)
{
if (StringIn[i] == StringFind[0])
{
for (LONG j = 1; j < fSize; ++j)
{
if (StringIn[i+j] != StringFind[j])
{
goto next;
}
}
*Position = ++i;
return S_OK;
}
next:;
}
return S_OK;
}
Jack Famous, ваши примеры используют встроенные инструменты поиска из стандартной библиотеки std::string. Думаю они будут медленнее, в силу копирования данных в памяти для возможности использовать стандартную библиотеку (конструкторы, деструкторы строки). Я читаю сразу из памяти, без копирования, по указателю переданной строки (BSTR). Но за примеры спасибо)
что лишний раз доказывает полезность разумного её применения я вообще очень уважаю беготню по меткам, т.к. они очень шустрые и больших циклах могут дать неплохой прирост. Например проверка If Len(x) Then быстрее If Len(x)>0 Then, а GoTo равен или даже чуть выигрывает при гораздо большей универсальности
чем богаты, как говорится вот если бы вы кусочек кода в VBA написали, то посмотрел бы. Хотя когда там уже разговор идёт о прямой работе с памятью, то тут нужна команда куда серьёзней меня: Андрей VG, sokol92 и Казанский (может ещё кого-то из мэтров, глубоко ныряющих в код, забыл)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
" ////..
. .... . .... ,, ,,, ::asdasПЕРЕНОСЫ
КОНЕЦ ПЕРЕНОСОВ daaaaaaaaaaasd::::: ::: :: : ::; ;;;asdaaaaaaaaaaas100X36 100 X36 d100X 36 d100 X 36 d100 X 36 d100x36 d100 x36 d100x 36 d100 x 36 zzzzzzzzzzzzzzaaaaaaaaaaaaaaazzzzzzzzzzzzzzzzzz d100 x 36 d100 Х36 d100Х 36 d100 Х 36 d100 Х 36 d100х36 d100 х36 d100х 36 d100 х 36 d100 х 36 d100х36 ddaaaaaaaaaasda;;;;;;;; asd as d asd asdaaaaaaaaaaaasdasda, aszzzzzzzzzzzzzzdasdazzzzzzzzzzzsd ad azzzzzzzzzzzzzzzzsFINDd a, //////// "
1. Сравнение, InStr " в лоб" поиск подстроки "FIND" в 497 позиции. Классика - ~650 мс. bedvit - 660-670 ms. Минимальное преимущество штатной, можно списать на погрешность 2. Сравнение в качестве замены штатной в составе Sub ReplaceInStr — около 0,5 сек выигрыша bedvit'а
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, уже взялся))) первые вопросы: аргументы у вашей Replace такие же, как у оргинала, но Count что-то не работает -
Код
Sub t()
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
MsgBox bVBA.Replace("анатолий", "а", "ы", , 1)
End Sub
должен выдать "анытолий", а выдаёт всё-равно "ынытолий". Плюс, как мне кажется, при указании Start'а всё-таки не стоит обрезать результат, это указание, как мне кажется, нужно только, чтобы ускорить замену в длинных строках…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Start- не обрезает результат, Count - еще не допилен (не работает), т.е. заменяются все вхождения. Остальное - все рабочее. Все параметры аналогичны обычной функции, за исключением того, что строка не обрезается (сейчас проверю). Проверил - работает как и стандартная - обрезает. Т.е. функционал/итоги абсолютно одинаковы, за исключением скорости. Хотим изменить алгоритм в сравнении со стандартной?
bedvit, я тут пока тесты завершил))) Потрясающе! При увеличении количества заменяемых элементов, ваша Replace улетает (отрывается) просто в космос чтоб, вы понимали — регулярки разбиты
Замена 8 двойных символов одинарными аналогами (100 k элементов): • Replace4x: классика - 6,3, bedvit - 2,95 (!!!). Это при условии 4х прогонов без проверки • ReplaceInStr: классика - 6,4; bedvit(Replace+Instr) - 3,2; bedvitReplace + штатная Instr - 3-3,1 • ReplaceRegExp: 5,9 + InStr от bedvit'а немного, но стабильно проигрывает штатному. Особенно это заметно при сравнении в составе других функций… Вполне допускаю, что на длинных строках покажет себя лучше, да и тест был уж очень узкоспециализированный.
Главный вывод: смело берём и используем!
Тестовый стенд
Код
Option Explicit
Option Private Module
'====================================================================================================
Const rMax& = 100000
'----------------------------------------------------------------------------------------------------
Public Property Get arrFind()
arrFind = Array(" ", "\.", ",", ":", ";", "/", "a", "z")
End Property
'----------------------------------------------------------------------------------------------------
Public Property Get arrReplace()
arrReplace = Array(" ", ".", ",", ":", ";", "/", "a", "z")
End Property
'====================================================================================================
'====================================================================================================
'====================================================================================================
Sub Tester()
Dim arr(), txt$, i&, n&, t!
Dim strTest$: strTest$ = [a3].Value2
strTest = Replace(strTest, Chr(10), " ")
'strTest = WorksheetFunction.Trim(strTest) 'для теста Split
ReDim arr(rMax - 1)
For i = 0 To UBound(arr)
arr(i) = strTest
Next i
t = Timer
For i = 0 To UBound(arr)
' InStrClassic arr(i)
' InStrBedvit arr(i)
ReplaceRegExp arr(i)
' Replace4x arr(i)
' ReplaceInStr arr(i)
Next i
t = 1000 * (Timer - t)
[a1].Value2 = arr(0)
MsgBox Format$(t, "0 ms")
End Sub
'====================================================================================================
'====================================================================================================
'====================================================================================================
Sub InStrClassic(ival)
ival = InStr(1, ival, "FIND")
End Sub
'====================================================================================================
Sub InStrBedvit(ival)
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
ival = bVBA.InStr(ival, "FIND")
End Sub
'====================================================================================================
'====================================================================================================
Sub SplitReplace(ival)
Dim arr(), i&, ii&, n&
ReDim arr(Len(ival) - Len(Replace(ival, " ", "")))
i = InStr(1, ival, " ")
Do While i
' arr(n)=mid(ival,i+1,)
Loop
ival = Join(arr, " • ")
End Sub
'====================================================================================================
'Sub SplitBedvit(ival)
'
'End Sub
'====================================================================================================
Sub Replace4x(ival)
Dim x, dx$, i&
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrReplace
dx = x & x
For i = 1 To 4
' ival = Replace(ival, dx, x)
ival = bVBA.Replace(ival, dx, x)
Next i
Next x
End Sub
'====================================================================================================
'Sub t()
'Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
'MsgBox bVBA.Replace("анатолий", "а", "ы", , 2)
'End Sub
'====================================================================================================
Sub ReplaceInStr(ival)
Dim x, dx$, i&
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrReplace
dx = x & x
' Do While InStr(1, ival, dx)
' ival = Replace(ival, dx, x)
' Loop
' Do While bVBA.InStr(ival, dx)
Do While InStr(ival, dx)
ival = bVBA.Replace(ival, dx, x)
Loop
Next x
End Sub
'====================================================================================================
Sub ReplaceRegExp(ival)
Dim i&
Static RE As RegExp: If RE Is Nothing Then Set RE = New RegExp: RE.Global = True ': RE.MultiLine = True
For i = 0 To UBound(arrFind)
RE.Pattern = arrFind(i) & "{2,}"
ival = RE.Replace(ival, arrReplace(i))
Next i
End Sub
'==================================================================================================== По необязательным аргументам Replace даже не знаю — никогда их не использовал… Думаю, что нужно сделать так, как быстрее (в вашем алгоритме). А при прочих равных вот не могу представить, чтобы мне нужно было обрезать строку с некоторого символа, при условии, что я буду менять что-то после него и вообще непонятно на какой позиции… Штатные Left$, Mid$ и Right$ весьма скоростные, так что "обрезать" я всегда смогу
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
#31. Jack Famous: хоть Replace и не удаляет ничего, если в строке нет нужного, но добавление условия If Instr(1, строка, что_ищем) позволяет стабильно ускорить процесс. Если возьмётесь, то было бы круто вшить что-то подобное внутрь
Цитата
#32. bedvit: думаю в данном случае это не пригодится
забыл сказать, что проверка на InStr перед Replace по-прежнему (я про bVBA.Replace()) стабильно сокращает общее время выполнения (немного). Можно и нужно ли это оптимизировать?…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: InStr от bedvit'а немного, но стабильно проигрывает штатному.
в зависимости от строки, есть совсем мизерный проигрыш, до выигрыша в 5 раз. Все зависит от того, встречается ли в строке поиска первый символ строки, которую ищем. Мой алгоритм это эффективно проверяет, стандартный отстает. Хотите давайте тесты проведем. Обычно довольно часто встречается такая ситуация при длинной строке.
Цитата
Jack Famous написал: проверка на InStr перед Replace по-прежнему (я про bVBA.Replace()) стабильно сокращает общее время выполнения (немного).
Option Explicit
Option Private Module
'====================================================================================================
Const rMax& = 1000000
'====================================================================================================
Public Property Get arrSym()
arrSym = Array(1, 2, 3, 4)
'arrSym = Array(1, 3)
'arrSym = Array(2, 4)
End Property
'====================================================================================================
'====================================================================================================
Sub Tester()
Dim arr(), txt$, i&, n&, t!
Dim strTest$: strTest$ = [a3].Value2
strTest = Replace(strTest, Chr(10), " ")
ReDim arr(rMax - 1)
For i = 0 To UBound(arr)
arr(i) = strTest
Next i
t = Timer
For i = 0 To UBound(arr)
ReplaceCheck arr(i)
Next i
t = 1000 * (Timer - t)
[a1].Value2 = arr(0)
MsgBox Format$(t, "0 ms")
End Sub
'====================================================================================================
'====================================================================================================
Sub ReplaceCheck(iVal)
Dim x
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrSym
' If InStr(iVal, x) Then iVal = bVBA.Replace(iVal, x, "")
iVal = bVBA.Replace(iVal, x, "")
Next x
End Sub
Итоги (у меня): • Массив {1, 2, 3, 4} (2 есть, 2 нет): без проверки - 5,4; с проверкой - 5,2 • Массив {1, 3} (всё есть): без проверки - 3,45; с проверкой - 4,1 • Массив {2, 4} (ничего нет): без проверки - 3; с проверкой - 2,1
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous, если заведомо известно, что вхождения есть, включение проверки через InStr - увеличивает время. Возможно целесообразно использовать InStr, когда с большой долей вероятности можно сказать, что вхождений не должно быть. Тогда выигрыш будет, за счёт того, что "InStr" быстрее чем "Replace". Включать эту проверку в bVBA.Replace, значит увеличить время работы функции. Подразумевается, что используя эту функцию, программист уверен, что вхождения есть.
bedvit, всё так и есть. Учитывая, что InStr отрабатывает лям менее чем за 0,5 сек, я теперь его включаю в 99% случаев Вы скажете, когда Replace будет допилена или она уже?)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: bedvit : совсем мизерный проигрыш, до выигрыша в 5 разтогда вопрос снят. Как я уже писал, у меня крайне узкоспециализированные тесты…
в большинстве стандартных случаев действительно проигрывает. Выигрыш обычно появляется на длинных строках (сотни тысяч и выше символов). Подумаю что можно еще сделать. Спецы писавшие стандартную InStr, намного прокаченнее меня, трудно соревноваться
Вот придумал такой генератор случайной строки и нужного случайного числа(строки)
Код
Option Explicit
Sub RUN() 'для раннего связывания
'Подключаем библу "BedvitCOM" в References - version(1.0) для раннего связывания (если библа уже подключена - On Error Resume Next)
'Для раннего связывания сначала включаем в References библу, потом в конце кода отключаем. Для позднего связывания этого не нужно (см.ниже).
'Если BedvitCOM не оключать, могут быть ошибки в этом файле при отсутствии зарегестрированной BedvitCOM - выслали кому-то файл, или открыли из другого ПК и т.д., где не установлеена или не открыта надстройка BedvitXLL (которая автоматом распаковывает и регистрирует библиотеку BedvitCOM в реестре) или не зарегистрированна BedvitCOM вручную
'References хранятся в файле
On Error Resume Next
ThisWorkbook.VBProject.References.AddFromGuid "{77D79CA3-15A0-4310-B8D8-0BCBE3F72D96}", 1, 0 ' подключаем библу "BedvitCOM" в References - version(1.0) для раннего связывания (если библа уже подключена - On Error Resume Next)
Test
ThisWorkbook.VBProject.References.Remove ThisWorkbook.VBProject.References("BedvitCOM") 'оключаем библу в References
End Sub
Sub Test()
Dim s As String, x, t, s2, s3, s4
Dim bVBA As BedvitCOM.VBA: Set bVBA = New BedvitCOM.VBA 'раннее связывание
'Dim bVBA As Object: Set bVBA = CreateObject("BedvitCOM.VBA") 'позднее связывание
Randomize
For x = 0 To 10000
s = s + Format(Rnd() * 10000000000#, "0000000000")
Next
s2 = Format(Rnd() * 100000, "00000")
Debug.Print "find-"; s2
t = Timer
For x = 1 To 10000
s4 = InStr(s, s2)
Next
Debug.Print "InStr "; Timer - t; " сек, позиция-"; s4
t = Timer
For x = 1 To 10000
s4 = bVBA.InStr(s, s2)
Next
Debug.Print "bVBA.InStr "; Timer - t; " сек, позиция-"; s4
End Sub
Возможно вызов функции из моей СОМ стоит дороже вызова стандартной. Тогда частые вызовы функций играют на конкурента. Когда вызовов мало, но данных много, ситуация меняется. Надо над этим подумать.
bedvit, доброго дня! Благодарю за тесты и генератор
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Сегодня конкуренты нервно курят в сторонке. bVBA.InStr в разы быстрее стандартной InStr на любой длинны строках. Библиотека BedvitDLL(1.0.0.8).zip Replace оптимизировал в плане стабильности на разного размера строк. Обгоняет стандартную в разы/на порядок (в некоторых случаях). Новые функции (в новой библиотеке): bVBA.InStr и bVBA.Replace ни в одном тесте не проиграли стандартным. Обязательно к тестированию Ключ к успеху: быстрое копирование памяти Максимально быстрое сравнение двух блоков памяти
Тестовый фал выкладываю. Код:
Код
Option Explicit
Sub RUN() 'для раннего связывания
'Подключаем библу "BedvitCOM" в References - version(1.0) для раннего связывания (если библа уже подключена - On Error Resume Next)
'Для раннего связывания сначала включаем в References библу, потом в конце кода отключаем. Для позднего связывания этого не нужно (см.ниже).
'Если BedvitCOM не оключать, могут быть ошибки в этом файле при отсутствии зарегестрированной BedvitCOM - выслали кому-то файл, или открыли из другого ПК и т.д., где не установлеена или не открыта надстройка BedvitXLL (которая автоматом распаковывает и регистрирует библиотеку BedvitCOM в реестре) или не зарегистрированна BedvitCOM вручную
'References хранятся в файле
On Error Resume Next
ThisWorkbook.VBProject.References.AddFromGuid "{77D79CA3-15A0-4310-B8D8-0BCBE3F72D96}", 1, 0 ' подключаем библу "BedvitCOM" в References - version(1.0) для раннего связывания (если библа уже подключена - On Error Resume Next)
Test
ThisWorkbook.VBProject.References.Remove ThisWorkbook.VBProject.References("BedvitCOM") 'оключаем библу в References
End Sub
Sub Test()
Dim s As String, x, t, s2, s3, s4
Dim bVBA As BedvitCOM.VBA: Set bVBA = New BedvitCOM.VBA 'раннее связывание
'Dim bVBA As Object: Set bVBA = CreateObject("BedvitCOM.VBA") 'позднее связывание
Randomize
For x = 0 To 10000
s = s + Format(Rnd() * 10000000000#, "0000000000")
Next
s2 = Format(Rnd() * 100, "00")
Debug.Print "find-"; s2
t = Timer
For x = 1 To 10000000
s4 = InStr(s, s2)
Next
Debug.Print "InStr "; Timer - t
t = Timer
For x = 1 To 10000000
s4 = bVBA.InStr(s, s2)
Next
Debug.Print "bVBA.InStr "; Timer - t
t = Timer
For x = 1 To 10000
s4 = Replace(s, s2, "333")
Next
Debug.Print "Replace "; Timer - t
t = Timer
For x = 1 To 10000
s4 = bVBA.Replace(s, s2, "333")
Next
Debug.Print "bVBA.Replace "; Timer - t
End Sub
bedvit, у меня просто слов нет - вы действительно увлечённый своей работой человек! Спасибо вам огромное!!! Потрясающие результаты! Честно говоря, никогда бы не поверил, что стандартную InStr можно обогнать каким-либо образом — настолько она кажется "простой" и шустрой (0,5 сек на 1 млн прогонов по строке до 255 символов)… Завтра потестирую на своих данных + в составе других макросов на "работу в команде"
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Тесты очень порадовали: по самым грубым подсчётам выигрыш составил от 1,5 (голый InStr) до 6-7 раз (InStrReplace). "В команде" также работает хорошо - заменил некоторые регулярки вашим Replace'ом (например замена двойных символов). InStr настолько быстрый, что при малейшей вероятности, что в строке может отсутствовать подстрока для Replace, можно смело втыкать проверку типа:
Код
For Each x In arrSym
i = bVBA.InStr(iVal, x)
If i Then iVal = bVBA.Replace(iVal, x, "", i)
Next x
— то есть тут как раз очень кстати третий параметр Replace, позволяющий просматривать не с начала. Нашли? Знаем где. Не нашли? Не тратим ресурсы Replace и идём дальше. Со штатной, ясное дело, такой фокус не прокатит, т.к. придётся "приклеивать голову" до найденной позиции, что убивает любой выигрыш на корню (я пробовал)…
С другой стороны, макрос ReplaceCheck (откуда пример проверки) показывает, что на 100k элементов разница между прямым вызовом и вызовом с проверкой составляет ~ 200мс, что говорит о крайне высочайшей степени оптимизации! Отшлифовано просто грандиозно! Это результат на arrSym = Array(1, 2, 3, 4), то есть 2 символа есть в строке и 2 нет. Соответственно проверка даст большой выигрыш на arrSym = Array(2, 4) (символы отсутствуют в строке) и мизерный проигрыш на arrSym = Array(1, 3) (символы присутствуют в строке).
Предложение: пытался вот тут сколотить замену штатной Split через вашу Replace, но понял, что для хорошего результата вашему InStr не хватает третьего параметра "с какой позиции смотреть" (прямо как у Replace). Можно ли "прикрутить" его без потери скорости? При необходимости сделать обязательным (не опциональным), если это понадобится…? Уверен, что он для многих целей будет совсем не лишним
Стенд
Код
Option Explicit
Option Private Module
'====================================================================================================
Const rMax& = 100000
'----------------------------------------------------------------------------------------------------
Public Property Get arrFind()
arrFind = Array(" ", "\.", ",", ":", ";", "/", "a", "z")
End Property
'----------------------------------------------------------------------------------------------------
Public Property Get arrReplace()
arrReplace = Array(" ", ".", ",", ":", ";", "/", "a", "z")
End Property
'----------------------------------------------------------------------------------------------------
Public Property Get arrSym()
arrSym = Array(1, 2, 3, 4)
'arrSym = Array(1, 3)
'arrSym = Array(2, 4)
End Property
'====================================================================================================
'====================================================================================================
'====================================================================================================
Sub Tester()
Dim arr(), txt$, i&, n&, t!
Dim strTest$: strTest$ = [a3].Value2
strTest = Replace(strTest, Chr(10), " ")
ReDim arr(rMax - 1)
For i = 0 To UBound(arr)
arr(i) = strTest
Next i
t = Timer
For i = 0 To UBound(arr)
ReplaceCheck arr(i)
' Replace4x arr(i)
' ReplaceInStr arr(i)
' ReplaceRegExp arr(i)
Next i
t = 1000 * (Timer - t)
[a1].Value2 = arr(0)
MsgBox Format$(t, "0 ms")
End Sub
'====================================================================================================
'====================================================================================================
'====================================================================================================
Sub ReplaceCheck(iVal)
Dim x, i&
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrSym
i = bVBA.InStr(iVal, x)
If i Then iVal = bVBA.Replace(iVal, x, "", i)
' iVal = bVBA.Replace(iVal, x, "")
' If InStr(iVal, x) Then iVal = Replace(iVal, x, "")
' iVal = Replace(iVal, x, "")
Next x
End Sub
'====================================================================================================
Sub Replace4x(iVal)
Dim x, dx$, i&
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrReplace
dx = x & x
For i = 1 To 4
iVal = Replace(iVal, dx, x)
' iVal = bVBA.Replace(iVal, dx, x)
Next i
Next x
End Sub
'====================================================================================================
Sub ReplaceInStr(iVal)
Dim x, dx$, i&
Static bVBA As BedvitCOM.VBA: If bVBA Is Nothing Then Set bVBA = New BedvitCOM.VBA
For Each x In arrReplace
dx = x & x
'i = InStr(iVal, dx)
' Do While i
' iVal = Left(iVal, i - 1) & Replace(iVal, dx, x, i)
' i = InStr(iVal, dx)
' Loop
Do While InStr(iVal, dx)
iVal = Replace(iVal, dx, x)
Loop
'i = bVBA.InStr(iVal, dx)
' Do While i
' iVal = bVBA.Replace(iVal, dx, x, i)
' i = bVBA.InStr(iVal, dx)
' Loop
Next x
End Sub
'====================================================================================================
Sub ReplaceRegExp(iVal)
Dim i&
Static RE As RegExp: If RE Is Nothing Then Set RE = New RegExp: RE.Global = True ': RE.MultiLine = True
For i = 0 To UBound(arrFind)
RE.Pattern = arrFind(i) & "{2,}"
iVal = RE.Replace(iVal, arrReplace(i))
Next i
End Sub
'====================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: InStr не хватает третьего параметра "с какой позиции смотреть" (прямо как у Replace).
если внимательно посмотреть, то можно его увидеть Скорость моей InStr уже с учетом этого параметра ;-)
Цитата
Jack Famous написал: ReplaceCheck (откуда пример проверки) показывает, что на 100k элементов разница между прямым вызовом и вызовом с проверкой составляет ~ 200мс, что говорит о крайне высочайшей степени оптимизации! Отшлифовано просто грандиозно!