Форум » [x]Harbour » Универсальная функция поиска подстрок в строке » Ответить

Универсальная функция поиска подстрок в строке

Sergy: Добрый день. Подскажите пожалуйста "универсальную" функцию поиска. Наподобие: IF ("abc 345" $ MyString) .. ENDIF Т.е. нужен быстрый поиск всех входящих подстрок, разделенных пробелом в любом порядке. Wildcards пока(?) не актуален. Вряд-ли для этого нужно изобретать свой велосипед... Спасибо.

Ответов - 15

Dima: AT() (HB_AT()) не подходит ? А вообще нужно тестить что быстре $ , HB_WILDMATCH или HB_AT

Sergy: Dima пишет: AT() (HB_AT()) не подходит ? А вообще нужно тестить что быстре $ , HB_WILDMATCH или HB_AT AT(), HB_AT(), $ - не то... Нужно, к примеру, чтобы поиск по образцу "abc 123" дал TRUE в строке, например, такой: "012345 defabc" HB_WILDMATCH() - ща гляну, спасибо за наводку.

Dima: Sergy пишет: Нужно, к примеру, чтобы поиск по образцу По образцу смотри регулярные выражения в Harbour , их есть там :)


Sergy: Dima пишет: По образцу смотри регулярные выражения в Harbour , их есть там :) Немного не то, что нужно. Как, впрочем и hb_WildMatch(). Суть: есть ассортимент из ~7000 наименований товара. Юзеру нужен "быстрый и удобный" отбор. Набрал он, к примеру в строке поиска "10 SD 32" - ему из всего ассортимента нужно показать только тот товар, который удовлетворяет условию: ("10" $ name) .AND. ("SD" $ name) .AND. ("32 $ name"). Т.е. это флэшки SD или MicroSD емкостью 32гига 10-го класса. C регулярными выражениями никто морочиться не будет - сто пудофф. Максимум может понадобиться выражение "не входит", например: "10 SD 32 !micro" - даст тот-же самый результат, но в него не будут включены карты типа microSD - только SD. Пока наваял такое: [pre2] FUNC MatchIn(cPattern,cText) // поиск по набору подстрок LOCAL result,i,num_words,x num_words := NUMTOKEN(cPattern," ") // считаем кол-во слов IF num_words > 1 // сложный поиск ? result := TRUE // заранее FOR i:=1 TO num_words x := TOKEN(cPattern," ",i) // разбираем по словам IF (LEFT(x,1) $ "!~") // отрицание - то, чего быть не должно IF (SUBSTR(x,2) $ cText) // убираем первый символ result := FALSE EXIT ENDIF ELSE // обычная строка, которую ищем IF !(x $ cText) result := FALSE EXIT ENDIF ENDIF NEXT i ELSE // поиск по одному слову result := (cPattern $ cText) ENDIF RETURN result [/pre2] Но чую, что это велосипед, причем не самый быстрый...

Dima: во во тоже хотел с токенами предложить.

SergKis: Sergy А если так:[pre2] cString := ... // где ищем cToken := "10 SD 32" // что ищем ... aToken := hb_aTokens(cToken, ' ') nToken := len(aToken) FOR i := 1 TO nToken lFound := aToken[ i ] $ cString IF lFound EXIT ENDIF NEXT IF lFound // нашли ... ENDIF [/pre2]

Andrey: Sergy пишет: в строке поиска "10 SD 32" - ему из всего ассортимента нужно показать только тот товар, который удовлетворяет условию: ("10" $ name) .AND. ("SD" $ name) .AND. ("32 $ name") Я делаю такие поиски через условную индексацию. На базе в 10 тыс.записей меньше минуты, а то и меньше. Если держать открытым индекс по полю name то вообще будет мухой летать. Вот например такое условное выражение: DateDog>CTOD('01.01.10') .AND.KStDogov=1.AND.KVidOpl=3.AND.KFirma=31.AND.Kcity=1.AND.!DELETED()

Softlog86: Будете ржать , мой поиск на базе в 10 тыс наименований (несколько текстовых полей для поиска всех вхождений на локальной базе) занимает меньче чем пол секунды : Используем конечно токены :) сверхбыстрый поиск ! -> Достаточно построить таблицу хотя-бы с одним из фрагментов . А далее по рекурсии по вхождению остальных фрагментов поиска ..... PS: Пост # 406 именно так !

Andrey: Softlog86 пишет: вхождений на локальной базе) занимает меньче чем пол секунды А у меня поиск по сетевой базе.

Sergy: SergKis пишет: Sergy А если так: aToken := hb_aTokens(cToken, ' ') ... Ок, спасибо за наводку, ща посмотрю, что это за зверь...

Sergy: Andrey пишет: Я делаю такие поиски через условную индексацию. На базе в 10 тыс.записей меньше минуты, а то и меньше. Если держать открытым индекс по полю name то вообще будет мухой летать. Вот например такое условное выражение: DateDog>CTOD('01.01.10') .AND.KStDogov=1.AND.KVidOpl=3.AND.KFirma=31.AND.Kcity=1.AND.!DELETED() Понимаете, какая петрушка: сейчас оператору нужна флэшка на 32гига класс10, а потом он посмотрел на них, нажал Esc(отмена) или Enter(выбор) и дальше хочет найти на 16гигов. Или класс 6. Или вообще не флэшку, а кулер для проца. Вариант перестраивать индекс "меньше минуты" в такой ситуации - вообще не вариант...

Sergy: Softlog86 пишет: Будете ржать , мой поиск на базе в 10 тыс наименований (несколько текстовых полей для поиска всех вхождений на локальной базе) занимает меньче чем пол секунды : Используем конечно токены :) сверхбыстрый поиск ! -> Достаточно построить таблицу хотя-бы с одним из фрагментов . А далее по рекурсии по вхождению остальных фрагментов поиска ..... PS: Пост # 406 именно так ! Сорри, можно чуть более развернуто - про "таблицу с одним из фрагментов и рекурсией" ? И где этот пост №406 ?? Спасибо

SergKis: Sergy вместо aToken[ i ] $ cString можно попробовать: AT(aToken[ i ], @cString) > 0 или hb_at(aToken[ i ], @cString) > 0

Sergy: SergKis пишет: вместо aToken[ i ] $ cString можно попробовать: AT(aToken[ i ], @cString) > 0 или hb_at(aToken[ i ], @cString) > 0 Либо я чего-то не понимаю, либо операция "x $ y" оказывается самой быстрой: [pre2] FUNC Main() LOCAL start,i,sx,x,t1,t2,t3,counter counter := 10000000 sx := "A simply string we use to determine the speed of operations" start := HB_MILLISECONDS() FOR i:=1 TO counter x := AT("of",sx) NEXT i t1 := HB_MILLISECONDS() - start start := HB_MILLISECONDS() FOR i:=1 TO counter x := ("of" $ sx) NEXT i t2 := HB_MILLISECONDS() - start start := HB_MILLISECONDS() FOR i:=1 TO counter x := HB_AT("of",sx) NEXT i t3 := HB_MILLISECONDS() - start ? "Test AT() : ",STR(t1/1000),"sec" ? "Test $ : ",STR(t2/1000),"sec" ? "Test HB_AT() : ",STR(t3/1000),"sec" INKEY(0) [/pre2] дает результат: [pre2] Test AT() : 2.31 sec Test $ : 1.39 sec Test HB_AT() : 2.08 sec [/pre2] 10 миллионов операций за полторы секунды - думаю, тут нечего больше оптимизировать...

Softlog86: Sergy тут видимо обсуждаем как сделать поиск по вхождению нескольких фрагментов в файле базы данных .... а не поиск в памяти .... Первичная подготовка : SELECT SPISOK // наша база данных hs := HS_INDEX( "SPISOK","UPPER(CHARREM('-., ',TYPE+' '+COMMENT))" , 2, 0, , .T., 3 ) // Создаём Индекс по ДВУМ ТЕКСТОВЫМ ПОЛЯМ : TYPE + COMMENT // Предварительно удаляя символы -разделители >[ - . , ] Поиск фрагментов : SEARCH - это DBF-файл куда сбрасываем строки с найденными фрагментами (для дальнейшего просмотра) перед поиском - она пустая USE SEARCH NEW ALIAS SEARCH EXCLUSIVE SELECT SPISOK aTokens := HB_ATokens( MySTR, ' ' ) // в массиве aTOKENS - минифразы из общей строки (MySTR) - Разделитель ПРОБЕЛ // // По сути - значения по первому токену - далее идёт отфильтровывание записей в которых нет всех следующих // токенов . За тем пакуем данные и на выходе только строки со всеми вхождениями ! MyStrA:=UPPER(CHARREM('.,-',aTOKENS[1])) // Поиск первой фразы из токена 1 - записываем в выходной файл поисковика! HS_SET( hs, MySTRA ) // Ищем первый фрагмент в индексном файле GO TOP while ( n := HS_NEXT( hs ) ) > 0 dbgoto( n ) if HS_VERIFY( hs ) > 0 // Искомое найдено !!! Переписываем в результирующий файл SEARCH->(DBAPPEND()) REPLACE SEARCH->CODE WITH SPISOK->CODE REPLACE SEARCH->TYPE WITH SPISOK->TYPE REPLACE SEARCH->COMMENT WITH SPISOK->COMMENT endif enddo IF LEN(aTOKENS)>1 && Токенов больше чем 1 SELECT SEARCH GO TOP For i:=2 TO LEN(aTOKENS) // Для каждого элемента массива aTokens после первого cElem:=UPPER(CHARREM('.,-',aTOKENS)) // убираем ненужные символы GO TOP DO WHILE !EOF() // Проверяем на 'X' в вырезанных от ненужных символов строках "TYPE" + "COMMENT" K:= UPPER(CHARREM('.,-',SEARCH->TYPE+" "+SEARCH->COMMENT+" "+CHARREM(' ',SEARCH->OEM))) If (cElem $ K ) // Ничего не делаем ELSE DELETE // cELEM нет ни в одном из полей ! ENDIF SKIP ENDDO Next // Следующий токен для поиска в SEARCH.DBF PACK // Упаковываем SEARCH.DBF ENDIF SELECT SEARCH // << Теперь здесь находятся строки в которых есть ВСЕ ИСКОМЫЕ ФРАЗЫ



полная версия страницы