Работа с внешними источниками данных, такими как файлы формата DBF, часто сопровождается трудностями при реализации гибкого поиска. Стандартные механизмы платформы 1С, использующие объект XBase, отлично справляются с поиском по полному совпадению или по началу строки с помощью индексов, но поиск вхождения подстроки в середину значения требует иного подхода. В этой статье мы подробно разберем, почему стандартный поиск может выдавать неверные результаты, и изучим несколько эффективных способов решения этой задачи.
Рассмотрим ситуацию, с которой столкнулся автор темы. При попытке найти строку "Срань" в колонке "Name", используя оператор сравнения ">=", система возвращает записи типа "Стакан...". Это происходит потому, что поиск по индексу в формате xBase — это, прежде всего, математическое сравнение последовательности байтов. Буква "т" в слове "Стакан" следует в алфавитном порядке позже буквы "р" в искомом слове "Срань".
Использование оператора ">=" заставляет систему найти первую запись, которая либо в точности совпадает с искомой, либо является следующей по алфавиту (лексикографически большей). Таким образом, мы получаем не поиск вхождения подстроки, а позиционирование на ближайшую подходящую по порядку запись. Для поиска именно подстроки (вхождения части текста в любое место колонки) нам необходимо использовать другие методы.
Если объем DBF-файла относительно невелик (до нескольких десятков тысяч записей), самым простым и надежным способом будет прямой перебор всех записей в цикле. В этом случае нам не нужно тратить ресурсы на создание временных индексных файлов .cdx, что само по себе является медленной операцией ввода-вывода.
Проанализируем пример кода, который реализует такой поиск:
Таблица = Новый XBase;
Таблица.ОткрытьФайл(ПутьКФайлу, , Истина); // Открываем только на чтение
Если Таблица.Открыта() Тогда
Пока Таблица.Следующая() Цикл
// Проверяем наличие подстроки без учета регистра
Если Найти(ВРег(Таблица.Name), ВРег(СтрокаПоиска)) > 0 Тогда
Сообщить("Найдено совпадение в строке: " + Таблица.Name);
// Здесь можно добавить найденную запись в массив или таблицу значений
КонецЕсли;
КонецЦикла;
Таблица.ЗакрытьФайл();
КонецЕсли;
В данном примере мы используем функцию ВРег() для приведения обеих строк к верхнему регистру. Это критически важно, так как поиск в DBF по умолчанию чувствителен к регистру, и "срань" не будет найдена в строке "СРАНЬ ГОСПОДНЯ".
Для более крупных файлов или в случаях, когда требуется высокая скорость поиска без загрузки всех данных в память 1С, оптимальным решением станет использование Microsoft ActiveX Data Objects (ADO). Это позволит нам обращаться к DBF-файлу как к реляционной базе данных и использовать мощный синтаксис SQL, включая оператор LIKE.
Разберем по шагам, как это реализовать:
ADODB.Connection.LIKE '%СтрокаПоиска%'.
Connection = Новый COMОбъект("ADODB.Connection");
// Для Windows 7/10 обычно используется провайдер Microsoft.Jet.OLEDB.4.0
// Путь указывается только к ПАПКЕ, где лежат файлы
ПутьККаталогу = "C:\DBF_Data\";
СтрокаСоединения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ПутьККаталогу + ";Extended Properties=dBASE IV;";
Попытка
Connection.Open(СтрокаСоединения);
// Имя файла в запросе используется как имя таблицы
ТекстЗапроса = "SELECT * FROM [Vozv0297.dbf] WHERE [Name] LIKE '%" + СтрокаПоиска + "%'";
Recordset = Connection.Execute(ТекстЗапроса);
Пока НЕ Recordset.EOF Цикл
Сообщить("Найдено через ADO: " + Recordset.Fields("Name").Value);
Recordset.MoveNext();
КонецЦикла;
Connection.Close();
Исключение
Сообщить("Ошибка подключения: " + ОписаниеОшибки());
КонецПопытки;
Важный момент: Символ % в SQL означает любое количество любых символов. Окружая искомую строку этими знаками, мы гарантируем поиск вхождения в любом месте поля.
Если вам необходимо выполнять несколько различных поисков или фильтраций по одним и тем же данным, бывает эффективнее один раз прочитать весь DBF-файл в объект ТаблицаЗначений. После этого поиск можно выполнять либо методом НайтиСтроки(), либо (что еще профессиональнее) загрузить эту таблицу в запрос через МенеджерВременныхТаблиц.
Рассмотрим вариант с НайтиСтроки():
ТЗ_Данные = Новый ТаблицаЗначений;
ТЗ_Данные.Колонки.Добавить("Name", Новый ОписаниеТипов("Строка"));
// ... код заполнения ТЗ из DBF через объект XBase ...
// Поиск всех строк, где содержится подстрока
// К сожалению, НайтиСтроки ищет только полное совпадение.
// Поэтому для подстроки используем перебор ТЗ или ПостроительЗапроса
Для Каждого Стр Из ТЗ_Данные Цикл
Если Найти(Стр.Name, СтрокаПоиска) > 0 Тогда
// Обработка результата
КонецЕсли;
КонецЦикла;
При работе с файлами DBF в 1С необходимо учитывать несколько неочевидных факторов, которые могут влиять на результат поиска:
XBase свойство Кодировка установлено неверно, вместо кириллицы вы получите "кракозябры", и поиск Найти() никогда не вернет истину.Таблица.ЗаписьУдалена() в цикле перебора или установите Таблица.ОтображатьУдаленные = Ложь.СокрЛП() при получении значения из колонки.Выясним причину, по которой создание индексов "на лету" (как делал автор вопроса) часто не имеет смысла. Создание .cdx индекса оправдано только в двух случаях: если вы ищете по полному совпадению или по началу строки в огромном массиве данных (миллионы записей). Для поиска подстроки "внутри" текста стандартные b-tree индексы DBF бесполезны, так как они строятся от первого символа строки.
Таким образом, для решения задачи автора наиболее подходящим методом будет либо простой перебор XBase с использованием Найти() и ВРег(), либо использование ADO для выполнения SQL-запроса LIKE.