Что такое абсолютный и относительный путь к файлу

В наших проектах мы постоянно работаем с файлами:

Но при этом в одних проектах мы пишем просто имя файла, например 'data.json', а в других — вот так: '/Users/mike/server/data.json'. Или в одном месте подключаем скрипт командой

<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>

а в другом — командой <script src="script.js"></script>

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

Абсолютный путь к файлу

Абсолютный (он же полный) путь — это когда мы указываем все диски и папки, в которые нужно зайти, чтобы добраться до нужного файла.

В Windows всё начинается с диска — это может быть C:, D: или диск с любой другой буквой. Если на диске C: сделать папку Thecode, внутри неё — папку Media, а в Media положить файл «статья.txt», то абсолютный путь к этому файлу будет такой:

C:\Thecode\Media\статья.txt

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

C\Thecode\Media\статья.txt ← пропущено двоеточие после буквы диска.

C: Thecode Media статья.txt ← нет разделителей между названиями папок и файлов.

C:\Thecode\Media ← это путь к папке, но имя файла не указано.

В MacOS у дисков нет отдельной назначенной буквы, поэтому всё начинается с корневой папки /, внутри которой и находятся все остальные папки. 

Кстати, в MacOS, Linux и любой UNIX-подобной файловой системе все диски — это тоже папки. Например, когда мы делали скрипт для бэкапа, то флешка с названием WIN10_64 находилась по такому адресу: /Volumes/WIN10_64. А всё потому, что все новые диски по умолчанию подключаются как папки внутри папки /Volumes.

Ещё одно отличие MacOS и Linux в том, что вместо обратной косой черты используется просто косая черта, у которой наклон в другую сторону: /. Всё остальное работает по тому же принципу: в абсолютном пути мы собираем все папки, которые нужно пройти, начиная с корня.

Например, если у нас есть в документах папка Notes, внутри которой лежит markdown-файл с задачами на день tasks.md, то полный путь к нему будет в MacOS выглядеть так:

/Users/mike/Documents/Notes/tasks.md

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

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

Относительный путь к файлу

Относительный путь — это путь к файлу относительно текущего каталога. Текущий каталог — это тот, в котором запускается скрипт, программа или открывается страница в браузере.

Например, если мы в папку Notes из примера выше положим файлы нашего калькулятора на JavaScript, то абсолютные пути к каждому файлу будут выглядеть так:

/Users/mike/Documents/Notes/index.html
/Users/mike/Documents/Notes/style.css
/Users/mike/Documents/Notes/script.js

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

index.html
style.css
script.js

Когда браузер встретит такой путь, он возьмёт путь к каталогу, где лежит страница, и в нём будет искать эти файлы. А если нам понадобится файл data.json из папки Documents, которая лежит уровнем выше, то мы можем обратиться к нему так: 

. ./data.json

Первая точка означает текущий каталог, вторая — на уровень выше, а косая черта — что нам нужно зайти в тот каталог и взять оттуда файл data.json.

Когда что использовать

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

Общее правило будет такое: если вы работаете со своими файлами, лучше указывать относительный путь, а если нужен чужой файл из интернета — то абсолютный.

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Виталий Вебер

Относительный путь к файлу | by sergeybr94 | Medium

Многим новичкам трудно разобраться, как правильно строить относительный путь к файлу. Сегодня, я постараюсь разобрать все нюансы связанные с этим.

Если вы уже знакомы с данной темой, то можете закрыть вкладку и не читать

Начнем. Для начала, посмотрим как выглядит абсолютный путь к файлу на сайте:

https://www.mysite.ru/source/img/main.png

  1. https:// — протокол подключения к сайту
  2. www.mysite.ru — домен
  3. source, img — папки
  4. main.png — нужный нам файл

Выглядит вроде просто. Но на практике, никто не указывает абсолютный путь к файлу при верстке страниц. Для этого всегда используют относительный.

Для правильного построения относительного пути, нужно разобраться с его синтаксисом:

  • Знак точка “ . ” — указывает на текущую (корень) папку, где находится файл
  • Две точки “ .. ” — указывает на родителя текущей папки, где находится файл
  • Слэш “ / ” — показывает какую следующую папку/файл будет выбирать

Пока просто запомните эту информацию. Если во время прочтения, забудете, вернитесь к этому списку и вспомните.

Представим что у вас есть вот такая структура проекта. (папки — прямоугольники, файлы — названия с полоской)

Теперь напишем простой относительный путь до картинки first.png . Для этого, в файле index.html нужно прописать вот такую строчку:

Все работает. И можно было бы закончить, но нет 😁.

Такой адрес показывает, что поиск файла будет происходить относительно папки, где расположен index.html .

Если бы, вы в начале пути указали / или C:// , это являлось бы абсолютным путем. Слэш в начале “ / ” (для Linux систем) или “ C:// ” (для windows) в начале показывает, что поиск файла будет происходит начиная от корня вашего диска (файловой системы).

Как реально выглядит расположение нашего проекта?

Корень проекта, тоже является папкой, поэтому не забывайте об этом.

Алгоритм работы поиска файла

Прописывая вот такой путь до файла:

Браузер начинает его искать вот таких образом:

  1. Подключение картинки написано в файле index.html. Смотрит где находится файл index.html. Находит эту папку. Это корень проекта
  2. В корне проекта ищет папку source. Если нашел, все ок, идем дальше
  3. В папке source ищет папку img
  4. В папке img находит файл first.png

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

Точка

Давайте теперь рассмотрим вариант посложнее.

В начале прописали ./ . Что же это значит? Я уже писал об этом ранее:

  • Точка указывает на текущую папку, где находиться файл index.html. Т.е. на корень проекта
  • Слэш после точки указывает, что внутри корня проекта будет искать папку, которая находиться после него. Т.е. source

В итоге получилась равнозначная запись предыдущей. Алгоритм поиска будет ровно такой же, просто предыдущая запись является сокращенной и более приятной глазу. Почти всегда используют первую запись пути.

Две точки

Но это еще не все. Давайте немного поменяем проект и добавим еще одну папку и страницу:

У нас появились новая папка about, в корне проекта, а в ней страница home.html

И если мы захотим использовать ту же запись для подключения картинки на странице home.html, то она не сработает. Потому, что поиск будет теперь происходить относительно папки about, а не корня проекта, как ранее.

Что же делать? На помощь нам приходят две точки “ .. ”.

Две точки позволяют, так сказать, провалиться на уровень выше. Т.е. поиск будет происходить не от текущей папки home, а от корня проекта.

  1. Подключение картинки написано в файле home.html. Смотрит где находится файл home.html. Находит эту папку. Это папка about
  2. Дальше смотрит кто является родителем папки about. Им является корень проекта
  3. В корне проекта ищет папку source. Если нашел, все ок, идем дальше
  4. В папке source ищет папку img
  5. В папке img находит файл first.png

Если, например, внутри папки home была еще одна папка, и в ней была бы еще одна страница, то путь до файла first.png, внутри этой страницы, выглядел бы вот так:

Теперь все

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

Если у вас еще остались вопросы, можете задать их в моем telegram чате. Так же я занимаюсь обучением программированию в своей telegram группе.

Каталог

— Как открыть файл C с относительным путем?

спросил

Изменено 3 года, 4 месяца назад

Просмотрено 46 тысяч раз

Я пытаюсь проанализировать файл txt и хочу сослаться на него относительно моего текущего местоположения в каталоге. Если я полностью укажу путь к файлу, он будет работать, но я пытаюсь использовать ..\src\test.txt не найдет. Разве это не правильный способ ссылки на файл относительно одного каталога?

Будем признательны за любую помощь

  • c
  • каталог

1

Если вы поместите «..\src\test.txt» в строковую константу, вам нужно удвоить все обратные косые черты, чтобы «\t» и «\s» не интерпретировались как управляющие последовательности. (Или вместо этого вы можете использовать прямую косую черту, что более переносимо.)

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

Если test.txt находится в папке c:\code\app\src, а ваше приложение — в папке c:\code\app, относительный путь «. .\src\test.txt» будет заканчиваться c:\code \src\test.txt (если запущен из проводника).

Попробуйте распечатать вывод _getcwd, прежде чем пытаться открыть файл, чтобы проверить, какой каталог является текущим рабочим каталогом.

Предположим, что ваша структура каталогов выглядит так:

проект
 +---источник
 | test.txt
 | проект c
 \---бин
         a.out <- Рабочий каталог
 

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

Если вы действительно имеете в виду, что файл находится в одном каталоге выше, как вы сказали, вот так: (Примечание: это неудобная структура проекта)

проект
\---источник
    | test.txt
    | проект c
    \---бин
            выход
 

или так: (имеет больше смысла)

проект
| test.txt
+---источник
| проект c
\---бин
        выход
 

Тогда вам нужен путь "../test.txt" или, что то же самое, "../../project/test. txt"

Лучшим местом будет каталог данных, поэтому ваш путь будет ".. /данные/test.txt"

1

Я предполагаю, что это Windows VS vbuild (учитывая обратный путь)

VS поместит двоичный файл во что-то вроде project\bin\debug. И когда вы запустите его в VS, он установит текущий WD в местоположение двоичного файла.

поэтому

а) скопируйте файл в нужное место

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

в - Какие преимущества относительного пути, такого как «../include/header.h» для заголовка?

Я просмотрел вопросы Как правильно использовать директиву include и семантику #include C++, и ни один из них не касается этого, как и другие, предложенные SO, когда я набрал заголовок...

Каковы преимущества записи:

 #include "../include/someheader. h"
#include "../другой_каталог/другой.h"
 

по сравнению с использованием простого имени файла:

 #include "someheader.h"
#include "другой.h"
 

или, возможно, относительное имя без " .. ":

 #include "include/someheader.h"
#include "другой_каталог/другой.h"
 

Проблемы, которые я вижу:

  • Вы не можете переместить заголовок, не беспокоясь о том, какие исходные файлы включают его.
  • Вы можете получить очень длинные пути для заголовков в зависимостях и отчетах об ошибках. Сегодня у меня был один с " ../dir1/include/../../include/../dir2/../include/header.h ".

Единственное достоинство, которое я вижу, заключается в том, что, хотя вам не нужно перемещать файлы, вы можете уйти, не всегда используя директивы '

-I ' для поиска заголовков, но потеря гибкости и сложность компиляции в под-подкаталогах и т. д., кажется, перевешивает выгоду.

Значит, я упускаю выгоду?


Спасибо за информацию. Я думаю, что все согласны с тем, что в обозначении с использованием «..» нет никаких серьезных преимуществ, которые я упускаю из виду. В общем, мне нравится нотация «somewhere/header.h»; Я использую его в новых проектах. Тот, над которым я работаю, далеко не новый.

Одна из проблем заключается в том, что существуют различные наборы заголовков, часто с такими префиксами, как

rspqr.h , rsabc.h , rsdef.h , rsxyz.h . Все они связаны с кодом в каталоге rsmp , но некоторые из заголовков находятся в каталоге rsmp , а другие — в центральном каталоге include, в котором нет подкаталогов, таких как rsmp . (И повторите для различных других областей кода: заголовки находятся в нескольких местах, случайным образом нужные другим частям кода.) Перемещение элементов — серьезная проблема, потому что код за эти годы стал настолько запутанным. И makefiles не согласованы в которых -I вариантов.
В общем, это грустная история не очень доброго пренебрежения в течение десятилетий. Исправить все это, ничего не сломав, будет долгой и утомительной работой.

  • c
  • включает
  • заголовок

4

Я предпочитаю синтаксис пути, так как он ясно показывает, к какому пространству имен или модулю принадлежит заголовочный файл.

 #include "Physics/Solver.h"
 

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

Я почти никогда не использую синтаксис «..», вместо этого в моем проекте указаны правильные базовые местоположения.

Проблема с #include "../include/header.h" заключается в том, что он часто срабатывает случайно, а затем кажущееся несвязанным изменение приводит к тому, что позже он перестает работать.

Например, рассмотрим следующий исходный макет:

 ./include/header. h
./lib/library.c
./lib/feature/feature.c
 

Допустим, вы запускаете компилятор с путем включения -I. -I./lib . Что происходит?

  • ./lib/library.c может сделать #include "../include/header.h" , что имеет смысл.
  • ./lib/feature/feature.c также может выполнять #include "../include/header.h" , хотя это и не имеет смысла. Это связано с тем, что компилятор попытается использовать директиву #include относительно местоположения текущего файла, и если это не удастся, он попробует Директива #include относительно каждой записи -I в пути #include .

Более того, если вы позже удалите -I./lib из пути #include , вы сломаете ./lib/feature/feature.c .

Я считаю предпочтительнее следующее:

 ./projectname/include/header.h
./имя_проекта/lib/library.c
./projectname/lib/feature/feature.
c

Я бы не стал добавлять какие-либо записи пути включения, кроме -И. , а затем оба library.c и feature.c будут использовать #include "projectname/include/header.h" . Предполагая, что "имя проекта" может быть уникальным, в большинстве случаев это не должно приводить к конфликтам имен или двусмысленности. Вы также можете использовать путь включения и/или функцию make VPATH , чтобы разделить физический макет проекта на несколько каталогов, если это абсолютно необходимо (например, для размещения автоматически сгенерированного кода для конкретной платформы; это то, что действительно ломается, когда вы использовать

#include "../../somefile.h" ).

3

IANALL, но я не думаю, что вы должны помещать .. в фактические исходные файлы C или C++, потому что это не переносимо и стандарт не поддерживает это. Это похоже на использование \ в Windows. Делайте это только в том случае, если ваш компилятор не может работать ни с каким другим методом.

5

Начни путь своей

#include "" директив с последовательностью из одной или нескольких " ../ "s когда:

  • вы хотите включить файл, расположение которого с включаемым файлом фиксировано и
  • вы строите в системе POSIX или с VC++ и
  • вы хотите избежать двусмысленности в отношении того, какой файл будет включен.

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

Например, рассмотрим следующий макет проекта:

 ./your_lib/include/foo/header1.h
./your_lib/include/bar/header2. h
./their_lib/include/bar/header2.h
 

Каким образом your_lib/include/foo/header1.h должен включать your_lib/include/bar/header2.h ? Рассмотрим два варианта:

  1. #include

    Предполагая, что и your_lib/include , и its_lib/include указаны как пути поиска заголовков (например, с использованием GCC -I или -isystem ), то выбор header2.h зависит от порядка поиска этих двух путей.

  2. #include "../bar/header2.h"

    Первое расположение, в котором компилятор будет искать, — это расположение your_lib/include/foo/header1.h , то есть your_lib/include/foo/ . Сначала он попробует your_lib/include/foo/../bar/header2.h , что сокращается до your_lib/include/bar/header2.h , где он найдет правильный файл. Пути поиска заголовков вообще не будут использоваться, и здесь мало места для двусмысленности.

Я бы настоятельно рекомендовал вариант 2) в этом случае по указанным причинам.

В ответ на некоторые аргументы в других ответах:

  • @andrew-grant говорит:

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

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

  • @bk1e говорит:

    ...часто срабатывает случайно...

    Я бы сказал, что относительные пути будут работать только случайно в очень редких случаях, когда проект был сломан с самого начала и его можно было легко исправить. Столкнуться с таким конфликтом имен, не вызывая ошибки компилятора, кажется маловероятным. Обычный сценарий — это когда файл зависимого проекта включает один из ваших заголовков, который включает в себя другой из ваших заголовков. Компиляция вашего набора тестов должна привести к ошибке «Нет такого файла или каталога» при компиляции отдельно от этого зависимого проекта.

  • @singlenegationelimination говорит

    ... это не переносимо и не поддерживается стандартом.

    Стандарт ISO C может не указывать все детали систем, в которых компилируется или запускается программа. Это не означает, что они не поддерживаются, просто стандарт не указывает слишком много платформ, на которых будет работать C. (Различие между тем, как "" и <> интерпретируются в обычных современных системах, вероятно, происходит из стандарта POSIX.)

  • @daniel-paull говорит

    «..» имеет относительное расположение и является хрупким

    Хрупкий как? Предположительно чувствителен к расположению двух файлов. Таким образом, " .. " следует (и всегда) использовать только тогда, когда автор включаемого файла контролирует их местоположение.

3

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

Я бы избегал таких путей, как:

  • "include/foo/bar.h" — "include" кажется нелогичным и избыточным
  • "../foo/bar.h" — ".." принимает относительное расположение и является хрупким
  • "bar.h" — если bar.h не находится в текущем каталоге, это загрязняет глобальное пространство имен и вызывает двусмысленность.

Лично я склонен добавлять путь, подобный следующему, в мои проекты, включая путь — "..;../..;../../..;../../../.. .

Это позволяет применить своего рода правило скрытия к вашим #include и дает некоторую свободу перемещения заголовков, не нарушая остальной код. Конечно, это происходит за счет введения риска привязки к неправильному заголовочному файлу, если вы не будете осторожны, поскольку неполные имена могут быть (или станут со временем) неоднозначными.

Я стремлюсь полностью квалифицировать #include в общедоступных заголовках, чтобы третьим лицам, использующим мой код, не нужно было добавлять "..;../..;../../..;.. /../../.." в свой проект — это просто удобство для моего личного кода и системы сборки.

14

Другая проблема с окнами с относительными путями — MAX_PATH. Это вызовет проблемы компиляции, когда, например. кросс-компиляция для Android, и ваш путь становится больше 260 в длину.

1

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