При разработке алгоритмов обработки данных в платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией: визуально значение гарантированно присутствует в таблице значений (ТЗ), переменная поиска содержит то же самое значение, однако метод НайтиЗначение упорно возвращает 0 (значение не найдено). Разберем причины такого поведения платформы, научимся диагностировать скрытые расхождения и рассмотрим надежные способы решения проблемы.
Рассмотрим типовой сценарий, с которым столкнулся автор темы. В коде создается и типизируется колонка таблицы значений, затем таблица заполняется данными из запроса или справочника, после чего вызывается поиск:
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Код", "Строка", 9);
ТЗ.НоваяКолонка("НомКод", "Строка", 15);
ТЗ.НоваяКолонка("Объект", "Справочник.Номенклатура");
// Заполнение таблицы значениями...
ТЗ.Сортировать("НомКод");
НовыйНомер = "00001";
Если ТЗ.НайтиЗначение(НовыйНомер, , "НомКод") = 1 Тогда
// Обработка найденной строки
Иначе
// Метод возвращает 0, хотя "00001" в таблице есть!
КонецЕсли;
При визуальном просмотре содержимого таблицы значений через отладчик или интерактивную форму кажется, что строка "00001" присутствует. Однако метод НайтиЗначение выполняет строгое точное сравнение. Главная причина сбоя заключается в несовпадении фактической длины строк из-за скрытых концевых пробелов.
Проанализируем данные на наличие невидимых символов. Человеческий глаз в интерфейсе не отличает строку "00001" от строки "00001 " (дополненной пробелами до длины колонки). Чтобы выявить расхождение, выведем значения в окно сообщений, заключив их в маркеры-разделители (например, одинарные кавычки или восклицательные знаки):
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Сообщить("В таблице: '" + ТЗ.НомКод + "'");
КонецЦикла;
Сообщить("Ищем: '" + НовыйНомер + "'");
В результате такого вывода сразу обнаруживается несоответствие:
В таблице: '00001 '
Ищем: '00001'
Поскольку строка в таблице имеет длину 15 символов (дополнена пробелами справа из-за типизации колонки или формата поля в базе данных), прямое строковое сравнение выдает отрицательный результат.
Разберем по шагам, как устранить эту ошибку и сделать поиск стабильным.
Самый надежный способ — использовать функцию СокрЛП при помещении строковых данных в таблицу значений. Это гарантирует удаление пробелов слева и справа:
Пока Запрос.Группировка() = 1 Цикл
ТЗ.НоваяСтрока();
ТЗ.Код = Запрос.Код;
ТЗ.НомКод = СокрЛП(Запрос.НомКод);
ТЗ.Объект = Запрос.Объект;
КонецЦикла;
При добавлении колонки методом НоваяКолонка не рекомендуется задавать фиксированную длину для строк, если по ним планируется свободный поиск произвольных значений. Достаточно указать тип без размера или вовсе не типизировать колонку жестко:
// Вместо ТЗ.НоваяКолонка("НомКод", "Строка", 15);
ТЗ.НоваяКолонка("НомКод", "Строка");
В этом случае платформа не будет принудительно дополнять ячейки пробелами до заданной длины.
Если в поисковой переменной также могут случайно оказаться пробелы, перед передачей аргумента в метод обязательно вызываем функцию СокрЛП:
НомерДляПоиска = СокрЛП(НовыйНомер);
Если ТЗ.НайтиЗначение(НомерДляПоиска, , "НомКод") = 1 Тогда
// Значение успешно найдено
КонецЕсли;
Выясним другие тонкости платформы 1С 7.7, которые могут помешать корректной работе поиска:
Синтаксис метода: НайтиЗначение(Значение, Строка, Колонка). Параметр Строка является одновременно входным и выходным! Если передать в метод переменную, которая уже содержит число больше 0, поиск будет производиться только внутри этой указанной строки, а не по всей таблице. При поиске в цикле переменную строки необходимо обязательно сбрасывать перед каждой итерацией:
НомСтр = 0;
Если ТЗ.НайтиЗначение(НовыйНомер, НомСтр, "НомКод") = 1 Тогда
// Найдено в строке НомСтр
КонецЕсли;
Метод НайтиЗначение выполняет последовательный перебор строк сверху вниз. Предварительный вызов метода ТЗ.Сортировать(...) не ускоряет работу самого метода НайтиЗначение, так как бинарный поиск платформой внутри этого метода не используется.
Поиск строковых значений в 1С 7.7 чувствителен к регистру: строки "абв" и "АБВ" не будут признаны равными. Для регистронезависимого поиска строки в таблице и искомое значение необходимо приводить к единому регистру через функцию ВРег().
Функция СокрЛП удаляет только стандартные пробелы (код 32). Если данные были загружены из Excel или внешних файлов, внутри могут оказаться неразрывные пробелы (код 160) или табуляции (код 9). Их необходимо предварительно заменять:
ЧистаяСтрока = СтрЗаменить(ГрязнаяСтрока, Симв(160), "");
ЧистаяСтрока = СокрЛП(ЧистаяСтрока);
Если таблица содержит десятки тысяч строк, а поиск выполняется многократно внутри цикла, линейный перебор методом НайтиЗначение может привести к зависанию обработки. Рассмотрим альтернативные варианты ускорения:
Отсортируем таблицу методом ТЗ.Сортировать("НомКод") и реализуем классический алгоритм деления отрезка пополам. Быстрое позиционирование методом ТЗ.ПолучитьСтрокуПоНомеру(Индекс) позволяет сократить сложность поиска с линейной до логарифмической.
Параллельно с таблицей значений можно заполнить объект СписокЗначений, где ключом будет искомая строка, а значением — номер строки в ТЗ. Поиск по представлению в предварительно отсортированном списке выполняется платформой существенно быстрее.
В высоконагруженных решениях для 1С 7.7 часто используют компонент 1С++ и класс ИндексированнаяТаблица, который поддерживает автоматическое построение индексов по колонкам и обеспечивает мгновенный поиск даже на миллионных массивах строк.