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