Почему НайтиФайлы возвращает лишние расширения и как правильно фильтровать результаты

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке механизмов импорта данных или обработки внешних каталогов программисты часто сталкиваются со странным поведением метода НайтиФайлы. Например, при попытке найти только файлы старого формата Excel с маской "*.xls", система неожиданно возвращает в массиве и современные файлы .xlsx. Рассмотрим подробнее, почему это происходит, разберем системные причины и выработаем надежные программные решения этой проблемы.

Выясним причину: механизм коротких имен 8.3 в NTFS

Первое, что нужно понять: это не баг платформы 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С отрабатывает «шире», чем ожидает разработчик.

Решение №1: Программная пост-фильтрация (Рекомендуемый способ)

Поскольку мы не можем изменить логику работы API Windows, самым надежным и универсальным способом будет ручная проверка расширения каждого найденного файла. Разберем этот процесс по шагам.

После получения массива объектов типа Файл, нам необходимо обойти его в цикле и оставить только те элементы, расширение которых строго соответствует нашему запросу. Обратите внимание, что свойство Расширение объекта Файл возвращается вместе с точкой и в том регистре, в котором оно записано в системе.

Посмотрим на пример кода:


// Определяем путь и маску
ПутьККаталогу = "C:\Exchange\";
МаскаПоиска = "*.xls";

// Получаем предварительный список (может содержать .xlsx)
НайденныеФайлы = НайтиФайлы(ПутьККаталогу, МаскаПоиска, Ложь);

// Создаем массив для точного результата
ЧистыйСписокФайлов = Новый Массив;

Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
    // Важно: приводим к нижнему регистру для надежности сравнения
    Если НРег(ТекущийФайл.Расширение) = ".xls" Тогда
        ЧистыйСписокФайлов.Добавить(ТекущийФайл);
    КонецЕсли;
КонецЦикла;

// Теперь в массиве ЧистыйСписокФайлов только честные .xls файлы

Решение №2: Использование регулярных выражений для фильтрации

Если файлов в каталоге тысячи, и вы заботитесь о производительности, можно воспользоваться объектом VBScript.RegExp. Это позволит отсечь лишние элементы массива строк быстрее, чем при обходе тяжелых объектов Файл.

Рассмотрим ситуацию, когда нам нужно отфильтровать список путей к файлам:


// Предположим, у нас есть массив полных имен файлов СписокПутей
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Истина;
// Маска для поиска строго расширения .xls в конце строки
RegExp.Pattern = "\.xls$";

Индекс = СписокПутей.Количество() - 1;
Пока Индекс >= 0 Цикл
    Если НЕ RegExp.Test(СписокПутей[Индекс]) Тогда
        СписокПутей.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

Решение №3: Системный подход (настройка ОС)

В некоторых случаях системные администраторы могут вмешаться в работу файловой системы, чтобы предотвратить создание имен 8.3. Это радикальный метод, который может ускорить работу файловой системы на серверах с огромным количеством мелких файлов, но он требует осторожности.

Для управления этим механизмом используется утилита fsutil. Проанализируем основные команды:

  1. Запрос статуса: fsutil 8dot3name query C: — позволяет узнать, включена ли генерация коротких имен на диске.
  2. Отключение генерации: fsutil 8dot3name set C: 1 — отключает создание новых коротких имен.

Важно: Отключение генерации имен 8.3 не удалит уже существующие короткие имена. Чтобы они исчезли для старых файлов, файлы нужно физически пересоздать (например, скопировать в новую папку и удалить старую). Кроме того, помните о рисках: некоторые старые установщики программ и драйверов до сих пор полагаются на пути типа C:\PROGRA~1\. Если их лишить этой возможности, ПО может перестать работать.

Особенности поиска в разных оболочках

Интересно заметить, что обычный Проводник Windows (Windows Shell) при поиске ведет себя иначе. Он использует индексы и современные алгоритмы, которые игнорируют короткие имена. Именно поэтому пользователь, открыв папку и введя в поиске *.xls, видит «правильный» результат, а программист 1С, использующий НайтиФайлы, получает «грязную» выборку.

Если вы используете PowerShell, то команда Get-ChildItem -Filter *.xls выдаст тот же результат, что и 1С (с лишними файлами). Однако использование параметра -Include заставляет PowerShell выполнять фильтрацию программно, что дает чистый результат.

Выводы и рекомендации

Подводя итог, можно сказать, что ситуация с «лишними» файлами — это классическая особенность работы с файловой системой в среде Windows. Мы рекомендуем придерживаться следующих правил:

Используя эти методы, вы сделаете свои обработки по загрузке данных более надежными и защищенными от системных особенностей Windows.

← На главную