В процессе разработки часто возникает задача привести текст кода в порядок: удалить лишние пробелы, множественные табуляции или специфические символы (например, неразрывные пробелы), которые могут попасть в модуль при копировании из внешних источников. Задача осложняется тем, что необходимо обеспечить высокую скорость обработки, особенно если речь идет о десятках тысяч строк или модулей. Рассмотрим основные методы решения этой задачи, начиная от простых встроенных функций и заканчивая использованием регулярных выражений.
Это самый простой и интуитивно понятный способ, который не требует использования сторонних библиотек. Основная идея заключается в циклической замене двух пробелов на один до тех пор, пока в строке не останется ни одного вхождения двойного пробела. Проанализируем реализацию этого алгоритма:
Данные = "Текст с лишними пробелами";
Данные = СокрЛП(Данные); // Удаляем пробелы по краям
Пока СтрНайти(Данные, " ") <> 0 Цикл
Данные = СтрЗаменить(Данные, " ", " ");
КонецЦикла;
Преимущества метода: простота реализации и отсутствие зависимости от внешних объектов. Недостатки: низкая производительность на больших объемах текста, так как функция СтрЗаменить создает новую копию строки в памяти при каждой итерации. Кроме того, этот метод не обрабатывает табуляции и неразрывные пробелы (символ с кодом 160) за один проход.
С появлением в платформе 8.3 функций СтрРазделить и СтрСоединить, обработка строк стала работать значительно быстрее. Разберем, как применить эти функции для нашей задачи. Мы можем разделить строку по набору символов-разделителей, а затем собрать её обратно, игнорируя пустые элементы.
// Определяем строку разделителей (пробел, табуляция, неразрывный пробел)
Разделители = " " + Символы.Таб + Символы.НПП;
// Разделяем строку на массив, пропуская пустые части
МассивСлов = СтрРазделить(ИсходныйТекст, Разделители, Ложь);
// Соединяем слова обратно через один пробел
Результат = СтрСоединить(МассивСлов, " ");
Рассмотрим подробнее параметры функции СтрРазделить. Третий параметр Ложь указывает платформе, что пустые строки, образовавшиеся в местах скопления нескольких пробелов, не должны включаться в итоговый массив. Это "магическим" образом удаляет все лишние пробелы за одну операцию. Выясним причину эффективности: 1С выполняет разбор и сборку строки на уровне внутреннего движка C++, что гораздо быстрее циклов на встроенном языке.
Если задача требует более тонкой настройки (например, удаление пробелов везде, кроме строк внутри кавычек), на помощь приходят регулярные выражения. В среде Windows мы можем использовать COM-объект VBScript.RegExp. Посмотрим на пример кода:
РегВыражение = Новый COMОбъект("VBScript.RegExp");
РегВыражение.IgnoreCase = Истина;
РегВыражение.Global = Истина;
РегВыражение.MultiLine = Истина;
// Паттерн для поиска последовательностей любых пробельных символов (пробел, таб, перенос)
// Для 1С важно добавить символ \xA0 (неразрывный пробел)
РегВыражение.Pattern = "[\s\xA0]{2,}";
Текст = "МояПеременная = 15; // Комментарий";
Результат = РегВыражение.Replace(Текст, " ");
Проанализируем ситуацию с паттерном. Метасимвол \s обозначает любой пробельный символ, но в реализации VBScript он не всегда корректно определяет неразрывный пробел (код 160). Поэтому мы используем квадратные скобки [\s\xA0] для явного указания обоих типов. Квантификатор {2,} говорит о том, что нужно искать вхождения длиной от двух символов и более. Это позволяет заменить "кучу" пробелов на один единственный.
Однако автор темы столкнулся с проблемой использования сложных условий, таких как Lookbehind (просмотр назад, (?<=...)). Важно знать: VBScript.RegExp является устаревшим движком и не поддерживает многие современные функции регулярных выражений. Если вам нужна максимальная мощь, рекомендуется использовать System.Text.RegularExpressions.Regex через NetObjectToCOM или внешние компоненты.
Иногда требуется реализовать сложную логику, которую трудно описать регулярным выражением. В этом случае мы можем реализовать лексический анализатор (лексер) "на минималках". Рассмотрим пример функции, которая проходит по строке и собирает результат, пропуская лишние пробелы:
Функция ОчиститьЛишниеПробелы(Знач Текст)
Результат = "";
ПредыдущийБылПробелом = Ложь;
Для Поз = 1 По СтрДлина(Текст) Цикл
КодСимвола = КодСимвола(Текст, Поз);
ТекущийПробел = (КодСимвола = 32 ИЛИ КодСимвола = 9 ИЛИ КодСимвола = 160);
Если ТекущийПробел Тогда
Если НЕ ПредыдущийБылПробелом Тогда
Результат = Результат + " ";
ПредыдущийБылПробелом = Истина;
КонецЕсли;
Иначе
Результат = Результат + Сред(Текст, Поз, 1);
ПредыдущийБылПробелом = Ложь;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Разберем по шагам работу этого кода. Мы заводим флаг ПредыдущийБылПробелом. Когда алгоритм встречает первый пробел в серии, он добавляет его в результат и взводит флаг. Все последующие пробелы в этой серии будут проигнорированы, пока не встретится значащий символ. Это гарантирует, что любая последовательность пробелов/табов сожмется в один пробел.
Если задача стоит разово или в рамках подготовки кода к "продакшену", рассмотрим вариант использования профессиональных текстовых редакторов. Пошагово это выглядит так:
[ \t]+ на (один пробел).Важный момент: Современные редакторы позволяют корректно обрабатывать табуляцию в начале строк, сохраняя структуру кода, но удаляя "мусор" внутри строк.
Выясним, какой метод лучше выбрать в зависимости от условий:
При обработке 25 000 модулей (как упоминалось в теме), наиболее надежным будет выгрузка конфигурации в файлы и использование скриптов на Python или C#, так как встроенный язык 1С при работе с огромными текстовыми массивами может значительно нагружать оперативную память из-за особенностей работы со строками типа Immutable.