ВНИМАНИЕ!!!! Требуется понимать, что такое регулярные выражения. Если данного знания нет, то гугл подскажет.
Сразу на примере. Есть строка типа.
ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "Ромашка", ИНН 1233455671, БИК 032405676, р/с - 10203810565780991778
Из неё нужно вытащить наименование организации, инн, бик и р/с. Вполне очевидно, представить себе строку в следующем виде
"Наименование организации", ИНН "ИНН", БИК "БИК", р/с - "Расчетный счет"
Теперь надо перевести это в более понятный машине формат
%[Наименование]%, ИНН %[ИНН]%, БИК %[БИК]%, р/с - %[РасчетныйСчет]%"
В квадратных скобках назовём параметром, остальное - разделители. Замечу, что подряд 2 разделителя или параметра быть не может (разделителем может быть и один пробел).
Тут вроде бы всё хорошо, но возникает вопрос, а что если у самого параметра определённый формат? или есть строки, в целом, одинаковые, но определённый параметр отличается, типа формата даты? Делать кучу шаблонов - не вариант. А как вообще вытащить данные, которые относятся к тому или иному параметру?? Нужны ответы, их есть у меня. Достаточно сделать так, что бы пользователь сам описал нужный формат параметра, а если он не указан, то указать какой либо по умолчанию, и пустить через RegExp.
Я это реализовал так.
%[Наименование]%, ИНН %[%(ИНН,\d+)%]%, БИК %[%(БИК,\d+)%]%, р/с - %[%(РасчетныйСчет,\d+)%]%
Если в %[]% указаны скобки %()% то в них прописывается наименование параметра и формат параметра в тексте. По умолчанию, формат [0-9A-Za-zА-Яа-я\_\"\'\ \(\)]*. Это значит что берутся все числа, английские буквы, русские буквы, разные символы и их может быть от 0 до много. При необходимости формат можно подправить. Более того в %()% могут идти через запятую. К примеру %[%(ИНН_Юр,\d{10})%,%(ИНН_Ип,\d{12})%]%. То есть, если в инн 12 чисел, то вернёт параметр ИНН_Ип, если 10, то ИНН_Юр.
Самое время начать вытягивать из текста информацию. Сделаю массив из параметров и разделителей. В данном случае получиться так.
%[Наименование]%
, ИНН
%[%(ИНН,\d+)%]%
, БИК
%[%(БИК,\d+)%]%
, р/с -
%[%(РасчетныйСчет,\d+)%]%
Теперь беру по 3 элемента массива. Именно 3, потому что при сборке паттерна после описания формата параметра обязательно должен быть разделитель. А так как параметр либо 1й, либо 2й элемент массива, то смотрю ещё и 3й.
Теперь, если в параметре прописаны отдельные форматы, название переменных, то парсю их через тот же RegExp, создаю тз с параметрами и шаблоном к ним, если отдельного формата нет, то тз будет с 1 строкой, где шаблон по умолчанию. Для каждого шаблона этого тз строю паттерн с разделителями. Если паттерн нашёлся, то вставляю имя параметра, значение паттерна, при этом со значение убираю последний разделитель, если он есть, а из разбираемой строки само значение.
То есть сначала программа пытается найти текст по следующему паттерну "^ *[0-9A-Za-zА-Яа-я\_\"\'\(\)\ ]*", +ИНН + * *", находит
"ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "Ромашка", ИНН"
Видит, что ", ИНН " - это разделитель, удаляет его из результата, и результат удаляется из строки.
На следующем шаге в строке ", ИНН 1233455671, БИК 032405676, р/с - 10203810565780991778" ищет "^ *, +ИНН + *\d+, +БИК + *", находит
", ИНН 1233455671, БИК"
удаляет что надо и так далее. На выходе получаю структуру
Наименование: ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "Ромашка"
ИНН: 1233455671
БИК: 032405676
РасчетныйСчет: 10203810565780991778
Задача решена!
Конечно, упущены нюансы, что не ищу точное количество пробелов, а допускаю что их может быть не один, спец символы в разделителях заменяю и прочее. Но на описание основной идеи это не влияет.
функция ПараметрВТЗШаблонов(знач Параметр, ИсключатьПробел, RegExp, Все = ложь)
тз = новый ТаблицаЗначений;
тз.Колонки.Добавить("Параметр");
тз.Колонки.Добавить("Шаблон");
RegExp.pattern = "\%\[[0-9A-Za-zА-Яа-я\_]*\]\%";
Поиск = RegExp.Execute(Параметр);
если Поиск.count>0 тогда
//обычное название параметра
Параметр = СтрЗаменить(Параметр,"%[","");
Параметр = СтрЗаменить(Параметр,"]%","");
нов = тз.Добавить();
нов.Параметр = Параметр;
нов.Шаблон = ?(Все,".+","[0-9A-Za-zА-Яа-я\_\.\,\""\'\(\)"+?(не ИсключатьПробел,"\ ","")+"]*");
иначе
RegExp.pattern = "\%\([0-9A-Za-zА-Яа-я_\*\+]*\,[0-9A-Za-zА-Яа-я_\[\]\(\)\.\^\\\*\+\{\}\|]*\)\%";
Поиск = RegExp.Execute(Параметр);
Для инт=0 по Поиск.count-1 цикл
ПараметрШаблон = Поиск.item(инт).Value;
//формат такой (Параметр,шаблон), при этом запятые могут быть и в шаблоне
мас = СтрРазделить(ПараметрШаблон,",");
Параметр = СокрЛП(мас[0]);
Параметр = прав(Параметр,СтрДлина(Параметр)-2);
//Шаблон, слева %(, справа )%
Шаблон = сокрЛП(СтрЗаменить(ПараметрШаблон,Параметр,""));
//убирваю скобки
Шаблон = сред(Шаблон,3,СтрДлина(Шаблон)-4);
//убираю ,
Шаблон = сокрЛП(Шаблон);
Шаблон = прав(Шаблон,СтрДлина(Шаблон)-1);
нов = тз.Добавить();
нов.Параметр = Параметр;
нов.Шаблон = Шаблон;
КонецЦикла;
КонецЕсли;
возврат тз;
КонецФункции
функция ВставитьСлужебныеСимволы(Знач СтрокаШаблон)
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"\","\\");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"/","\/");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"+","\+");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"[","\[");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"]","\]");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"{","\{");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"}","\}");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"*","\*");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,".","\.");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон,"|","\|");
СтрокаШаблон = СтрЗаменить(СтрокаШаблон," "," +");
возврат СтрокаШаблон;
КонецФункции
функция РазложитьШаблонПоПараметрам(Шаблон,RegExp)
RegExp.pattern = "\%\[.+?]%";
Поиск = RegExp.Execute(Шаблон);
Мас = новый Массив;
если Поиск.count>0 тогда
Для инт = 0 по Поиск.count-1 цикл
ТекЭл = Поиск.item(инт);
если инт <= Поиск.count-2 тогда
//беру текущий и следущий параметр, на основе их длины и начала заполняю массив
СледЭл = Поиск.item(инт+1);
если инт = 0 и ТекЭл.FirstIndex <> 0 тогда
Мас.Добавить(лев(Шаблон,ТекЭл.FirstIndex));
КонецЕсли;
Мас.Добавить(ТекЭл.value);
//рассчитываем разделитель
разделитель = сред(Шаблон,(ТекЭл.FirstIndex+ТекЭл.Length+1),(СледЭл.FirstIndex-(ТекЭл.FirstIndex+ТекЭл.Length)));
если не разделитель = "" тогда
мас.Добавить(разделитель);
КонецЕсли;
иначе
//добавляю сам параметр и остаток строки, если он есть
Мас.Добавить(ТекЭл.value);
если ТекЭл.FirstIndex+ТекЭл.Length < СтрДлина(шаблон) тогда
Мас.Добавить(прав(шаблон,СтрДлина(шаблон)-(ТекЭл.FirstIndex+ТекЭл.Length)));
КонецЕсли;
КонецЕсли;
КонецЦикла;
иначе
Мас.Добавить(Шаблон);
КонецЕсли;
возврат мас;
КонецФункции
Функция РазложитьСтрокуПоШаблону(Знач Шаблон,Знач Текст, RegExpКомп = Неопределено) Экспорт
если RegExpКомп = Неопределено тогда
RegExp = новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;
иначе
RegExp = RegExpКомп;
КонецЕсли;
//1 выделяю параметры в квадратных скобках
//нужна правильная скобочная запись, посчитаем
МассивРазделенныхПараметров = РазложитьШаблонПоПараметрам(Шаблон,RegExp);
//беру 3 элемента массива, если это начало строки или конец, то может быть и 2, и 1 элемент
//смотрю, какие элементы являются разделителем параметров
//возможны случаи: ПРР, ПРП, РПР - остальное ошибка и не обрабатывается
РасмТекст = Текст;
РасмТекст = СтрЗаменить(РасмТекст,Символы.ПС," ");
РасмТекст = СтрЗаменить(РасмТекст,Символы.ВТаб," ");
РасмТекст = СтрЗаменить(РасмТекст,Символы.ВК," ");
РасмТекст = СтрЗаменить(РасмТекст,Символы.НПП," ");
УбратьДвойныеПробелы(РасмТекст);
УбратьДвойныеПробелы(Шаблон);
СтруктураРеквизитов = новый Структура;
Пока МассивРазделенныхПараметров.Количество()<>0 цикл
Если МассивРазделенныхПараметров.Количество()>2 тогда
ПодСтр1 = МассивРазделенныхПараметров[0];
ПодСтр2 = МассивРазделенныхПараметров[1];
ПодСтр3 = МассивРазделенныхПараметров[2];
иначеЕсли МассивРазделенныхПараметров.Количество()>1 тогда
ПодСтр1 = МассивРазделенныхПараметров[0];
ПодСтр2 = МассивРазделенныхПараметров[1];
ПодСтр3 = "";
иначе
ПодСтр1 = МассивРазделенныхПараметров[0];
ПодСтр2 = "";
ПодСтр3 = "";
КонецЕсли;
//нужно определить, какая подстрока разделитель. Так как если разделитель " ", и идет за параметром,
//и параметр без отдельного шаблона, то в шаблоне параметра нужно удалить пробел.
ПодСтр1Разделитель = не (лев(ПодСтр1,2) = "%[" и прав(ПодСтр1,2)="]%");
ПодСтр2Разделитель = не (лев(ПодСтр2,2) = "%[" и прав(ПодСтр2,2)="]%");
ПодСтр3Разделитель = не (лев(ПодСтр3,2) = "%[" и прав(ПодСтр3,2)="]%");
//элементы не могут быть одновременно ни разделителями, ни параметрами
если МассивРазделенныхПараметров.Количество()>1 и не ((ПодСтр1Разделитель и ПодСтр2Разделитель) или не (ПодСтр1Разделитель и ПодСтр2Разделитель)) тогда
//ошибка
Найден = ложь;
Прервать;
ИначеЕсли МассивРазделенныхПараметров.Количество()>2 и не ((ПодСтр1Разделитель и ПодСтр2Разделитель) или не (ПодСтр1Разделитель и ПодСтр2Разделитель))
и не ((ПодСтр2Разделитель и ПодСтр3Разделитель) или не (ПодСтр2Разделитель и ПодСтр3Разделитель)) тогда
Найден = ложь;
Прервать;
КонецЕсли;
если не ПодСтр3Разделитель тогда
//нужно убрать этот параметр
ПодСтр3 = "";
ПодСтр3Разделитель = истина;
КонецЕсли;
//так как на одну позицию параметра возможны несколько вариантов отображения, то парсим варианты и проходим последователдьно
Если не ПодСтр1Разделитель тогда
МассивШаблонов = ПараметрВТЗШаблонов(ПодСтр1,СокрЛП(ПодСтр1)="",RegExp,МассивРазделенныхПараметров.Количество()=1);
ИначеЕсли не ПодСтр2Разделитель тогда
МассивШаблонов = ПараметрВТЗШаблонов(ПодСтр2,СокрЛП(ПодСтр2)="",RegExp);
иначе
МассивШаблонов = новый ТаблицаЗначений;
КонецЕсли;
Для каждого шаблон из МассивШаблонов цикл
СтрШаблон = "^ *";//начало строки
Если ПодСтр1Разделитель тогда
СтрШаблон = СтрШаблон + ВставитьСлужебныесимволы(ПодСтр1)+" *";
КонецЕсли;
СтрШаблон = СтрШаблон + шаблон.Шаблон;
Если ПодСтр2Разделитель тогда
СтрШаблон = СтрШаблон + ВставитьСлужебныесимволы(ПодСтр2)+" *";
КонецЕсли;
Если ПодСтр3Разделитель тогда
СтрШаблон = СтрШаблон + ВставитьСлужебныесимволы(ПодСтр3)+" *";
КонецЕсли;
RegExp.pattern = СтрШаблон;
Поиск = RegExp.Execute(РасмТекст);
если Поиск.count>0 тогда
НайдЗнач = Поиск.item(0).value;
//нужно удалить разделители.
Если ПодСтр1Разделитель тогда
НайдЗнач = прав(сокрЛП(НайдЗнач),СтрДлина(сокрЛП(НайдЗнач)) - СтрДлина(сокрЛП(ПодСтр1)));
КонецЕсли;
Если ПодСтр2Разделитель тогда
НайдЗнач = Лев(сокрЛП(НайдЗнач),СтрДлина(сокрЛП(НайдЗнач)) - СтрДлина(сокрЛП(ПодСтр2)));
КонецЕсли;
Если ПодСтр3Разделитель тогда
НайдЗнач = Лев(сокрЛП(НайдЗнач),СтрДлина(сокрЛП(НайдЗнач)) - СтрДлина(сокрЛП(ПодСтр3)));
КонецЕсли;
СтруктураРеквизитов.Вставить(шаблон.Параметр,сокрЛП(НайдЗнач));
РасмТекст = прав(РасмТекст,СтрДлина(РасмТекст)-(Поиск.item(0).Length - ?(ПодСтр2Разделитель,СтрДлина(ПодСтр2),?(ПодСтр3Разделитель,СтрДлина(ПодСтр3),0))));
иначе
Найден = ложь;
КонецЕсли;
КонецЦикла;
Если МассивРазделенныхПараметров.Количество()>1 и не ПодСтр2Разделитель тогда
//значит параметр в середине, удалить первый разделитель и параметр
МассивРазделенныхПараметров.Удалить(1);
МассивРазделенныхПараметров.Удалить(0);
Иначе
МассивРазделенныхПараметров.Удалить(0);
КонецЕсли;
если Найден = ложь тогда
прервать;
КонецЕсли;
КонецЦикла;
если Найден = ложь тогда
возврат новый Структура;
иначе
возврат СтруктураРеквизитов;
КонецЕсли;
КонецФункции
Процедура УбратьДвойныеПробелы(текст) Экспорт
Пока найти(текст," ")>0 цикл
текст = СтрЗаменить(текст," "," ");
КонецЦикла;
КонецПроцедуры
Пример использования: СтруктураРеквизитов = РазложитьСтрокуПоШаблону(Шаблон,текст);