Добрый день, уважаемые Планетяне! Название темы такое выбрано для облегчения поиска. Впоследствии на эту тему можно будет ссылаться, т.к. решение должно получиться универсальным Вот здесь я влез в чужую тему с попыткой разобраться в возможностях применения RegExp для решения моей задачи. Ребята мне (и надеюсь не только мне) очень помогли - родилось 2 универсальные функции на регулярках: замена и извлечение. Долгое время этого хватало, но теперь, когда настало время извлечения данных из текста другого форматирования, появились проблемы.
В файле-примере подробное описание проблемы, текущая ситуация и желаемый результат.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Доброе время суток Jack Famous, честно говоря не понял пример, то ли вы хотите удалить переносы строки (тогда удаляете \n\r), то ли извлечь текст с квадратными скобками, тогда \[.+?\] для извлечения.
Function QQ(s$)
With CreateObject("VBScript.RegExp")
.Global = True
.IgnoreCase = False
.MultiLine = False
.Pattern = "(?:\(|\[)(.+?)(?:\)|\])"
If .Test(s) Then
For Each sM In .Execute(s)
ss = ss & Chr(10) & sM.SubMatches(0)
Next
QQ = Mid(ss, 2)
End If
End With
End Function
Здравствуйте, Андрей VG! удалить переносы мне проще через Ctrl+H. Слева от текста находятся КРУГЛЫЕ скобки, справа - КВАДРАТНЫЕ. Ваш шаблон работает только если каждая пара скобок отделена хотя бы одним ПЕРЕНОСОМ (разрывом) строки и был изначально в примере. RAN, всё работает (причём с любым количеством пар, вроде) - спасибо огромное! Кстати, можно было ИЛИ не использовать, т.к. ВСЕГДА слева круглые скобки, а справа квадратные, но на будущее усёк))))
Осталась ещё пара вопросов: 1. как НЕ извлекать разделители (то есть скобки слева и справа, в данном случае) 2. как исправить НЕрегулярную UDF (есть в файле, чтобы точно также работала)
P.S.: пользовался ТОЛЬКО шаблонами, используя универсальную UDF из файла. Заменил файл-пример в заголовке темы. Дублирую тут…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Я вот так и знал - вот прямо чувствовал, что кто-то подколет по "моей" теме )))))) Юрий М, это решение для извлечения всех вхождений данных между 2мя ВСЕГДА попарно одинаковыми разделителями (то есть, например, извлечь все данные из "контейнеров", ограниченных слева "пробелом", а справа ";". Очень хорошо это иллюстрирует НЕрегулярная UDF из примера - она как раз под это заточена. А вариант RAN, существенно расширяет границы извлечения и, безусловно, будет жизненно необходим при более рандомных условиях
К тому же, в подавляющем большинстве вопросов по этой теме на форуме необходимо как раз ЭТО (буду сюда отправлять), а более-менее длинные шаблоны для RegExp не вызывают желания понять суть их работы и являются разовыми решениями (опять же - для большинства из задающих вопросы)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
RAN, не совсем понял, что конкретно вы имеете ввиду. Дело в том, что UDF (не на регулярках), извлекающая данные между двумя разделителями действительно извлекает безразделителей, НО только ПЕРВОЕ вхождение. В то же время, UDF на регулярках извлекает ВСЕ вхождения, однако и разделители цепляет... а короче говоря: 1. Извлекает, как надо, но только первое 2. Извлекает всё, что нужно, но результат извлечения нуждается в дополнительной очистке
Я, конечно, надеюсь, что в итоге кто-нибудь из VBA-мастеров сделает (или скажет, где это уже решено) UDF или макрос, в которой(ом) нужно будет указать 2 разделителя (начальный и конечный), а также разделитель сцепления (через что будет сцеплено найденное) - считаю этот вариант самым оптимальным в использовании, но не знаю, насколько он труден в исполнении. А пока - только регулярками)))))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
RAN, прошу прощения...не понял)))) как и написал - использовал ТОЛЬКО шаблон для UDF «ВзятьРегулярками» из примера (в таком случае, разделители остаются). Добавлю ВАШУ UDF в коллекцию - огромное спасибо!!! Также большое спасибо, buchlotnik, за подсказку! - я-то решил, что RAN, имел ввиду нерегулярную функцию «ВзятьИзРазделителей» из примера
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Кто-нибудь, подскажите, как внутрь шаблона "засунуть" переменные del1 и del2, которые будут указываться в функции (со знаком сцепления я разберусь) Что-то типа .Pattern = "(?:del1)(.+?)(?:del2)" - но это, естественно, не сработает, как надо))))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Изменено: Jack Famous - 06.06.2016 16:38:03(Перезалил пример)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Function reg_del(t$, d1$, d2$)
With CreateObject("VBScript.regexp")
.Global = True
.Pattern = "(?:\" & d1 & ")(.+?)(?:\" & d2 & ")"
If .Test(t) Then
For Each sM In .Execute(t)
ss = ss & Chr(10) & sM.SubMatches(0)
Next
reg_del = Mid(ss, 2)
End If
End With
End Function
издеваетесь? вы переименовали del1 и del2 в del_begin и del_finish, паттерн при этом заменить не потрудились и да - точка перед Pattern тоже значение имеет
buchlotnik, не издеваюсь…косячнул, т.к. поторопился… Изменял, т.к. мне удобнее. Ваш пример отлично работает. Убрал "\" из константы - при необходимости буду в переменной указывать. Всё работает - спасибо вам большое!
Код
'Вытащить значения между двумя разделителями (несколько вхождений)
'Особенность: Используются регулярные выражения. В переменных del1 и del2 при необходимости указывать "\". Если указать в качестве знака сцепления "перенос", то будет использован перенос строки
'Автор: RAN
'Главный редактор: buchlotnik (сделал UDF универсальной)
'Тема на форуме: http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=78513&TITLE_SEO=78513-izvlecheniya-dannykh-iz-skobok&MID=657681&result=edit#postform
'==================================================================================================================================================================================================
Function ВзятьИзРазделителейРегулярками(cell$, del1$, del2$, del_merge$)
If del_merge = "перенос" Then del_merge = Chr(10)
With CreateObject("VBScript.regexp")
.Global = True
.Pattern = "(?:" & del1 & ")(.+?)(?:" & del2 & ")"
If .Test(cell) Then
For Each sM In .Execute(cell)
ss = ss & del_merge & sM.SubMatches(0)
Next
ВзятьИзРазделителейРегулярками = Mid(ss, Len(del_merge)+1)
End If
End With
End Function
Изменено: Jack Famous - 07.06.2016 10:57:23(стыдоба))))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Public Function GetInternals(ByVal Text As String, ByVal Delimiter As String, _
ByVal leftSign As String, ByVal rightSign As String) As String
Dim leftPos As Long, rightPos As Long, sOut As String
Dim leftLen As Long, rightLen As Long
leftLen = Len(leftSign): rightLen = Len(rightSign)
sOut = "": rightPos = -rightLen + 1
Do
leftPos = InStr(rightPos + rightLen, Text, leftSign, vbTextCompare)
If leftPos = 0 Then Exit Do
rightPos = InStr(leftPos + leftLen + 1, Text, rightSign, vbTextCompare)
If rightPos = 0 Then Exit Do
sOut = sOut & Mid$(Text, leftPos + leftLen, rightPos - leftPos - leftLen) & Delimiter
Loop
If Len(sOut) > Len(Delimiter) Then sOut = Left$(sOut, Len(sOut) - Len(Delimiter))
GetInternals = sOut
End Function
Тест на 10000 проходах в цикле, лучшее время по 5 запускам 0,1855469 Для сравнения ВзятьИзРазделителейРегулярками - 1,808594 Успехов.
buchlotnik, точно - я же изменил перенос строки на переменную ...стыдоба))))) спасибо большое!!! - только начинаю вникать… Тело макроса поправил Андрей VG, огромное спасибо за НЕрегулярное решение проблемы! я-то уже начал инструкцию писать для краткого ознакомления с синтаксисом RegExp (когда применять «\» а когда нет)))))
16.09.2016. Чуть приукрасил для удобства использования
Вариант с регулярками: от RAN и buchlotnik
Код
'Вытащить регулярными выражениями значения между двумя разделителями (несколько вхождений)
'Автор: RAN
'Главный редактор: buchlotnik (сделал UDF универсальной)
'Сделал использование функции чуть понятнее для новичков: Jack_Famous
'Тема на форуме: http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=78513&TITLE_SEO=78513-izvlecheniya-dannykh-iz-skobok&MID=657681&result=edit#postform
'Аргументы функции:
''Особенность: Используются регулярные выражения. В переменных РазделительСлева и РазделительСправа при необходимости указывать "\". Если указать в качестве знака сцепления "10", то будет использован перенос строки
''Аргументы функции:
'Текст - единственный обязательный аргумент. Ссылка на ячейку с текстом
'РазделительСлева - необязательный аргумент. Граница слева, после которой начнётся извлечение символов. По умолчанию = "("
'РазделительСправа - необязательный аргумент. Граница справа, перед которой прекратится извлечение символов. По умолчанию = ")"
'СцепитьЧерез - необязательный аргумент. Если указать "10", то будет использован перенос строки. По умолчанию = "; "
'======================================================================================================================================================================================================================
Function ВзятьИзРазделителейРегулярками(Текст, Optional РазделительСлева As String = "(", Optional РазделительСправа As String = ")", Optional СцепитьЧерез As String = "; ") As String
If СцепитьЧерез = "10" Then СцепитьЧерез = Chr(10)
With CreateObject("VBScript.regexp")
.Global = True
.Pattern = "(?:" & РазделительСлева & ")(.+?)(?:" & РазделительСправа & ")"
If .Test(Текст) Then
For Each sM In .Execute(cell)
ss = ss & СцепитьЧерез & sM.SubMatches(0)
Next
ВзятьИзРазделителейРегулярками = Mid(ss, Len(СцепитьЧерез) + 1)
End If
End With
НЕрегулярное решение от Андрей VG:
Код
'Вытащить значения между двумя разделителями (несколько вхождений)
'Автор: Андрей VG
'Сделал использование функции чуть понятнее для новичков: Jack_Famous
'Тема на форуме: http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=78513&TITLE_SEO=78513-izvlecheniya-dannykh-iz-skobok&MID=657699&result=edit#message657699
'Аргументы функции:
'Текст - единственный обязательный аргумент. Ссылка на ячейку с текстом
'РазделительСлева - необязательный аргумент. Граница слева, после которой начнётся извлечение символов. По умолчанию = "("
'РазделительСправа - необязательный аргумент. Граница справа, перед которой прекратится извлечение символов. По умолчанию = ")"
'СцепитьЧерез - необязательный аргумент. Если указать "10", то будет использован перенос строки. По умолчанию = "; "
'===================================================================================================================================================================================
Public Function ВзятьИзРазделителей(ByVal Текст As String, Optional ByVal РазделительСлева As String = "(", Optional ByVal РазделительСправа As String = ")", Optional ByVal СцепитьЧерез As String = "; ") As String
If СцепитьЧерез = "10" Then СцепитьЧерез = Chr(10)
Dim leftPos As Long, rightPos As Long, sOut As String
Dim leftLen As Long, rightLen As Long
leftLen = Len(РазделительСлева): rightLen = Len(РазделительСправа)
sOut = "": rightPos = -rightLen + 1
Do
leftPos = InStr(rightPos + rightLen, Текст, РазделительСлева, vbTextCompare)
If leftPos = 0 Then Exit Do
rightPos = InStr(leftPos + leftLen + 1, Текст, РазделительСправа, vbTextCompare)
If rightPos = 0 Then Exit Do
sOut = sOut & Mid$(Текст, leftPos + leftLen, rightPos - leftPos - leftLen) & СцепитьЧерез
Loop
If Len(sOut) > Len(СцепитьЧерез) Then sOut = left$(sOut, Len(sOut) - Len(СцепитьЧерез))
ВзятьИзРазделителей = sOut
End Function
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄