Как вытащить папку из полного пути к файлу в 1С

· 9 мин

Содержание

Чтобы получить каталог файла в 1С, создайте объект Файл по полному имени и прочитайте его свойство Путь. Оно вернёт папку, в которой лежит файл, причём с завершающим разделителем: C:\Обмен\Выгрузка\.

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

Ниже: свойства объекта Файл, ручной разбор строки, работа с разделителями, различия клиента и сервера и проверки, которые спасают от падений.

Из чего состоит путь к файлу в 1С

Полный путь — это каталог, имя и расширение. Для C:\Обмен\Выгрузка\Реестр_март.xlsx разбор такой:

ЧастьЗначение
КаталогC:\Обмен\Выгрузка\
Имя с расширениемРеестр_март.xlsx
Имя без расширенияРеестр_март
Расширение.xlsx

Три вещи, о которых забывают:

  • 📁 каталог в 1С всегда заканчивается разделителем, если его вернула платформа;
  • 🔤 расширение приходит с точкой;
  • 🖥 путь привязан к машине: клиентской или серверной. Это отдельная тема, она ниже.

Строка пути в 1С — обычная строка. Платформа не проверяет, что файл существует, пока вы не спросите об этом отдельно.

Свойство «Путь» объекта «Файл»: основной способ

Это самый короткий и надёжный вариант. Объект создаётся одной строкой, обращение к диску при этом не происходит.

ПолноеИмя = "C:\Обмен\Выгрузка\Реестр_март.xlsx";
ОбъектФайл = Новый Файл(ПолноеИмя);
Каталог = ОбъектФайл.Путь;
// Каталог = "C:\Обмен\Выгрузка\"

Если нужен путь без завершающего слеша, например чтобы сравнить две папки, уберите его вручную:

Каталог = ОбъектФайл.Путь;
Если Прав(Каталог, 1) = "\" Или Прав(Каталог, 1) = "/" Тогда
    Каталог = Лев(Каталог, СтрДлина(Каталог) - 1);
КонецЕсли;

Новый объект Файл не требует, чтобы файла не было или он был. Можно разобрать путь к файлу, который только собираетесь создать.

Не склеивайте каталог и имя через + без проверки последнего символа. Получите C:\ОбменРеестр.xlsx или двойной слеш.

Полное имя, имя и расширение: что даёт каждое свойство

У объекта Файл есть пять свойств для разбора пути. Про одно из них часто путаются.

СвойствоЧто вернётПример
ПолноеИмяВесь путь целикомC:\Обмен\Выгрузка\Реестр_март.xlsx
ПутьКаталог с разделителем на концеC:\Обмен\Выгрузка\
ИмяИмя с расширениемРеестр_март.xlsx
ИмяБезРасширенияИмя без точки и расширенияРеестр_март
РасширениеРасширение с точкой.xlsx

Отдельного свойства ИмяФайла у объекта Файл нет. Так обычно называют переменную или реквизит, куда кладут полное имя. Если встретили в чужом коде ИмяФайла.Путь, значит, там уже лежит объект Файл, а не строка.

Полезная комбинация — собрать путь к соседнему файлу:

ОбъектФайл = Новый Файл(ПолноеИмя);
ПутьПротокола = ОбъектФайл.Путь + ОбъектФайл.ИмяБезРасширения + "_протокол.txt";
// C:\Обмен\Выгрузка\Реестр_март_протокол.txt

Протокол окажется рядом с исходным файлом, и пользователю не придётся его искать.

Если Файл не подходит: разбор строки вручную

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

Найти последний разделитель в строке:

Функция КаталогИзСтроки(Знач ПолноеИмя)
    Позиция = 0;
    Для Индекс = 1 По СтрДлина(ПолноеИмя) Цикл
        Символ = Сред(ПолноеИмя, Индекс, 1);
        Если Символ = "\" Или Символ = "/" Тогда
            Позиция = Индекс;
        КонецЕсли;
    КонецЦикла;
    Возврат Лев(ПолноеИмя, Позиция);
КонецФункции

Функция возвращает каталог вместе с последним разделителем, то есть ровно то, что отдаёт Путь. Если разделителей в строке нет, вернётся пустая строка.

В актуальных релизах платформы есть строковые функции, которые позволяют сделать то же короче, например поиск с конца строки. Но этот вариант работает везде и понятен любому, кто откроет код через пять лет.

Если пути идут из Linux-сервера или из внешнего сервиса, в них бывает /. Проверяйте оба символа, как в примере.

Разделители пути: слеш, обратный слеш и разные ОС

В Windows каталоги разделяет \, в Linux — /. Платформа понимает оба в большинстве мест, но собственные строки лучше собирать через специальные функции:

  • ПолучитьРазделительПути() — разделитель текущей платформы;
  • ПолучитьРазделительПутиКлиента() — разделитель на компьютере пользователя;
  • ПолучитьРазделительПутиСервера() — разделитель на сервере 1С.

Зачем это нужно: пользователь работает под Windows, а сервер приложений стоит на Linux. Путь, склеенный с \, на сервере не заработает.

Разделитель = ПолучитьРазделительПутиСервера();
ПутьКФайлу = КаталогВыгрузки + Разделитель + ИмяФайла;

Если каталог уже заканчивается разделителем, лишний добавлять не надо. Проще всего написать маленькую функцию и пользоваться только ей:

Функция ДобавитьРазделитель(Знач Каталог, Знач Разделитель)
    Если Не ПустаяСтрока(Каталог) И Прав(Каталог, 1) <> Разделитель Тогда
        Каталог = Каталог + Разделитель;
    КонецЕсли;
    Возврат Каталог;
КонецФункции

В новых релизах платформы есть готовая функция объединения путей, она сама следит за разделителями. Если ваша версия её поддерживает, берите её: меньше своего кода — меньше ошибок.

Полезные функции для работы с каталогами

Обычно каталог нужен не сам по себе, а чтобы что-то с ним сделать. Вот что чаще всего пригождается.

Готовые системные каталоги:

  • КаталогВременныхФайлов() — временная папка, подходит для промежуточных файлов;
  • КаталогДокументов() — «Документы» пользователя;
  • КаталогПрограммы() — папка, где установлена платформа.

Поиск файлов в каталоге:

Файлы = НайтиФайлы(Каталог, "*.xml", Ложь);
Для Каждого НайденныйФайл Из Файлы Цикл
    Сообщить(НайденныйФайл.ПолноеИмя);
КонецЦикла;

Третий параметр — искать ли в подкаталогах. Для больших деревьев папок оставляйте Ложь, иначе обход займёт заметное время.

Выбор каталога пользователем:

Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Диалог.Заголовок = "Выберите папку для выгрузки";
Диалог.Показать(Новый ОписаниеОповещения("ПослеВыбораКаталога", ЭтотОбъект));

После выбора диалог отдаёт массив выбранных путей, у каталога в нём нет завершающего разделителя. Тут и пригодится функция ДобавитьРазделитель из предыдущего раздела.

Создание каталога:

Если Не Новый Файл(Каталог).Существует() Тогда
    СоздатьКаталог(Каталог);
КонецЕсли;

СоздатьКаталог создаёт всю цепочку вложенных папок за один вызов, проверять каждый уровень отдельно не нужно.

Практический пример: сложить файлы по папкам-источникам

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

Количество = Новый Соответствие;
Для Каждого Строка Из СписокПутей Цикл
    Каталог = Новый Файл(Строка.ПолноеИмя).Путь;
    Текущее = Количество.Получить(Каталог);
    Количество.Вставить(Каталог, ?(Текущее = Неопределено, 1, Текущее + 1));
КонецЦикла;
Для Каждого Элемент Из Количество Цикл
    Сообщить(Элемент.Ключ + " — файлов: " + Элемент.Значение);
КонецЦикла;

Каталог служит ключом соответствия. Так как Путь у всех файлов из одной папки одинаков до символа, ключи совпадают без дополнительной нормализации.

Нюанс: Windows не различает регистр в путях, а строки в 1С сравниваются с учётом регистра. C:\Обмен\ и c:\обмен\ станут двумя разными ключами. Если пути приходят из разных источников, приведите их к нижнему регистру функцией НРег перед сравнением.

Клиент и сервер: где именно лежит ваш файл

Это самая частая причина фразы «у меня работает, а у пользователя нет». Объект Файл всегда смотрит на файловую систему той стороны, где выполняется код.

Где выполняется кодКакой диск видит Файл
Клиент (форма, &НаКлиенте)Компьютер пользователя
Сервер (&НаСервере, модуль объекта)Диск сервера 1С

Сам по себе разбор строки (Путь, Имя, Расширение) от диска не зависит, он чисто строковый. Различие проявляется, когда вы спрашиваете о существовании, ищете файлы или читаете содержимое.

Разберём пример. Пользователь выбрал на своём компьютере D:\Отчёты\акт.pdf. Вы передали эту строку на сервер и вызвали Новый Файл(...).Существует(). На сервере диска D: с такими папками нет, ответ будет Ложь.

Что делать:

  1. Разбирайте путь там, где он получен, или на сервере: строковые свойства работают одинаково.
  2. Проверку существования и чтение выполняйте на клиенте.
  3. Нужно содержимое на сервере — передайте его как ДвоичныеДанные или через временное хранилище, а не строкой пути.

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

В управляемых формах клиентские файловые операции в современных релизах платформы выполняются асинхронными методами с оповещением, а синхронные устарели и в некоторых режимах недоступны. Если форма должна работать и в тонком клиенте, и в браузере, пишите асинхронно сразу.

Проверка существования и обработка ошибок

Новый Файл не падает, даже если пути не существует. Ошибки возникают на следующем шаге: при чтении, копировании, поиске. Поэтому проверяйте до действия, а не после.

Минимальный безопасный набор:

ОбъектФайл = Новый Файл(ПолноеИмя);
Если Не ОбъектФайл.Существует() Тогда
    ВызватьИсключение "Файл не найден: " + ПолноеИмя;
КонецЕсли;
Если Не ОбъектФайл.ЭтоФайл() Тогда
    ВызватьИсключение "Это каталог, а не файл: " + ПолноеИмя;
КонецЕсли;

Метод ЭтоФайл() отличает файл от каталога, ЭтоКаталог() — наоборот. Это важно, когда путь пришёл от пользователя и он мог ткнуть в папку.

Что ещё ломает код на ровном месте:

  • ❌ пустая строка пути: Новый Файл("") создаст объект, Путь вернёт пустоту, а следом упадёт склейка;
  • ❌ недопустимые символы в имени (:, *, ?, ", <, >, |) — на Windows файл не создастся;
  • ❌ нет прав на папку: чтение пройдёт, а запись выдаст ошибку доступа;
  • ❌ слишком длинный путь: Windows по умолчанию ограничивает его примерно 260 символами.

Если операция с диском может не удаться по причинам вне вашего контроля, оберните её в Попытка … Исключение и покажите пользователю понятный текст, а не техническое сообщение платформы:

Попытка
    ДвоичныеДанные = Новый ДвоичныеДанные(ПолноеИмя);
Исключение
    Сообщить("Не удалось прочитать файл " + ПолноеИмя + ": " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
    Возврат;
КонецПопытки;

Отдельный случай — запись в каталог. Перед сохранением результата убедитесь, что папка существует и в неё можно писать. Простой способ: создайте в ней пустой временный файл, удалите его и только потом запускайте долгую выгрузку. Иначе пользователь час ждёт формирования отчёта, а в конце получает отказ в доступе.

Перед работой с путём в своём коде
Выполнено: 0 / 5

Частые вопросы

Почему `Путь` возвращает слеш на конце, а в диалоге выбора каталога его нет?
Это разные механизмы. Объект Файл отдаёт путь с разделителем, диалог возвращает строку как выбрал пользователь, обычно без него. Приводите оба случая к одному виду функцией добавления разделителя.
Как получить имя родительской папки, а не весь путь?
Возьмите Путь, уберите завершающий разделитель и создайте ещё один объект Файл из этой строки. У него в свойстве Имя окажется название нужной папки, а в Путь — каталог уровнем выше.
Работает ли `Новый Файл` с сетевыми путями?
Да, пути вида \\сервер\ресурс\папка\файл.txt разбираются так же. Но существование и доступ проверяются от имени учётной записи, под которой запущен процесс: на сервере это пользователь службы 1С, а не вы.
Можно ли получить путь к файлу, который хранится в базе, как присоединённый?
Нет, у такого файла пути нет: он лежит в базе как двоичные данные или в томах хранения. Чтобы получить настоящий путь, сохраните содержимое во временный каталог и работайте уже с ним.
Чем отличается `ИмяБезРасширения` от `Имя`?
Имя включает расширение (акт.pdf), ИмяБезРасширения отдаёт только акт. Расширение с точкой берите из отдельного свойства.