При разработке механизмов импорта данных или обработки внешних каталогов программисты часто сталкиваются со странным поведением метода НайтиФайлы. Например, при попытке найти только файлы старого формата Excel с маской "*.xls", система неожиданно возвращает в массиве и современные файлы .xlsx. Рассмотрим подробнее, почему это происходит, разберем системные причины и выработаем надежные программные решения этой проблемы.
Первое, что нужно понять: это не баг платформы 1С. Такое поведение заложено в самой операционной системе Windows и файловой системе NTFS. Проанализируем ситуацию: для обеспечения обратной совместимости с очень старыми программами (времен MS-DOS), Windows автоматически создает для каждого файла с длинным именем так называемое короткое имя (Short File Name или SFN).
Короткое имя всегда соответствует формату 8.3: не более 8 символов на само имя и ровно 3 символа на расширение. Если у вас есть файл с именем Отчет_за_январь_2023.xlsx, операционная система может присвоить ему скрытый алиас вида ОТЧЕТ~1.XLS.
Когда метод 1С НайтиФайлы обращается к системной функции FindFirstFile (Win32 API), поиск происходит одновременно и по длинным, и по коротким именам. Поскольку расширение короткого имени (.XLS) совпадает с вашей маской поиска, файл .xlsx попадает в итоговую выборку. Это объясняет, почему поиск по .doc часто находит .docx, а поиск по .htm — файлы .html.
В обсуждениях на форумах часто приводится пример вывода команды dir /x в консоли Windows. Эта команда позволяет увидеть те самые короткие имена. Рассмотрим ситуацию:
Результат команды dir:
03.04.2022 22:00 10 312 EE95~1.XLS Плюсы минусы переезда.xlsx
Здесь мы видим, что файл Плюсы минусы переезда.xlsx имеет короткое имя EE95~1.XLS. Для функции поиска этот файл является одновременно и .xlsx, и .xls. Именно поэтому стандартная маска в 1С отрабатывает «шире», чем ожидает разработчик.
Поскольку мы не можем изменить логику работы API Windows, самым надежным и универсальным способом будет ручная проверка расширения каждого найденного файла. Разберем этот процесс по шагам.
После получения массива объектов типа Файл, нам необходимо обойти его в цикле и оставить только те элементы, расширение которых строго соответствует нашему запросу. Обратите внимание, что свойство Расширение объекта Файл возвращается вместе с точкой и в том регистре, в котором оно записано в системе.
Посмотрим на пример кода:
// Определяем путь и маску
ПутьККаталогу = "C:\Exchange\";
МаскаПоиска = "*.xls";
// Получаем предварительный список (может содержать .xlsx)
НайденныеФайлы = НайтиФайлы(ПутьККаталогу, МаскаПоиска, Ложь);
// Создаем массив для точного результата
ЧистыйСписокФайлов = Новый Массив;
Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
// Важно: приводим к нижнему регистру для надежности сравнения
Если НРег(ТекущийФайл.Расширение) = ".xls" Тогда
ЧистыйСписокФайлов.Добавить(ТекущийФайл);
КонецЕсли;
КонецЦикла;
// Теперь в массиве ЧистыйСписокФайлов только честные .xls файлы
Если файлов в каталоге тысячи, и вы заботитесь о производительности, можно воспользоваться объектом VBScript.RegExp. Это позволит отсечь лишние элементы массива строк быстрее, чем при обходе тяжелых объектов Файл.
Рассмотрим ситуацию, когда нам нужно отфильтровать список путей к файлам:
// Предположим, у нас есть массив полных имен файлов СписокПутей
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
// Маска для поиска строго расширения .xls в конце строки
RegExp.Pattern = "\.xls$";
Индекс = СписокПутей.Количество() - 1;
Пока Индекс >= 0 Цикл
Если НЕ RegExp.Test(СписокПутей[Индекс]) Тогда
СписокПутей.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
В некоторых случаях системные администраторы могут вмешаться в работу файловой системы, чтобы предотвратить создание имен 8.3. Это радикальный метод, который может ускорить работу файловой системы на серверах с огромным количеством мелких файлов, но он требует осторожности.
Для управления этим механизмом используется утилита fsutil. Проанализируем основные команды:
fsutil 8dot3name query C: — позволяет узнать, включена ли генерация коротких имен на диске.fsutil 8dot3name set C: 1 — отключает создание новых коротких имен.Важно: Отключение генерации имен 8.3 не удалит уже существующие короткие имена. Чтобы они исчезли для старых файлов, файлы нужно физически пересоздать (например, скопировать в новую папку и удалить старую). Кроме того, помните о рисках: некоторые старые установщики программ и драйверов до сих пор полагаются на пути типа C:\PROGRA~1\. Если их лишить этой возможности, ПО может перестать работать.
Интересно заметить, что обычный Проводник Windows (Windows Shell) при поиске ведет себя иначе. Он использует индексы и современные алгоритмы, которые игнорируют короткие имена. Именно поэтому пользователь, открыв папку и введя в поиске *.xls, видит «правильный» результат, а программист 1С, использующий НайтиФайлы, получает «грязную» выборку.
Если вы используете PowerShell, то команда Get-ChildItem -Filter *.xls выдаст тот же результат, что и 1С (с лишними файлами). Однако использование параметра -Include заставляет PowerShell выполнять фильтрацию программно, что дает чистый результат.
Подводя итог, можно сказать, что ситуация с «лишними» файлами — это классическая особенность работы с файловой системой в среде Windows. Мы рекомендуем придерживаться следующих правил:
НайтиФайлы как на единственный инструмент фильтрации, если вам важна точность расширения.Файл.Расширение в цикле.НРег(), так как расширения могут быть записаны как .XLS, так и .xls или даже .Xls..xls не скрывается другой тип данных.Используя эти методы, вы сделаете свои обработки по загрузке данных более надежными и защищенными от системных особенностей Windows.