Как в 1С разделить строку с номером телефона и текстовым комментарием?

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

Нередко при импорте контактов или из-за некорректного ручного ввода пользователи заносят в поле номера телефона дополнительную информацию, например: «380000000000 (директор)» или «+7 (495) 111-22-33: Иванов А. Б.». В результате в базе скапливаются тысячи записей, где в одном строковом реквизите смешаны цифры номера и текстовые пояснения. Выясним причину этой проблемы и разберем практические способы программного разделения таких строк на чистый номер и комментарий.

Вариант 1. Посимвольный разбор до первого нечислового символа

Рассмотрим самый простой сценарий: номер расположен строго в начале строки, а за ним следует пояснение в скобках или через пробел. В этом случае мы можем последовательно перебирать символы строки с помощью функций Сред() и КодСимвола(). Как только алгоритм встречает символ с кодом вне диапазона цифр (коды ASCII от 48 до 57 соответствуют цифрам от 0 до 9), строка разбивается на две части.

Посмотрим на пример реализации:


ИсходнаяСтрока = СокрЛП(СтрокаКонтакта);
Длина = СтрДлина(ИсходнаяСтрока);
СтрокаНомер = "";
СтрокаКомментарий = "";

Для Инд = 1 По Длина Цикл
    Символ = Сред(ИсходнаяСтрока, Инд, 1);
    КодСимвола = КодСимвола(Символ);
    
    Если КодСимвола >= 48 И КодСимвола <= 57 Тогда
        СтрокаНомер = СтрокаНомер + Символ;
    Иначе
        СтрокаКомментарий = СокрЛП(Сред(ИсходнаяСтрока, Инд, Длина));
        Прервать;
    КонецЕсли;
КонецЦикла;

Такой подход отлично справляется с простыми случаями, когда номер состоит исключительно из слитных цифр, но не учитывает наличие в номере знаков «+», скобок или дефисов.

Вариант 2. Разделение строки по первой букве с очисткой обрамляющих скобок

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

Разберем по шагам данный алгоритм:


СтрокаНомер = "";
СтрокаКомментарий = "";
стрДлина = СтрДлина(ИсходнаяСтрока);

Для х = 1 По стрДлина Цикл
    симв = Сред(ИсходнаяСтрока, х, 1);
    
    // Проверяем, является ли символ цифрой
    Если (симв >= "0") И (симв <= "9") Тогда
        СтрокаНомер = СтрокаНомер + симв;
    // Находим начало буквенного комментария
    ИначеЕсли (симв >= "А" И симв <= "я") ИЛИ симв = "Ё" ИЛИ симв = "ё" Тогда
        СтрокаКомментарий = Сред(ИсходнаяСтрока, х);
        Прервать;
    КонецЕсли;
КонецЦикла;

// Очищаем закрывающую скобку в конце комментария, если перед ним была открывающая скобка
Если Прав(СтрокаКомментарий, 1) = ")" И х > 1 И Сред(ИсходнаяСтрока, х - 1, 1) = "(" Тогда
    СтрокаКомментарий = Лев(СтрокаКомментарий, СтрДлина(СтрокаКомментарий) - 1);
КонецЕсли;

СтрокаКомментарий = СокрЛП(СтрокаКомментарий);

Важный нюанс: при проверке диапазонов символов встроенного языка кириллические буквы «Ё» и «ё» имеют коды 1025 и 1105, которые не входят в непрерывный диапазон кодов от «А» до «я» (1040–1103). Их необходимо проверять отдельно, чтобы избежать логических ошибок при разборе фамилий или должностей.

Вариант 3. Поиск границы между номером и должностью через разделители

Рассмотрим более гибкий подход, когда номер телефона может содержать форматирование («+7 (495) 111-22-33»), а за ним следует пояснение («Иванов А. Б.»). Алгоритм находит первую осмысленную букву, после чего выполняет шаг назад для поиска символа-разделителя (пробел, двоеточие или скобка) и делит исходную строку на две части.

Посмотрим на пример функции:


Функция РазделитьНомерИПояснение(ИсходнаяСтрока)
    Длина = СтрДлина(ИсходнаяСтрока);
    ПозицияТекста = 0;
    
    // Ищем первый буквенный символ
    Для Инд = 1 По Длина Цикл
        Символ = ВРег(Сред(ИсходнаяСтрока, Инд, 1));
        Если (Символ >= "А" И Символ <= "Я") ИЛИ (Символ >= "A" И Символ <= "Z") ИЛИ Символ = "Ё" Тогда
            ПозицияТекста = Инд;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Если ПозицияТекста = 0 Тогда
        Возврат Новый Структура("Номер, Комментарий", СокрЛП(ИсходнаяСтрока), "");
    КонецЕсли;
    
    // Отступаем назад, отсекая разделители
    ПозицияРазделителя = ПозицияТекста;
    Пока ПозицияРазделителя > 1 Цикл
        СимволПред = Сред(ИсходнаяСтрока, ПозицияРазделителя - 1, 1);
        Если Найти(" :-(", СимволПред) > 0 Тогда
            ПозицияРазделителя = ПозицияРазделителя - 1;
        Иначе
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Номер = СокрЛП(Лев(ИсходнаяСтрока, ПозицияРазделителя - 1));
    Комментарий = СокрЛП(Сред(ИсходнаяСтрока, ПозицияТекста));
    
    // Удаляем концевую скобку у комментария, если она осталась
    Если Прав(Комментарий, 1) = ")" Тогда
        Комментарий = СокрЛП(Лев(Комментарий, СтрДлина(Комментарий) - 1));
    КонецЕсли;
    
    Возврат Новый Структура("Номер, Комментарий", Номер, Комментарий);
КонецФункции

Вариант 4. Использование функций Библиотеки стандартных подсистем (БСП)

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

Разберем пример извлечения блоков:


Функция РазложитьНаМногострочнуюСВыделениемЦифр(ИсходнаяСтрока)
    МассивНеСтрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивСлов(ИсходнаяСтрока, "0123456789");
    ТекущийОтрезок = ИсходнаяСтрока;
    Результат = "";
    
    Пока НЕ ПустаяСтрока(ТекущийОтрезок) Цикл
        Если МассивНеСтрок.Количество() = 0 Тогда
            Результат = Результат + Символы.ПС + ТекущийОтрезок;
            ТекущийОтрезок = "";
        Иначе
            ИндексНеЦифровогоОтрезка = Найти(ТекущийОтрезок, МассивНеСтрок[0]);
            Результат = Результат + Символы.ПС + Лев(ТекущийОтрезок, ИндексНеЦифровогоОтрезка - 1);
            Результат = Результат + Символы.ПС + МассивНеСтрок[0];
            ТекущийОтрезок = Сред(ТекущийОтрезок, ИндексНеЦифровогоОтрезка + СтрДлина(МассивНеСтрок[0]));
            МассивНеСтрок.Удалить(0);
        КонецЕсли;
    КонецЦикла;
    
    Возврат СокрЛП(Результат);
КонецФункции

Кроме того, в типовых конфигурациях подсистема работы с контактной информацией предоставляет общий модуль УправлениеКонтактнойИнформациейКлиентСервер, функции которого умеют преобразовывать произвольную строку телефонного номера в каноническую структуру с полями номера, кода города и комментария.

Рекомендации по пакетной обработке большого объема записей

Когда требуется обработать массив из 10 000 и более записей, необходимо позаботиться об оптимизации производительности:

  1. Выбирайте данные запросом: считывание объектов выполняйте только через Запрос, получая исключительно ссылку и поле контакта, а не динамической выборкой справочника.
  2. Порционная фиксация транзакций: не открывайте одну общую транзакцию на тысячи элементов. Разделяйте запись на пакеты по 500–1000 записей с помощью методов НачатьТранзакцию() и ЗафиксироватьТранзакцию(), чтобы не перегружать журнал СУБД.
  3. Отключение проверок: для ускорения записи выставляйте свойство ОбменДанными.Загрузка = Истина у модифицируемого объекта. Это отключит лишние подписки на события и пользовательские проверки.
  4. Проверка на изменение: записывайте объект только в том случае, если исходный номер действительно изменился, чтобы избежать лишней дисковой нагрузки.
← На главную