Как просто разобрать строку с двоеточиями и получить пары Ключ-Значение

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработчика 1С часто встречается задача разбора неструктурированных строк, приходящих из внешних систем (банковские выписки, логи, текстовые файлы). Типичный пример такой строки: "Ключ1: Значение Ключ2: Другое значение". Сложность здесь заключается в том, что пробелы могут присутствовать как в самих значениях, так и разделять пары. Разберем наиболее эффективные способы решения этой задачи без привлечения тяжеловесных инструментов вроде регулярных выражений (RegExp).

Анализ структуры данных

Прежде чем приступать к написанию кода, проанализируем входящие данные. В нашем случае строка имеет вид: "Aaaa: ловыа ва лывоа Bbb.bb: sdfkjfsd lksdf Cccc: kjlsdfl lsd". Мы видим, что:

  1. Ключ всегда заканчивается двоеточием и не содержит пробелов (но может содержать точки).
  2. Значение может состоять из нескольких слов, разделенных пробелами.
  3. Разделителем между значением предыдущего параметра и именем следующего ключа является обычный пробел.

Метод 1: Разделение по двоеточиям с последующим выделением ключа

Рассмотрим самый логичный подход. Мы можем разделить всю строку на массив, используя символ ":" в качестве разделителя. Однако при таком подходе имя следующего ключа окажется в конце предыдущего элемента массива. Разберем этот алгоритм по шагам.

Проанализируем программную реализацию этого метода:


Функция РазобратьСтрокуПараметров(Знач ВходнаяСтрока)
    Результат = Новый Соответствие;
    
    // 1. Разбиваем строку по двоеточиям
    МассивЧастей = СтрРазделить(ВходнаяСтрока, ":");
    
    Если МассивЧастей.Количество() < 2 Тогда
        Возврат Результат;
    КонецЕсли;

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

Этот метод хорош тем, что он устойчив к наличию точек в именах ключей и пробелов в значениях. Мы используем встроенную функцию СтрНайти с параметром НаправлениеПоиска.СКонца, что позволяет нам эффективно "отрезать" имя ключа от текста значения.

Метод 2: Посимвольный разбор (Токенизация)

Рассмотрим альтернативный вариант, который предложили участники обсуждения: разбить строку по пробелам и проверять каждое "слово". Если слово заканчивается на двоеточие — перед нами ключ.

Выясним причину, по которой этот метод может быть удобен: он позволяет обрабатывать строку "потоком", не создавая промежуточных больших подстрок. Посмотрим на пример реализации:


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

Важный нюанс: метод токенизации по пробелам (СтрРазделить(Строка, " ")) будет работать корректно только в том случае, если двоеточие всегда приклеено к ключу (например, "Ключ:"), а не отделено пробелом ("Ключ :"). Если в ваших данных возможны пробелы перед двоеточием, используйте предварительную обработку функцией СтрЗаменить(Строка, " :", ":").

Сравнение подходов и производительность

Проанализируем ситуацию с большими объемами данных. Если вы обрабатываете строки длиной в мегабайты (например, при загрузке логов):

Рассмотрим подробнее вопрос "особых случаев". Что если значение само содержит двоеточие (например, время 12:00)? В этой ситуации все методы, основанные на простом СтрРазделить по двоеточию, потребуют дополнительной проверки: является ли слово перед двоеточием валидным ключом. В таких случаях метод посимвольного перебора строки оказывается наиболее гибким для доработки логики.

Резюме

Для большинства задач оптимальным будет Метод 1 (разбиение по двоеточию + СтрНайти с конца). Он сочетает в себе читаемость кода и высокую скорость работы. При написании таких парсеров всегда помните о функции СокрЛП, так как лишние пробелы — главный враг при сопоставлении ключей в Соответствии или Структуре.

← На главную