Загрузка данных


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

КонецФункции

// Выполняет пошаговый разбор одной склеенной строки билета
Функция РаспарситьСтрокуБилета(Знач СтрокаБилета, Шаблоны)
    
    ДанныеБилета = Новый Структура;
    ДанныеБилета.Вставить("ТипБилета", "");
    ДанныеБилета.Вставить("Перевозчик", "");
    ДанныеБилета.Вставить("ИНН", "");
    ДанныеБилета.Вставить("НомерБилета", "");
    ДанныеБилета.Вставить("Маршрут", "");
    ДанныеБилета.Вставить("Дата", "");
    ДанныеБилета.Вставить("Класс", "");
    ДанныеБилета.Вставить("Пассажир", "");
    ДанныеБилета.Вставить("ДопИнфо", "");
    ДанныеБилета.Вставить("ИсходнаяСтрока", СтрокаБилета); 
    
    ОстатокСтроки = СтрокаБилета;
    
    // ШАГ 1: Тип билета
    ДанныеБилета.ТипБилета = ИзвлечьТипБилета(ОстатокСтроки, Шаблоны.Тип);
    
    // ШАГ 2: Перевозчик и ИНН
    ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьПеревозчикаИИнн(ОстатокСтроки, Шаблоны.Перевозчик));
    
    // ШАГ 3: Класс обслуживания
    ДанныеБилета.Класс = ИзвлечьКлассБилета(ОстатокСтроки, Шаблоны.Класс);
    
    // ШАГ 4: Дата, Пассажир и Доп. информация
    ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьДатуПассажираДопИнфо(ОстатокСтроки, Шаблоны.Дата, Шаблоны.Пассажир));
    
    // ШАГ 5: Номер билета и маршрут
    ЗаполнитьЗначенияСвойств(ДанныеБилета, ИзвлечьНомерИМаршрут(ОстатокСтроки, Шаблоны.НомерМаршрут, Шаблоны.МусорСлеш, Шаблоны.МусорУдалить));
    
    Возврат ДанныеБилета;

КонецФункции

// --- ШАГ 1: ТИП БИЛЕТА ---
Функция ИзвлечьТипБилета(ОстатокСтроки, ШаблонТип)
    ТипБилета = "";
    
    // Использование глобальной функции СтрНайтиПоРегулярномуВыражению
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонТип);
    Если РезультатПоиска <> Неопределено Тогда
        ТипБилета = СокрЛП(РезультатПоиска.Значение);
        ОстатокСтроки = СокрЛП(Сред(ОстатокСтроки, РезультатПоиска.Длина + 1));
    КонецЕсли;
    
    Возврат ТипБилета;
КонецФункции

// --- ШАГ 2: ПЕРЕВОЗЧИК И ИНН ---
Функция ИзвлечьПеревозчикаИИнн(ОстатокСтроки, ШаблонПеревозчик)
    Результат = Новый Структура("Перевозчик, ИНН", "", "");
    
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонПеревозчик);
    Если РезультатПоиска <> Неопределено Тогда
        Группы = РезультатПоиска.Группы;
        Если Группы.Количество() > 0 И Группы[0] <> Неопределено Тогда
            Результат.Перевозчик = СокрЛП(Группы[0].Значение);
        КонецЕсли;
        Если Группы.Количество() > 1 И Группы[1] <> Неопределено Тогда
            Результат.ИНН = СокрЛП(Группы[1].Значение);
        КонецЕсли;
        
        ОстатокСтроки = СокрЛП(СтрЗаменить(ОстатокСтроки, РезультатПоиска.Значение, ""));
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

// --- ШАГ 3: КЛАСС ОБСЛУЖИВАНИЯ ---
Функция ИзвлечьКлассБилета(ОстатокСтроки, ШаблонКласс)
    КлассБилета = "";
    
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонКласс);
    Если РезультатПоиска <> Неопределено Тогда
        Группы = РезультатПоиска.Группы;
        Если Группы.Количество() > 0 И Группы[0] <> Неопределено Тогда
            КлассБилета = СокрЛП(Группы[0].Значение);
        КонецЕсли;
        ОстатокСтроки = СокрЛП(СтрЗаменить(ОстатокСтроки, РезультатПоиска.Значение, ""));
    КонецЕсли;
    
    Возврат КлассБилета;
КонецФункции

// --- ШАГ 4: ДАТА, ПАССАЖИР, ДОП. ИНФО ---
Функция ИзвлечьДатуПассажираДопИнфо(ОстатокСтроки, ШаблонДата, ШаблонПассажир)
    Результат = Новый Структура("Дата, Пассажир, ДопИнфо", "", "", "");
    
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонДата);
    Если РезультатПоиска <> Неопределено Тогда
        Результат.Дата = РезультатПоиска.Значение;
        
        ПозицияДаты = СтрНайти(ОстатокСтроки, Результат.Дата);
        Хвост = СокрЛП(Сред(ОстатокСтроки, ПозицияДаты + СтрДлина(Результат.Дата)));
        ОстатокСтроки = СокрЛП(Лев(ОстатокСтроки, ПозицияДаты - 1)); 
        
        ПозЗапятой = СтрНайти(Хвост, ",");
        Если ПозЗапятой > 0 Тогда
            Результат.Пассажир = СокрЛП(Лев(Хвост, ПозЗапятой - 1));
            Результат.ДопИнфо  = СокрЛП(Сред(Хвост, ПозЗапятой + 1));
        Иначе
            Результат.Пассажир = Хвост;
        КонецЕсли;
    Иначе
        // Бездатные билеты - ищем ФИО с конца строки
        РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонПассажир);
        Если РезультатПоиска <> Неопределено Тогда
            Группы = РезультатПоиска.Группы;
            Если Группы.Количество() > 0 И Группы[0] <> Неопределено Тогда
                Результат.Пассажир = СокрЛП(Группы[0].Значение);
            КонецЕсли;
            ОстатокСтроки = СокрЛП(Лев(ОстатокСтроки, СтрДлина(ОстатокСтроки) - РезультатПоиска.Длина));
        КонецЕсли;
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

// --- ШАГ 5: НОМЕР БИЛЕТА И МАРШРУТ ---
Функция ИзвлечьНомерИМаршрут(ОстатокСтроки, ШаблонНомерМаршрут, ШаблонМусорСлеш, ШаблонМусорУдалить)
    Результат = Новый Структура("НомерБилета, Маршрут", "", "");
    
    // Использование глобальной функции СтрЗаменитьПоРегулярномуВыражению
    ОстатокСтроки = СтрЗаменитьПоРегулярномуВыражению(ОстатокСтроки, ШаблонМусорСлеш, "/ ");
    ОстатокСтроки = СтрЗаменитьПоРегулярномуВыражению(ОстатокСтроки, ШаблонМусорУдалить, "");
    ОстатокСтроки = СокрЛП(ОстатокСтроки);
    
    РезультатПоиска = СтрНайтиПоРегулярномуВыражению(ОстатокСтроки, ШаблонНомерМаршрут);
    Если РезультатПоиска <> Неопределено Тогда
        Группы = РезультатПоиска.Группы;
        Если Группы.Количество() > 0 И Группы[0] <> Неопределено Тогда
            Результат.НомерБилета = СокрЛП(Группы[0].Значение);
        КонецЕсли;
        Если Группы.Количество() > 1 И Группы[1] <> Неопределено Тогда
            Результат.Маршрут = СокрЛП(Группы[1].Значение);
        КонецЕсли;
    Иначе
        Результат.НомерБилета = ОстатокСтроки;
    КонецЕсли;
    
    Возврат Результат;
КонецФункции