В процессе разработки часто возникает задача автоматического форматирования текста. Типичный пример — разделение слов, которые «слиплись» из-за разного регистра, например, когда за строчной (маленькой) буквой сразу следует прописная (заглавная). Проанализируем различные подходы к решению этой задачи, начиная от простых алгоритмов перебора и заканчивая мощными инструментами регулярных выражений, появившимися в современных версиях платформы 1С:Предприятие 8.3.23.
Разберем самый понятный и универсальный способ, который будет работать на любой версии платформы. Мы будем посимвольно обходить строку и проверять регистр текущего и следующего символа. Чтобы не зависеть от жестко прописанного алфавита, воспользуемся функциями НРег и ВРег.
Рассмотрим пример реализации функции, которая анализирует строку и собирает результат в новую переменную:
Функция ВставитьПробелМеждуРегистрами(Знач ВхСтрока)
Результат = "";
ДлинаСтроки = СтрДлина(ВхСтрока);
Для НомерСимвола = 1 По ДлинаСтроки - 1 Цикл
ТекСимвол = Сред(ВхСтрока, НомерСимвола, 1);
СледСимвол = Сред(ВхСтрока, НомерСимвола + 1, 1);
// Проверяем: текущий — строчной, следующий — прописной
ЭтоСтрочная = (ТекСимвол = НРег(ТекСимвол)) И (ТекСимвол <> ВРег(ТекСимвол));
ЭтоПрописная = (СледСимвол = ВРег(СледСимвол)) И (СледСимвол <> НРег(СледСимвол));
Результат = Результат + ТекСимвол;
Если ЭтоСтрочная И ЭтоПрописная Тогда
Результат = Результат + " "; // Вставляем пробел
КонецЕсли;
КонецЦикла;
// Не забываем добавить последний символ
Если ДлинаСтроки > 0 Тогда
Результат = Результат + Сред(ВхСтрока, ДлинаСтроки, 1);
КонецЕсли;
Возврат Результат;
КонецФункции
Важный нюанс: условие ТекСимвол <> ВРег(ТекСимвол) необходимо для того, чтобы исключить цифры и знаки препинания, которые при смене регистра не изменяются.
Проанализируем более хитрый метод. Если мы хотим избежать сложной логики внутри цикла, можно предварительно подготовить строку. Сначала заменим все возможные маленькие буквы на один символ (например, «а»), а все большие — на другой (например, «А»). Тогда в модифицированной строке нам останется найти только сочетание «аА».
Посмотрим, как это выглядит в коде:
Функция ВставитьПробелЧерезМаску(Знач Стр)
Маленькие = "аеёиоуыэюябвгджзйклмнпрстфхцчшщъь";
Большие = ВРег(Маленькие);
// Создаем "карту" строки, где все буквы приведены к двум видам
МодифицированнаяСтрока = Стр;
Для Индекс = 1 По СтрДлина(Маленькие) Цикл
МодифицированнаяСтрока = СтрЗаменить(МодифицированнаяСтрока, Сред(Маленькие, Индекс, 1), "а");
МодифицированнаяСтрока = СтрЗаменить(МодифицированнаяСтрока, Сред(Большие, Индекс, 1), "А");
КонецЦикла;
// Теперь ищем вхождение "аА" в маске и вставляем пробел в оригинал
Позиция = СтрНайти(МодифицированнаяСтрока, "аА");
Смещение = 0;
Пока Позиция > 0 Цикл
Стр = Сред(Стр, 1, Позиция + Смещение) + " " + Сред(Стр, Позиция + Смещение + 1);
Смещение = Смещение + 1; // Учитываем добавленный пробел
Позиция = СтрНайти(МодифицированнаяСтрока, "аА", , Позиция + 1);
КонецЦикла;
Возврат Стр;
КонецФункции
Если ваша платформа не самая свежая, но задача требует высокой скорости и краткости кода, можно использовать объект VBScript.RegExp. Выясним, как составить паттерн для поиска строчных и прописных букв.
Паттерн ([а-яё])([А-ЯЁ]) описывает две группы захвата: первая ищет маленькую букву, вторая — большую. Обратите внимание на букву «ё», она часто не входит в стандартный диапазон «а-я».
Функция ОбработатьРегуляркойCOM(ВхСтрока)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.Global = Истина;
RegExp.Pattern = "([а-яё])([А-ЯЁ])";
// В VBScript можно использовать $1 $2 для подстановки найденных групп
Возврат RegExp.Replace(ВхСтрока, "$1 $2");
КонецФункции
Это решение изящное, но имеет ограничение: оно работает только в операционной системе Windows, так как использует COM-технологию.
С выходом версии 8.3.23 в 1С появилась нативная поддержка регулярных выражений по стандарту ICU. Это наиболее производительный и современный метод. Рассмотрим, какие новые функции нам помогут:
СтрНайтиПоРегулярномуВыражению() — поиск первого вхождения.СтрЗаменитьПоРегулярномуВыражению() — замена по шаблону.\p{Ll} — специальный литерал Unicode для любой строчной буквы.\p{Lu} — специальный литерал Unicode для любой прописной буквы.Проанализируем ситуацию: в текущей реализации 1С 8.3.23 в методе замены пока отсутствует поддержка групп захвата (обращение через $1, $2). Поэтому для вставки пробела нам придется получить все вхождения и обработать их в цикле.
Функция ВставитьПробелы8323(Знач ВхСтрока)
// Паттерн: строчная буква, за которой следует прописная
Паттерн = "\p{Ll}\p{Lu}";
// Получаем все найденные совпадения
Результаты = СтрНайтиВсеПоРегулярномуВыражению(ВхСтрока, Паттерн);
// Обрабатываем результаты с конца, чтобы не поплыли индексы
Для Индекс = Результаты.Количество() - 1 По 0 Цикл
ТекРезультат = Результаты[Индекс];
// Вставляем пробел в середину найденной пары (длина пары всегда 2 символа)
ВхСтрока = Сред(ВхСтрока, 1, ТекРезультат.НачальнаяПозиция)
+ " "
+ Сред(ВхСтрока, ТекРезультат.НачальнаяПозиция + 1);
КонецЦикла;
Возврат ВхСтрока;
КонецФункции
Разберем важный технический момент, связанный с «шестым битом», о котором часто говорят опытные программисты. В кодировке Unicode разница между кодом заглавной и строчной буквы кириллицы часто составляет 32 (это и есть 5-й или 6-й бит в зависимости от системы отсчета). Например, «А» (1040) и «а» (1072).
Внимание: полагаться на битовые операции опасно из-за буквы «Ё». У «Ё» код 1025, а у «ё» — 1105. Разница между ними — 80. Поэтому всегда используйте либо встроенные функции 1С (НРег/ВРег), либо Unicode-категории в регулярных выражениях (\p{L}).
Также помните, что новые функции платформы 8.3.23 по работе с регулярными выражениями доступны в основном на стороне сервера. Если вам нужна обработка на клиенте в тонком клиенте, убедитесь, что версия платформы поддерживает это, или используйте серверный вызов.
Подводя итог, для новых проектов на современных версиях 1С оптимальным будет использование \p{Ll}\p{Lu}, а для поддержки старых конфигураций — надежный метод со сравнением НРег() и ВРег().