Что нового

[строки] Поиск в строке текста между подстроками. (Функция)

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
AutoIt: 3.2.5.4 и далее (возможно и ранее)
Версия: v1.1

Категория: Данные, строки

[box title=Функция:]
Возвращает найденную подстроку в строке по заданным граничащим подстрокам.
StringFindSE ($string[,$start[,$end[,$start_occ[,$end_occ[,$cas]]]]])
Параметры функции:
$stringСтрока в которой будет происходить поиск
$startОпционально. Подстрока находящаяся до искомой (если значение пустое(по умолчанию) - то подстрока считается началом строки)
$endОпционально. Подстрока находящаяся после искомой (если значение пустое(по умолчанию) - то подстрока считается концом строки)
$start_occОпционально. Номер искомого вхождения подстроки $start (по умолчанию 1). Отрицательные значения означают поиск с конца строки.
$end_occОпционально. Номер искомого вхождения подстроки $end (по умолчанию 1). Отрицательные значения означают поиск с конца строки.
$casОпционально. Режим учета регистра написания.
0 = не зависит от регистра написания согласно языку пользователя (по умолчанию)
1 = зависит от регистра написания
2 = не зависит от регистра написания согласно базовому/наиболее быстрому сравнению
Возвращаемые значения:
При успешном выполнении возвращает искомую подстроку.

Примечание: Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start

Код:
Func StringFindSE($string,$start="",$end="",$start_occ=1,$end_occ=1,$cas=0)
    Local $source_start,$source_count,$source
    If $start <> '' Then
		Local $st_count
		$st_count = StringSplit($start,'')
		$source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
	Else
		$source_start = 1
	EndIf
	If $end <> '' Then
		$source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
	Else
		$source_count = -1
	EndIf
	Return StringMid ($string,$source_start,$source_count)
EndFunc
[/box]

Примеры:
Код:
$string = "В этой строке мы будем искать слово между 'мы ' и ' искать'."
$start = "мы "
$end = " искать"
$source = StringFindSE($string,$start,$end)
MsgBox(0,"Найдено:",$source)
Код:
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

GUICreate("Поиск", 305, 145, 489, 259)
GUICtrlCreateLabel("Сдесь мы будем искать:", 8, 5, 129, 17)
$eString = GUICtrlCreateEdit("В этой строке мы будем искать слово между 'мы ' и ' искать'.", 8, 24, 289, 49, BitOR($ES_AUTOVSCROLL,$ES_WANTRETURN,$WS_VSCROLL))
GUICtrlCreateLabel("Мы будем искать между", 8, 80, 129, 17)
$iSourceS = GUICtrlCreateInput("мы ", 136, 80, 65, 21)
GUICtrlCreateLabel("и", 208, 80, 10, 17)
$iSourceE = GUICtrlCreateInput(" искать", 224, 80, 73, 21)
$bFind = GUICtrlCreateButton("Искать", 88, 112, 99, 25)
GUISetState(@SW_SHOW)

While 1
	$nMsg = GUIGetMsg()
	Switch $nMsg
		Case $GUI_EVENT_CLOSE
			Exit
		Case $bFind
			$string = GUICtrlRead($eString)
			$start = GUICtrlRead($iSourceS)
			$end = GUICtrlRead($iSourceE)
			$source = StringFindSE($string,$start,$end)
			MsgBox(0,"Найдено:",$source)
	EndSwitch
WEnd

Прошлые версии:
Код:
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Функция возвращает найденную подстроку
; в строке по заданным граничащим подстрокам.
;
; $string = Строка в которой будет происходить поиск
; $start = Подстрока находящаяся до искомой
; $end = Подстрока находящаяся после искомой
; $start_occ = Номер искомого вхождения подстроки $start (по умолчанию 1)
;      Отрицательные значения означают поиск справа.
; $end_occ = Номер искомого вхождения подстроки $end (по умолчанию 1)
;      Отрицательные значения означают поиск справа.
; $cas = Режим учета регистра написания.
;      0 = не зависит от регистра написания согласно языку пользователя (по умолчанию)
;      1 = зависит от регистра написания 
;      2 = не зависит от регистра написания согласно базовому/наиболее быстрому сравнению
;
; Возвращаемое значение: искомая подстрока
;
; Примечание: Поиск подстроки $end начинается с символа,
; на котором заканчивается подстрока $start
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc
Код:
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
;Функция возвращает найденную подстроку
;в строке по заданным граничащим подстрокам.
;
;$string = Строка в которой будет происходить поиск
;$start = Подстрока находящаяся до искомой
;$end = Подстрока находящаяся после искомой
;
;Возвращаемое значение: искомая подстрока
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Func StringFindSE($string,$start,$end)
	Local $st_count,$source_start,$source_count,$source
	$st_count = StringSplit($start,"")
	$source_start = StringInStr($string,$start) + $st_count[0]
	$source_count = StringInStr($string,$end,0,1,$source_start) - $source_start
	$source = StringMid ($string,$source_start,$source_count)
;~	ConsoleWrite(@CRLF&"Первый сивол: "&$source_start&" Последний символ: "&$source_count&" Результат: "&$source&@CRLF)
	Return $source
EndFunc

[box title=ЧенжЛог]
Август 13, 2010 v1.1 добавлена возможность указать значения $start и $end пустыми, соответственно объявив начало и конец строки.
Июль 31, 2010 v1.1 добавлена возможность выбора номера совпадения $start и $end и чувствительности к регистру.
Июль 16, 2010 v1.0 первая версия[/box]


Источник: Эта страница и является первоисточником.
Автор: HukpoFuJl

От CreatoR'a:
Поиск первого совпадения в строке:
Код:
$source = StringRegExpReplace($string, '(?s).*?\Q' & $start & '\E(.*?)\Q' & $end & '\E.*', '$1')

Поиск последнего совпадения встроке:
Код:
$source = StringRegExpReplace($string, '(?s).*\Q' & $start & '\E(.*?)\Q' & $end & '\E.*', '$1')


Аналогичный способ в библиотеке String.au3:
Возвращает массив совпадений:
Код:
$source = _StringBetween($string, $start, $end [, $сas = -1])
Ссылка: _StringBetween
 
  • Like
Реакции: Norm

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Re: Поиск в строке текста между подстроками. (Функция)

Во-первых по моему на UDF оно не тянет, во-вторых такая функция уже есть (_StringBetween), ну и в третьих, при написании примера не нужно добавлять тело самой функции, получается что в сообщении она встречается аж 3 раза.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Re: Поиск в строке текста между подстроками. (Функция)

HukpoFuJl
Я плохо знаю регулярные выражения и сам часто пользуюсь подобной обработкой текста, но мне давно кто-то из знатоков подсказал следующую конструкцию (за что - огромное спасибо!), которая меня пока не подводила:
Код:
$string = 'В этой строке мы будем искать слово между "мы " и " искать".'
$start = 'мы '
$end = ' искать'
$sPattern = '(?s).*?' & $start & '(.*?)' & $end & '?.*'
$source = StringRegExpReplace($string, $sPattern, '\1')
MsgBox(0, 'Найдено:', $source)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Re: Поиск в строке текста между подстроками. (Функция)

madmasles [?]
мне давно кто-то из знатоков подсказал следующую конструкцию
Она кстати может не сработать если начальное или завершающее слово содержит служебные символы. Лучше так:

Код:
$sPattern = '(?s).*\Q' & $start & '\E(.*?)\Q' & $end & '\E.*'
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
Re: Поиск в строке текста между подстроками. (Функция)

Не вдавался в подробности, но, имхо, это можно регулярными выражениями найти.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Re: Поиск в строке текста между подстроками. (Функция)

CreatoR
В моем примере из второго поста с Вашим $sPattern у меня возвращается " и " вместо будем. :wacko:
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Re: Поиск в строке текста между подстроками. (Функция)

CreatoR сказал(а):
Во-первых по моему на UDF оно не тянет
А я и не говорил, что это UDF, это просто функция :smile: Так в теме и написал...
CreatoR сказал(а):
во-вторых такая функция уже есть (_StringBetween)
Эта функция возвращает аж массив и при всём при этом это целый UDF инклюдить надо для одного только действия (в моём случае так точно)... Вариант с регулярным выражением тогда уже лучше, но я слабоват в синтаксисе рег. выражений и посему как-то с описанным мною примером чувствую себя надежнее...
CreatoR сказал(а):
ну и в третьих, при написании примера не нужно добавлять тело самой функции, получается что в сообщении она встречается аж 3 раза.
Как-то не подумал... Исправил :smile:


Добавлено:
Сообщение автоматически объединено:

madmasles сказал(а):
CreatoR
В моем примере из второго поста с Вашим $sPattern у меня возвращается " и " вместо будем. :wacko:
Аналогично. Находит не первое совпадение, а последнее.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Re: Поиск в строке текста между подстроками. (Функция)

HukpoFuJl [?]
А я и не говорил, что это UDF, это просто функция
Но раздел то для UDF ;)

Эта функция возвращает аж массив
и что в этом плохого?

при всём при этом это целый UDF инклюдить надо для одного только действия
Оно мешает? :smile:

Нужно добавить знак вопроса после первого «.*»....
Код:
$sPattern = '(?s).*?\Q' & $start & '\E(.*?)\Q' & $end & '\E.*'
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Re: Поиск в строке текста между подстроками. (Функция)

CreatoR сказал(а):
Но раздел то для UDF ;)
Ну я прочитал "Библиотеки и отдельные пользовательские функции" ну и долго не думал... Если промазал - то переместите в полезняшки что ли, или где ей там более подходящее место :-[

CreatoR сказал(а):
Эта функция возвращает аж массив
и что в этом плохого?
Ну я обычно стараюсь избегать массивов, без них легче живется - путаешься меньше... ну в принципе тут уж кому как удобнее...

CreatoR сказал(а):
при всём при этом это целый UDF инклюдить надо для одного только действия
Оно мешает? :smile:
Опять же дело в удобности... не люблю инклюдить по 50 UDF'ок и пользоваться из каждой по 1й функции... Ну разве что при критичной необходимости...

CreatoR сказал(а):
Нужно добавить знак вопроса после первого «.*»....
Код:
$sPattern = '(?s).*?\Q' & $start & '\E(.*?)\Q' & $end & '\E.*'
О, так первое находит =)) Сколько не пытался эти коды анализировать - ничего толком и не понял =)) Но главное, что работает, спасибо ;)


Добавлено:
Сообщение автоматически объединено:

отредактировал первый пост.
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Вот недавно столкнулся с проблемой вытянуть доменное имя из ссылки вида "http://domain.ru/bla-bla-bla/megabla/blasheniki/" и слегка модернизировал функцию, добавив возможность указать номер найденного $start и $end, ну и за компанию сделал возможным выбрать реакцию на чувствительность :smile:

Код:
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Функция возвращает найденную подстроку
; в строке по заданным граничащим подстрокам.
;
; $string = Строка в которой будет происходить поиск
; $start = Подстрока находящаяся до искомой
; $end = Подстрока находящаяся после искомой
; $start_occ = Номер искомого вхождения подстроки $start (по умолчанию 1)
;      Отрицательные значения означают поиск справа.
; $end_occ = Номер искомого вхождения подстроки $end (по умолчанию 1)
;      Отрицательные значения означают поиск справа.
; $cas = Режим учета регистра написания.
;      0 = не зависит от регистра написания согласно языку пользователя (по умолчанию)
;      1 = зависит от регистра написания 
;      2 = не зависит от регистра написания согласно базовому/наиболее быстрому сравнению
;
; Возвращаемое значение: искомая подстрока
;
; Примечание: Поиск подстроки $end начинается с символа,
; на котором заканчивается подстрока $start
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
HukpoFuJl [?]
столкнулся с проблемой вытянуть доменное имя из ссылки
А разве шаблон RegExp что я привёл раньше не работает с этой строкой?
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
CreatoR сказал(а):
HukpoFuJl [?]
столкнулся с проблемой вытянуть доменное имя из ссылки
А разве шаблон RegExp что я привёл раньше не работает с этой строкой?
Работает, проверил =) Но, как я и говорил - я люблю пользоваться тем, что я понимаю как работает... В рег. выражениях я шарю слабовато (да и думаю не я один такой) и данную комбинацию символов просто непонимаю :smile:
Да и в любом случае, если например в строке "12345:\qwerty:word\бла-бла-бла" нужно будет найти то, что находиться между выделенным - то рег выражение найдет или ничего, или (если искать с конца) "word", хотя искомая подстрока "\qwerty:word"... На массивы разбивать тоже непонятно под каким номером там нужный результат потом будет, да и если нужно, например, найти какую-либо подстроку в начале агроменного бинарного файла - то массив просто забьется лишним и будет только занимать место в памяти...
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
HukpoFuJl [?]
я люблю пользоваться тем, что я понимаю как работает
Я тоже, и я также раньше не понимал RegExp, но мне было интересно понять, потому что его возможности куда болше обычных функции работы со строками.

в любом случае, если например в строке "12345:\qwerty:word\бла-бла-бла" нужно будет найти то, что находиться между выделенным - то рег выражение найдет или ничего, или (если искать с конца) "word", хотя искомая подстрока "\qwerty:word"
Тогда нужно задавать параметры правильно...
Код:
$vTest = '12345:\qwerty:word\бла-бла-бла'
$start = ':\'
$end = '\'
$sPattern = '(?s).*?\Q' & $start & '\E(.*?)\Q' & $end & '\E.*'

$sRet = StringRegExpReplace($vTest, $sPattern, '\\$1')
ConsoleWrite($sRet & @LF)


На массивы разбивать тоже непонятно под каким номером там нужный результат потом будет
Это зависит от групп и найденных совпадений в выражении.

если нужно, например, найти какую-либо подстроку в начале агроменного бинарного файла
Ну это мы уже далеко пошли, про бинарные данные никто не говорит, там RegExp почти не используется.
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
OffTopic:
В любом случае это из пустого в порожнее =)) Тут уж как кому удобнее... Я не говорю, что рег. выражения - это плохо... Это хорошо, но не все этим умеют на столько хорошо пользоваться :smile:
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
HukpoFuJl [?]
В рег. выражениях я шарю слабовато (да и думаю не я один такой) и данную комбинацию символов просто непонимаю

Да ведь комбинация ,то самая простая ,если ее упростить для понимания
:
Код:
$sPattern = '(?s).*?'   &    $start    &   ' (.*?)'   &    $end    &    '.*'
$sRet = StringRegExpReplace($vTest, $sPattern, '\\$1')

'.*?' ----- Удалить все ,что до $start (Не заменяя ни на что)
' (.*?)' и $1 --- Сохранить все что (в скобках) (Заменить на $1 т.е. на то,что находится в 1-ых скобках )
'.*' -----Удалить все , что после $end (Не заменяя ни на что)
'\\$1' ---- Добавить перед выражением , находящимся в скобках символ "\"
Поскольку в элементах $start и $end могут находиться символы,являющиеся спецсимволами (/, \ ,| и т.п.) регулярных выражений,
их обрамляют \Q и \E , которые отменяют- (\Q) и восстанавливают- (\E) обычное действие спецсимволов

Для примера если взять в скобки то,что после $end '(.*)' и указать
после запятой только '\2' (или' $2') - результатом будет только : бла-бла-бла
и т.п.
Я попытался описать так подробно,чтобы показать,что это выражение является простейшим и доступным для понимания человеку не очень знакомым с регулярными выражениями

CreatoR [?]
Я тоже, и я также раньше не понимал RegExp, но мне было интересно понять, потому что его возможности куда болше обычных функции работы со строками.
Присоединяюсь . Полностью согласен.Действительно возможности регулярных выражений огромны если их правильно и по необходимости применять.Достаточно начать их изучать и применять
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
Основное преимущество регвыров - их стандартность во многих ЯП. Поэтому овладев их техникой использования в одном, можно с легкостью применить в других.
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
gregaz, спасибо канешн за пояснение, суть в принципе понятна, после объяснений выражение уже не похоже на "кракозябры" :smile: Но всё же, чем держать в голове подобные строки - мне как-то проще (да и уже привычнее) пользоваться данной функцией (а пользоваться ею мне приходится часто). Работает она конечно медленнее, чем рег. выражение (см. спойлер), но пользоваться ею мне как-то удобнее...
Код:
Global $i, $a, $string, $timer, $time, $stat_time[11]
#region test
For $i = 1 To 100
	$string = StringFindSE("Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start",'$end','$start')
Next
#endregion
; test

For $a=1 To 10
	$timer = TimerInit()
	For $i = 1 To 100
		$string = StringFindSE("Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start",'$end','$start')
	Next
	$stat_time[$a] = TimerDiff($timer)
	ConsoleWrite ('Finish time: '&Round($stat_time[$a],2)&@CRLF)
Next
$time = ($stat_time[1]+$stat_time[2]+$stat_time[3]+$stat_time[4]+$stat_time[5]+$stat_time[6]+$stat_time[7]+$stat_time[8]+$stat_time[9]+$stat_time[10])/10
ConsoleWrite (@CRLF&'Среднее время: '&Round($time,3)&@CRLF&@CRLF)


Global $i, $string, $timer, $stat_time[11]
#region test
For $i = 1 To 100
	$string = StringRegExpReplace("Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start",'(?s).*?\Q' & '$end' & '\E(.*?)\Q' & '$start' & '\E.*', '$1')
Next
#endregion
; test

For $a=1 To 10
	$timer = TimerInit()
	For $i = 1 To 100
		$string = StringRegExpReplace("Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start",'(?s).*?\Q' & '$end' & '\E(.*?)\Q' & '$start' & '\E.*', '$1')
	Next
	$stat_time[$a] = TimerDiff($timer)
	ConsoleWrite ('Finish time: '&Round($stat_time[$a],2)&@CRLF)
Next
$time = ($stat_time[1]+$stat_time[2]+$stat_time[3]+$stat_time[4]+$stat_time[5]+$stat_time[6]+$stat_time[7]+$stat_time[8]+$stat_time[9]+$stat_time[10])/10
ConsoleWrite (@CRLF&'Среднее время: '&Round($time,3)&@CRLF&@CRLF)
Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
	Local $source_start,$source_count,$source
	If $start <> '' Then
		Local $st_count
		$st_count = StringSplit($start,'')
		$source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
	Else
		$source_start = 1
	EndIf
	If $end <> '' Then
		$source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
	Else
		$source_count = -1
	EndIf
	Return StringMid ($string,$source_start,$source_count)
EndFunc


А, и кстати, ещё немного доработал функцию, добавив возможность указать $start = "" как начало строки и $end="" как конец строки... И оформил в стиле справки:
[box title=Функция:]
Возвращает найденную подстроку в строке по заданным граничащим подстрокам.
StringFindSE ($string[,$start[,$end[,$start_occ[,$end_occ[,$cas]]]]])
Параметры функции:
$stringСтрока в которой будет происходить поиск
$startОпционально. Подстрока находящаяся до искомой (если значение пустое(по умолчанию) - то подстрока считается началом строки)
$endОпционально. Подстрока находящаяся после искомой (если значение пустое(по умолчанию) - то подстрока считается концом строки)
$start_occОпционально. Номер искомого вхождения подстроки $start (по умолчанию 1). Отрицательные значения означают поиск с конца строки.
$end_occОпционально. Номер искомого вхождения подстроки $end (по умолчанию 1). Отрицательные значения означают поиск с конца строки.
$casОпционально. Режим учета регистра написания.
0 = не зависит от регистра написания согласно языку пользователя (по умолчанию)
1 = зависит от регистра написания
2 = не зависит от регистра написания согласно базовому/наиболее быстрому сравнению
Возвращаемые значения:
При успешном выполнении возвращает искомую подстроку.

Примечание: Поиск подстроки $end начинается с символа, на котором заканчивается подстрока $start

Код:
Func StringFindSE($string,$start="",$end="",$start_occ=1,$end_occ=1,$cas=0)
    Local $source_start,$source_count,$source
    If $start <> '' Then
		Local $st_count
		$st_count = StringSplit($start,'')
		$source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
	Else
		$source_start = 1
	EndIf
	If $end <> '' Then
		$source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
	Else
		$source_count = -1
	EndIf
	Return StringMid ($string,$source_start,$source_count)
EndFunc
[/box]
 
Верх