Как вытащить папку из полного пути к файлу в 1С
Содержание
Чтобы получить каталог файла в 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: с такими папками нет, ответ будет Ложь.
Что делать:
- Разбирайте путь там, где он получен, или на сервере: строковые свойства работают одинаково.
- Проверку существования и чтение выполняйте на клиенте.
- Нужно содержимое на сервере — передайте его как
ДвоичныеДанныеили через временное хранилище, а не строкой пути.
В веб-клиенте прямой доступ к файловой системе пользователя ограничен: работа с локальными файлами требует расширения для браузера и асинхронных вызовов. Код, написанный для тонкого клиента, в браузере может молча не сработать.
В управляемых формах клиентские файловые операции в современных релизах платформы выполняются асинхронными методами с оповещением, а синхронные устарели и в некоторых режимах недоступны. Если форма должна работать и в тонком клиенте, и в браузере, пишите асинхронно сразу.
Проверка существования и обработка ошибок
Новый Файл не падает, даже если пути не существует. Ошибки возникают на следующем шаге: при чтении, копировании, поиске. Поэтому проверяйте до действия, а не после.
Минимальный безопасный набор:
ОбъектФайл = Новый Файл(ПолноеИмя);
Если Не ОбъектФайл.Существует() Тогда
ВызватьИсключение "Файл не найден: " + ПолноеИмя;
КонецЕсли;
Если Не ОбъектФайл.ЭтоФайл() Тогда
ВызватьИсключение "Это каталог, а не файл: " + ПолноеИмя;
КонецЕсли;
Метод ЭтоФайл() отличает файл от каталога, ЭтоКаталог() — наоборот. Это важно, когда путь пришёл от пользователя и он мог ткнуть в папку.
Что ещё ломает код на ровном месте:
- ❌ пустая строка пути:
Новый Файл("")создаст объект,Путьвернёт пустоту, а следом упадёт склейка; - ❌ недопустимые символы в имени (
:,*,?,",<,>,|) — на Windows файл не создастся; - ❌ нет прав на папку: чтение пройдёт, а запись выдаст ошибку доступа;
- ❌ слишком длинный путь: Windows по умолчанию ограничивает его примерно 260 символами.
Если операция с диском может не удаться по причинам вне вашего контроля, оберните её в Попытка … Исключение и покажите пользователю понятный текст, а не техническое сообщение платформы:
Попытка
ДвоичныеДанные = Новый ДвоичныеДанные(ПолноеИмя);
Исключение
Сообщить("Не удалось прочитать файл " + ПолноеИмя + ": " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
Отдельный случай — запись в каталог. Перед сохранением результата убедитесь, что папка существует и в неё можно писать. Простой способ: создайте в ней пустой временный файл, удалите его и только потом запускайте долгую выгрузку. Иначе пользователь час ждёт формирования отчёта, а в конце получает отказ в доступе.
Частые вопросы
Почему `Путь` возвращает слеш на конце, а в диалоге выбора каталога его нет?
Файл отдаёт путь с разделителем, диалог возвращает строку как выбрал пользователь, обычно без него. Приводите оба случая к одному виду функцией добавления разделителя.Как получить имя родительской папки, а не весь путь?
Путь, уберите завершающий разделитель и создайте ещё один объект Файл из этой строки. У него в свойстве Имя окажется название нужной папки, а в Путь — каталог уровнем выше.Работает ли `Новый Файл` с сетевыми путями?
\\сервер\ресурс\папка\файл.txt разбираются так же. Но существование и доступ проверяются от имени учётной записи, под которой запущен процесс: на сервере это пользователь службы 1С, а не вы.Можно ли получить путь к файлу, который хранится в базе, как присоединённый?
Чем отличается `ИмяБезРасширения` от `Имя`?
Имя включает расширение (акт.pdf), ИмяБезРасширения отдаёт только акт. Расширение с точкой берите из отдельного свойства.