Загрузка данных
// Главная функция: подготавливает текст и запускает парсинг построчно
Функция РаспарситьТекстБилетов(Знач ТекстБилетов) Экспорт
Результат = Новый Массив;
// 1. Очистка от экранирующих кавычек
ТекстБилетов = СтрЗаменить(ТекстБилетов, """", "");
// 2. Разбиваем текст на строки и склеиваем "разорванные" записи
МассивСырыхСтрок = СтрРазделить(ТекстБилетов, Символы.ПС, Ложь);
МассивСтрок = Новый Массив;
// Упаковываем шаблоны (строки) регулярных выражений в структуру.
// (?i) - включает игнорирование регистра в самом шаблоне.
Шаблоны = Новый Структура;
// Для СтрПодобнаПоРегулярномуВыражению (требует совпадения всей строки, поэтому добавляем .* в конец)
Шаблоны.Вставить("Начало", "(?i)^(Авиабилет|Электронный|Ж/д|Автобусный|Сверхнормативный|Доплата|а/б).*");
// Для СтрНайтиПоРегулярномуВыражению и СтрЗаменитьПоРегулярномуВыражению
Шаблоны.Вставить("Тип", "(?i)^(Авиабилет|Электронный ж/д билет|Ж/д билет|Автобусный билет|Сверхнормативный багаж[^(]*|Доплата за обмен[^(]*|а/б)");
Шаблоны.Вставить("Перевозчик", "(?i)\(\s*перевозчик\s*(.*?)(?:,\s*ИНН\s*(\d*))?\s*\)");
Шаблоны.Вставить("Класс", "(?i)\((Эконом|Купе|Плацкарт|Сидячий)\)");
Шаблоны.Вставить("Дата", "(?i)(\d{2}\.\d{2}\.\d{4}(?:\/\d{2}\.\d{2}\.\d{4})?)");
// Улучшенный шаблон пассажира: гибко ловит ФИО с инициалами (даже с опечатками в точках) или из 3-х слов, игнорируя невидимые пробелы в конце
Шаблоны.Вставить("Пассажир", "(?i)\s+([А-Яа-яЁёA-Za-z\-]+\s+[А-Яа-яЁёA-Za-z]\s*\.?\s*[А-Яа-яЁёA-Za-z]\s*\.*|[А-Яа-яЁёA-Za-z\-]+\s+[А-Яа-яЁёA-Za-z\-]+\s+[А-Яа-яЁёA-Za-z\-]+)\s*$");
Шаблоны.Вставить("НомерМаршрут", "(?i)^([A-Za-z0-9\s/\.\-]+?)\s+([А-Яа-яЁёA-Za-z].*)$");
// Для очистки от мусора перед номерами
Шаблоны.Вставить("МусорСлеш", "(?i)(к билету\s*№|Новый а/б\s*№)");
Шаблоны.Вставить("МусорУдалить", "(?i)(Квитанция\s*№|Исходный а/б\s*№|№)");
Для Каждого Стр Из МассивСырыхСтрок Цикл
СтрОчищ = СокрЛП(Стр);
Если ПустаяСтрока(СтрОчищ) Тогда
Продолжить;
КонецЕсли;
Если СтрПодобнаПоРегулярномуВыражению(СтрОчищ, Шаблоны.Начало) Тогда
МассивСтрок.Добавить(СтрОчищ);
Иначе
Если МассивСтрок.Количество() > 0 Тогда
ИндексПоследней = МассивСтрок.ВГраница();
МассивСтрок[ИндексПоследней] = МассивСтрок[ИндексПоследней] + " " + СтрОчищ;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// 3. Вызов отдельной функции парсинга
Для Каждого СтрокаБилета Из МассивСтрок Цикл
ДанныеБилета = РаспарситьСтрокуБилета(СтрокаБилета, Шаблоны);
Результат.Добавить(ДанныеБилета);
КонецЦикла;
Возврат Результат;
КонецФункции
// Выполняет пошаговый разбор одной склеенной строки билета
Функция РаспарситьСтрокуБилета(Знач СтрокаБилета, Шаблоны)
ДанныеБилета = Новый Структура;
ДанныеБилета.Вставить("ТипБилета", "");
ДанныеБилета.Вставить("Перевозчик", "");
ДанныеБилета.Вставить("ИНН", "");
ДанныеБилета.Вставить("НомерБилета", "");
ДанныеБилета.Вставить("Маршрут", "");
ДанныеБилета.Вставить("Дата", "");
ДанныеБилета.Вставить("Класс", "");
ДанныеБилета.Вставить("Пассажир", "");
ДанныеБилета.Вставить("ДопИнфо", "");
ДанныеБилета.Вставить("ИсходнаяСтрока", СтрокаБилета);
ОстатокСтроки = СтрокаБилета;
// ШАГ 1: Тип билета
ДанныеБилета.ТипБилета = ИзвлечьТипБилета(ОстатокСтроки, Шаблоны.Тип);
// ШАГ 2: Перевозчик и ИНН
ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьПеревозчикаИИнн(ОстатокСтроки, Шаблоны.Перевозчик));
// ШАГ 3: Класс обслуживания
ДанныеБилета.Класс = ИзвлечьКлассБилета(ОстатокСтроки, Шаблоны.Класс);
// ШАГ 4: Дата, Пассажир и Доп. информация
ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьДатуПассажираДопИнфо(ОстатокСтроки, Шаблоны.Дата, Шаблоны.Пассажир));
// ШАГ 5: Номер билета и маршрут
ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьНомерИМаршрут(ОстатокСтроки, Шаблоны.НомерМаршрут, Шаблоны.МусорСлеш, Шаблоны.МусорУдалить));
Возврат ДанныеБилета;
КонецФункции
// --- ШАГ 1: ТИП БИЛЕТА ---
Функция ИзвлечьТипБилета(ОстатокСтроки, ШаблонТип)
ТипБилета = "";
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонТип);
Если РезультатПоиска <> Неопределено Тогда
Блок = РезультатПоиска.Значение;
// Извлекаем первую группу с помощью замены найденного блока на $1
ТипБилета = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонТип, "$1"));
ОстатокСтроки = СокрЛП(Сред(ОстатокСтроки, РезультатПоиска.Длина + 1));
КонецЕсли;
Возврат ТипБилета;
КонецФункции
// --- ШАГ 2: ПЕРЕВОЗЧИК И ИНН ---
Функция ИзвлечьПеревозчикаИИнн(ОстатокСтроки, ШаблонПеревозчик)
Результат = Новый Структура("Перевозчик, ИНН", "", "");
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонПеревозчик);
Если РезультатПоиска <> Неопределено Тогда
Блок = РезультатПоиска.Значение;
// Перевозчик находится в первой группе ($1)
Результат.Перевозчик = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонПеревозчик, "$1"));
// ИНН находится во второй группе ($2). Очищаем от литерала "$2", если группа оказалась пустой.
ТекстИНН = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонПеревозчик, "$2"));
Результат.ИНН = СтрЗаменить(ТекстИНН, "$2", "");
ОстатокСтроки = СокрЛП(СтрЗаменить(ОстатокСтроки, Блок, ""));
КонецЕсли;
Возврат Результат;
КонецФункции
// --- ШАГ 3: КЛАСС ОБСЛУЖИВАНИЯ ---
Функция ИзвлечьКлассБилета(ОстатокСтроки, ШаблонКласс)
КлассБилета = "";
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонКласс);
Если РезультатПоиска <> Неопределено Тогда
Блок = РезультатПоиска.Значение;
КлассБилета = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонКласс, "$1"));
ОстатокСтроки = СокрЛП(СтрЗаменить(ОстатокСтроки, Блок, ""));
КонецЕсли;
Возврат КлассБилета;
КонецФункции
// --- ШАГ 4: ДАТА, ПАССАЖИР, ДОП. ИНФО ---
Функция ИзвлечьДатуПассажираДопИнфо(ОстатокСтроки, ШаблонДата, ШаблонПассажир)
Результат = Новый Структура("Дата, Пассажир, ДопИнфо", "", "", "");
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонДата);
Если РезультатПоиска <> Неопределено Тогда
Результат.Дата = РезультатПоиска.Значение;
ПозицияДаты = СтрНайти(ОстатокСтроки, Результат.Дата);
Хвост = СокрЛП(Сред(ОстатокСтроки, ПозицияДаты + СтрДлина(Результат.Дата)));
ОстатокСтроки = СокрЛП(Лев(ОстатокСтроки, ПозицияДаты - 1));
ПозЗапятой = СтрНайти(Хвост, ",");
Если ПозЗапятой > 0 Тогда
Результат.Пассажир = СокрЛП(Лев(Хвост, ПозЗапятой - 1));
Результат.ДопИнфо = СокрЛП(Сред(Хвост, ПозЗапятой + 1));
Иначе
Результат.Пассажир = Хвост;
КонецЕсли;
Иначе
// Бездатные билеты - ищем ФИО с конца строки
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонПассажир);
Если РезультатПоиска <> Неопределено Тогда
Блок = РезультатПоиска.Значение;
// $1 извлечет только саму фамилию и инициалы, отбросив невидимые пробелы по краям
Результат.Пассажир = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонПассажир, "$1"));
// Отрезаем найденный хвост от рабочей строки (используем длину всего найденного блока вместе с пробелами)
ОстатокСтроки = СокрЛП(Лев(ОстатокСтроки, СтрДлина(ОстатокСтроки) - РезультатПоиска.Длина));
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
// --- ШАГ 5: НОМЕР БИЛЕТА И МАРШРУТ ---
Функция ИзвлечьНомерИМаршрут(ОстатокСтроки, ШаблонНомерМаршрут, ШаблонМусорСлеш, ШаблонМусорУдалить)
Результат = Новый Структура("НомерБилета, Маршрут", "", "");
// Глобальная функция для очистки от лишних слов (№, Квитанция и т.д.)
ОстатокСтроки = СтрЗаменитьПоРегулярномуВыражению(ОстатокСтроки, ШаблонМусорСлеш, "/ ");
ОстатокСтроки = СтрЗаменитьПоРегулярномуВыражению(ОстатокСтроки, ШаблонМусорУдалить, "");
ОстатокСтроки = СокрЛП(ОстатокСтроки);
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонНомерМаршрут);
Если РезультатПоиска <> Неопределено Тогда
Блок = РезультатПоиска.Значение;
// Номер (всё до маршрута) - группа $1
Результат.НомерБилета = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонНомерМаршрут, "$1"));
// Маршрут (всё после) - группа $2
ТекстМаршрута = СокрЛП(СтрЗаменитьПоРегулярномуВыражению(Блок, ШаблонНомерМаршрут, "$2"));
Результат.Маршрут = СтрЗаменить(ТекстМаршрута, "$2", "");
Иначе
Результат.НомерБилета = ОстатокСтроки;
КонецЕсли;
Возврат Результат;
КонецФункции