Пути файловых систем в Windows страннее, чем можно подумать. В любой производной от Unix системе пути на удивление просты: если нечто начинается с /, то это путь. Но всё совершенно иначе в Windows, которая имеет озадачивающее разнообразие схем составления пути.
Когда я реализовал функцию автозавершения пути в Fileside 1.7, мне нужно было изучить этот вопрос внимательнее, чтобы ничего не упустить. В этой статье я расскажу о своих находках.
Стоит заметить, что статья ограничивается только тем типом путей, который видит пользователь приложений Windows (обусловленный Win32 API). Под этим слоем есть ещё больше любопытного, в основном касающегося тех, кто пишет драйверы оборудования и тому подобное.
Вкратце
Форматы абсолютных путей
Форматы относительных путей
Запрещённые символы
Ограничения длины
Схемы путей Windows
В Windows существует три разных вида абсолютного пути и три разных типа относительного пути.
Абсолютные пути
Абсолютные, или полные пути — это завершённые пути, сами по себе уникальным образом идентифицирующие местоположение в файловой системе.
Пути к диску
Пути к диску — это старые добрые пути, которые мы знаем и любим, они состоят из буквы диска и последовательности папок.
D:\Doughnut preferences\With jam in
UNC-пути
UNC расшифровывается как Universal Naming Convention, это описание файлов, начинающееся с \\, часто используемое для ссылок на сетевые накопители. Первый сегмент после \\ — это хост, который может быть или сервером с именем, или IP-адресом:
\\Work\Hard
\\192.168.1.15\Hard
UNC-пути также можно использовать для доступа к локальным дискам:
\\localhost\C$\Users\Andrew Fletcher
\\127.0.0.1\C$\Users\Alan Wilder
Или с использованием имени компьютера:
\\Pipeline\C$\Users\Martin Gore
Символ $ в C$ обозначает скрытую административную общую папку; он не заменяет двоеточие рядом с именем диска :. Общие диски в стиле C$ — это просто удобные ярлыки, автоматически создаваемые Windows. Доступ к дискам через них возможен, только если вы вошли как администратор.
Стоит также заметить, что \\Pipeline сам по себе не валидный путь к папке, он идентифицирует только сервер. Чтобы попасть в папку, нужно добавить имя общей папки.
Пути к устройству
Путь к устройству начинается с одного из следующих фрагментов:
\\?\\\.\
Кроме файлов и папок их можно использовать для адресации физических устройств (дисков, дисплеев, принтеров и так далее). Не совсем то, что вы используете в повседневном процессе управления файлами, но это полезно знать, если вы когда-нибудь найдёте что-то подобное.
Синтаксис доступа к локальной папке выглядит как один из этих вариантов:
\\?\Z:\Animals\Cute
\\.\Z:\Animals\Cunning
Если вам нужно ещё больше загадочности, то можно также подставить эквивалентный Z: идентификатор устройства:
\\?\Volume{59e01a55-88c5-411f-bf0b-92820bdb2548}\Animals\Cryptic
Здесь Volume{59e01a55-88c5-411e-bf0a-92820bdb2549} — это идентификатор дискового тома, на котором находится Z: в компьютере.
Также существует специальный синтаксис для описания UNC-путей как путей к устройству:
\\?\UNC\localhost\Z$\Animals\Curious
В путях к устройству часть, идущая после \\?\ или \\.\ — это имя, определённое во внутреннем пространстве имён Object Manager Windows. Те, кому любопытно исследовать это пространство имён, могут скачать инструмент WinObj и посмотреть.
Нормализованные и литеральные пути к устройству
Так в чём же разница между \\?\ и \\.\?
В обычном случае, когда вы передаёте путь операционной системе Windows, она очищает его, прежде чем использовать. Этот процесс называется нормализацией, подробнее о нём мы поговорим ниже.
Путь \\?\ пропускает этот этап очистки, а \\.\ не пропускает. Поэтому можно назвать пути \\?\ литеральными путями к устройству, а \\.\ — нормализованными путями к устройству.
Допустим, по какой-то непонятной причине, у вас есть файл с именем .. (например, он мог быть создан на сетевом диске в другой системе). В обычном случае вы бы не смогли получить доступ к нему, потому что нормализация резолвит его в родительскую папку, но благодаря литеральному пути к устройству это можно сделать.
Относительные пути
Относительные пути — это неполные пути, которые для уникальной идентификации местоположения необходимо скомбинировать с другим путём.
Пути, относительные к текущей папке
Эти пути используют в качестве начальной точки текущую папку, например, .\Torquay относится к подпапке текущей папки, а ..\Wales относится к подпапке родителя текущей папки.
Папки, относительные к корню текущего диска
Если начать путь с одной \, то путь интерпретируется как относительный к корню текущего диска. Поэтому если вы находитесь в любом месте диска E: и введёте \Africa, то окажетесь в E:\Africa.
Когда доступ к текущей папке выполняется через UNC-путь, то путь, относительный к текущему диску, интерпретируется относительно к общей корневой папке, допустим \\Earth\Asia.
Пути, относительные к текущей папке диска
Эти более редко используемые пути указывают диск без обратной косой черты, например E:Kreuzberg, и интерпретируются относительно к текущей папке этого накопителя. На самом деле это имеет смысл только в контексте оболочки командной строки, отслеживающей текущую рабочую папку для каждого диска.
Это единственный тип путей, не поддерживаемый Fileside, потому что в нём нет понятия текущей папки каждого диска. Текущую папку имеют только панели.
Нормализация
Как говорилось ранее, все пути, за исключением литеральных путей к устройству, перед использованием проходят процесс нормализации. Этот процесс состоит из следующих этапов:
- Замена косых черт (
/) на обратные косые черты (\) - Сворачивание повторяющихся разделителей в виде обратных косых черт в один
- Резолвинг относительных путей заменой всех
.или.. - Отсечение завершающих пробелов и точек
Таким образом, в общем случае можно указывать пути Windows при помощи косых черт.
Правила именования в Windows
Теперь рассмотрим отдельные элементы, из которых состоит путь. Существует множество ограничений имён, которые можно использовать для файлов и папок.
Запрещённые символы
В имени нельзя использовать следующие символы:
< > " / \ | ? *
Также исключаются любые непечатаемые символы со значением ASCII меньше 32.
Хитрое двоеточие
В большинстве случаев : также запрещено.
Однако существует экзотическое исключение в виде изменённых потоков данных NTFS, в которых двоеточие используется в качестве разделителя внутри имени. Малоизвестно, что в некоторых контекстах можно хранить внутри файла скрытый фрагмент данных, добавляя к его имени суффикс, которому предшествует двоеточие.
Опасная точка
Символ . допустим внутри или в начале имени, но запрещён в конце.
Начинающие и завершающие пробелы
Любопытно, что Windows допускает пробелы в начале, но не в конце имён. Так как имя с пробелами в начале и конце часто выглядит похожим на имя без пробелов, обычно это ужасная идея, и при переименовании или создании файлов Fileside автоматически удаляет их.
Запрещённые имена
По историческим причинам нельзя использовать следующие имена:
CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 и LPT9.
Это включает и имена с расширениями. Например, если вы назовёте файл COM1.txt, то внутри он преобразуется в \\.\COM1\ и интерпретируется самой Windows как устройство. А это не то, что нам нужно.
Чувствительность к регистру
В большинстве случаев Windows не делает различий между символами в верхнем и нижнем регистре в путях.
C:\Polish hamlet, c:\polish Hamlet, C:\Polish Hamlet и C:\POliSh hAMlET считаются абсолютно одинаковыми.
Однако с обновления Windows 10 за апрель 2018 года файловые системы NTFS имеют опцию включения чувствительности к регистру на уровне папок.
Ограничения длины
Мы ещё не закончили: ограничения есть и на длину.
Пути
Традиционно длина пути в Windows не могла превышать 260 символов. Даже сегодня это справедливо для некоторых приложений, если только их разработчики не предприняли мер для обхода этого ограничения.
Этот обход заключается в преобразовании каждого пути в литеральный путь к устройству перед передачей его Windows. Сделав это, мы сможем обойти ограничение в 260 символов и увеличить его до чуть более щедрого предела в 32767 символов.
Имена
Имена файлов и папок не могут быть длиннее 255 символов.
Так много способов сказать одно и то же
Вооружённые этим знанием, мы понимаем, что можем создать почти неограниченное количество различных строк путей, и все они будут ссылаться на одну и ту же папку.
C:\CHAMELEONc:\chameleonC:\/\\//\\\///ChameleonC:\Windows\..\Users\..\Chameleon\\localhost\C$\Chameleon\\127.0.0.1\C$\Chameleon\\?\C:\Chameleon\\.\C:\Chameleon\\.\UNC\localhost\C$\Chameleon\\?\Volume{59e01a55-88c5-411e-bf0a-92820bdb2549}\Chameleon\\.\GLOBALROOT\Device\HarddiskVolume4\Chameleon- и так далее
Вот что получаешь, когда приходится обеспечивать полную обратную совместимость в течение нескольких десятилетий!
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Введение
Абсолютные и относительные пути определяют расположение файла или каталога в файловой системе. Это набор инструкций для ОС по поиску того или иного элемента. Понимание того, как использовать пути, поможет как обычным, так и опытным пользователям.
В этой статье мы рассмотрим, чем отличается абсолютный путь от относительного, и посмотрим, как их найти и создать в своей системе.
Что такое пути?
Пути в компьютерной системе – это иерархический способ указания места, где располагается файл или каталог. Пути имеют ключевое значение для операционной системы, так как с их помощью она может находить файлы и каталоги, а также получать к ним доступ.
Существует два типа путей:
- Абсолютный путь. Полный и точный путь к файлу или каталогу из корневого каталога системы.
- Относительный путь. Расположение файла или каталога относительно текущего рабочего каталога.
То, какой путь выбрать, зависит от конкретной задачи и потребности в переносимости в данной ситуации. В следующих разделах мы подробно рассмотрим оба пути и приведем примеры.
Что такое абсолютный путь?
Абсолютный путь – это расположение файла или каталога, которое указывается, начиная с корневого каталога. Это полный путь от начала файловой системы.
Абсолютный путь всегда начинается с жесткого диска или какого-либо другого устройства хранения и описывает каждый шаг, который должен сделать пользователь, перемещаясь по файловой системе, чтобы оказаться в указанном месте. Абсолютные пути точны и просты для понимания. Их следует использовать тогда, когда вам нужно точно указать местоположение файла или каталога независимо от вашего текущего рабочего каталога.
Однако с абсолютными путями может быть неудобно работать, особенно если вы работаете с глубоко вложенными каталогами.
Пример абсолютного пути
Абсолютный путь может записываться по-разному. Это зависит от операционной системы. Любой абсолютный путь начинается с корневого каталога. В Unix-подобных ОС он записывается в виде слеша (/), а в Windows – в виде буквы диска и обратного слеша (например, С:\).
Вот пример абсолютного пути в Linux:
/home/user/documents/file.txt
А вот так выглядит абсолютный путь в Windows:
C:\Users\User\Documents\file.txt
И тот, и другой путь начинается с диска или корневого каталога и ведет к файлу под названием file.txt. Но при этом каждая ОС использует свой собственный способ указания пути.
Что такое относительный путь?
Относительный путь – это расположение файла или каталога относительно вашего текущего рабочего каталога. В отличие от абсолютного, относительный путь не начинается со слеша или буквы диска, так как он не начинается с корневого каталога.
Относительные пути упрощают работу с файлами, минимизируя количество вводимого текста для указания пути к файлу или каталогу. Относительный путь используется в тех случаях, когда вы ссылаетесь на файл или каталог только по его имени. Это самая простая форма относительного пути. В данном случае оболочка будет искать файл с указанным именем в текущем каталоге.
Относительные пути способны адаптироваться к изменениям в текущем рабочем каталоге, что делает из удобными для написания сценариев, разработки и управления файлами в пределах ограниченной области.
Этот путь также может принимать точки входа. Таким образом, пользователи могут указывать относительные пути к файлам, которые расположены ближе к корневому каталогу. Например,
- Символ .. говорит о том, что файл расположен в родительском каталоге
- Символ . говорит о том, что файл расположен в текущем каталоге
Для всех остальных файлов, которые расположены еще ближе к корневому каталогу, уже нужно использовать абсолютный путь.
Пример относительного пути
Относительный путь записывается по-разному в зависимости от ОС. Например, в Linux для разделения каталогов используется слеш (/), а в Windows – обратный слеш (\).
Linux
Если ваш текущий рабочий каталог – это /home/user/documents, и вы хотите обратиться к файлу с именем file.txt, который находится в этом каталоге, относительный путь будет выглядеть вот так:
file.txt
Система будет искать указанный файл в текущем рабочем каталоге.
Аналогично, чтобы обратиться к файлу с именем report.txt, который расположен в подкаталоге report текущего каталога, нужно записать следующий относительный путь:
./reports/report.txt
Путь указывает ОС, что искать файл report.txt нужно в каталоге report текущего рабочего каталога.
Windows
Если в данный момент вы находитесь в каталоге C:\Users\John\Documents и хотите обратиться к файлу с именем document.txt, который расположен в текущем каталоге, то запишите следующий относительный путь:
.\document.txt
Этот путь указывает Windows, что искать файл document.txt необходимо в текущем рабочем каталоге, то есть C:\Users\John\Documents. Символ точки (.) обозначает текущий каталог.
Если вы хотите обратиться к файлу invoice.pdf, который расположен в подкаталоге invoices текущего каталога, относительный путь будет выглядеть вот так:
.\invoices\invoice.pdf
Путь указывает системе, что искать файл invoice.pdf необходимо в каталоге invoices текущего рабочего каталога. Здесь символ обратного слеша (\) разделяет каталоги.
Абсолютные и относительные пути в Windows
Если речь идет о Windows, здесь для разделения каталогов в абсолютных и относительных путях используется обратный слеш (\). Абсолютный путь здесь начинается с буквы диска, двоеточий и обратного слеша (например, С:\).
Как найти абсолютный путь в Windows
Существует несколько способов, как можно найти абсолютный путь к файлу или каталогу в Windows. Самые популярные способы перечислены ниже.
Проводник (File Explorer)
1. Нажмите клавишу Windows и введите «Проводник». Откройте приложение, нажав Enter.
2. Перейдите туда, где расположен файл или каталог, абсолютный путь которого вы хотите найти.
3. После того, как вы окажетесь там, нажмите на адресную строку. Здесь отображается полный путь к текущему местоположению. Это и есть абсолютный путь:
4. Или вы можете щелкнуть правой кнопкой мыши на файл, для которого хотите найти абсолютный путь, и выбрать «Свойства» (Properties).
Окно свойств содержит расположение файла, то есть его абсолютный путь:
Командная строка (Command prompt)
1. Нажмите клавишу Windows и введите «Командная строка». Откройте приложение, нажав Enter.
2. Воспользуйтесь командой cd, чтобы перейти к каталогу, где содержится файл или каталог, для которого вы хотите найти абсолютный путь.
3. После того, как вы окажетесь там, запустите команду cd без аргументов. Отобразится абсолютный путь текущего каталога:
cd
PowerShell
1. Нажмите клавишу Windows и введите «PowerShell». Откройте приложение, нажав Enter.
2. Воспользуйтесь командой cd (Change-Location), чтобы перейти к каталогу, где содержится файл или каталог.
3. После того, как вы окажетесь в нужном каталоге, запустите командлет Get-Location, чтобы отобразить абсолютный путь текущего каталога.
Абсолютные и относительные пути в Linux
Если речь идет о Linux, то здесь для разделения каталогов в абсолютных и относительных путях используются слеши (/). Любой абсолютный путь здесь начинается с корневого каталога (/).
Ниже приведены инструкции, как найти абсолютный путь в Linux.
Как найти абсолютный путь в Linux
Существует несколько способов, как можно найти абсолютный путь к файлу или каталогу в Linux. Ниже приведены самые популярные.
Команда pwd
1. Откройте терминал, нажав Ctrl+Alt+T.
2. Воспользуйтесь командой cd, чтобы перейти в каталог, где содержится файл или каталог, для которого вы хотите найти абсолютный путь.
3. Запустите команду pwd (print working directory). Отобразится абсолютный путь текущего каталога.
pwd
Команда realpath
1. Откройте терминал.
2. Воспользуйтесь следующим синтаксисом, чтобы определить абсолютный путь к файлу или каталогу, пусть даже это символическая ссылка или относительный путь:
realpath [/path/to/file]
Например,
Графический пользовательский интерфейс
Чтобы найти абсолютный путь файла в Linux вам понадобиться Проводник.
1. Найдите в Файлах (Files) тот файл, абсолютный путь которого вы хотите найти.
2. Щелкните по нему правой кнопкой мыши и выберите «Свойства» (Properties).
3. В окне свойств найдите строку «Корневая папка» (Parent folder), где и указан абсолютный путь к родительскому каталогу файла.
Как создать относительный путь?
Путь файла также можно использовать для обращения к внешним файлам, например, веб-страницам, изображениям, таблицам стилей и т.д. Такие пути описывают расположение файла в структуре папок веб-сайта.
Абсолютные пути к файлам (полный URL-адрес) используют теги HTML, например, <a> и <img>, и это логично, так как он указывает на URL-адрес файла в Интернете. А вот относительные пути указывают на файл относительно текущей страницы. Относительные пути начинаются с ./. Далее следует путь к локальному файлу.
Чтобы создать ссылку на файл в Интернете с помощью относительного пути, выполните следующее:
Шаг 1. Убедитесь, что файл, на который вы хотите создать ссылку, и HTML-файл, с которым вы работаете, находятся на одном сервере. Неважно, разрабатываете ли вы веб-сайт онлайн или локально, файл, на который вы хотите создать ссылку, должен быть доступен.
Шаг 2. Чтобы создать относительный путь, путь к файлу должен быть записан относительно каталога, где расположен HTML-файл, с которым вы работаете. Представим, что вы хотите создать ссылку на файл с именем example.jpg, который располагается на текущей странице, а ваш локальный HTML-файл находится в каталоге web-development.
Шаг 3. Если относительно вашего HTML-файла файл example.jpg находится в подкаталоге images, то используйте символ ../, чтобы переместиться на один уровень каталога вверх. А затем укажите каталог images. В результате получится вот такой относительный путь:
<a href="../images/example.jpg"><img src="../images/example.jpg"></a>
В данном примере символ ../ указывает на переход на один уровень каталога вверх и последующее обращение к каталогу images с целью получить доступ к нужному файлу.
Добавив относительный путь в HTML, вы можете получить доступ к указанному файлу напрямую без необходимости указывать абсолютный путь, который включает все каталоги, ведущие к файлу. Преимущество использования относительных путей в HTML заключается в том, что вы можете без каких-либо проблем изменять имена доменов. Кроме того, вы можете спокойно перемещать каталоги, так как это никак не повлияет на относительный путь.
Заключение
В этой статье мы выяснили, чем отличается абсолютный путь от относительного, а также продемонстрировали, как можно найти абсолютный путь к файлу или каталогу в Linux и Windows с помощью графического пользовательского интерфейса или командной строки. Кроме того, мы рассмотрели, как можно создать относительный путь, ведущий к URL-адресу файла в Интернете.
Пути файлов – и абсолютные, и относительные, нужны для управления файлами, перемещения между ними и обращения к ним посредством сценариев. Они используются в самых различных ситуациях для эффективного взаимодействия и организации файлов и каталогов.
В наших проектах мы постоянно работаем с файлами:
- подключаем файлы со стилями в браузерных проектах;
- указываем нужные для бэкапа файлы при написании скриптов для резервного копирования;
- настраиваем служебные файлы на сервере;
- сохраняем в файл полученные через JSON данные;
- и делаем ещё десяток дел, используя файлы для решения своих задач.
Но при этом в одних проектах мы пишем просто имя файла, например '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.
Вам может быть интересно:
Когда что использовать
В наших проектах при подключении файлов скриптов или стилей мы используем относительный путь, потому что они лежат в той же папке, что и веб-страница для браузера. А вот когда мы подключаем скрипты из интернета, то указываем полный путь, потому что они лежат не в нашей папке, а на своём сервере.
Общее правило будет такое: если вы работаете со своими файлами, лучше указывать относительный путь, а если нужен чужой файл из интернета — то абсолютный.
Художник:
Даня Берковский
Вёрстка:
Кирилл Климентьев
Распознавание голоса и речи на C#
UnmanagedCoder 05.05.2025
Интеграция голосового управления в приложения на C# стала намного доступнее благодаря развитию специализированных библиотек и API. При этом многие разработчики до сих пор считают голосовое управление. . .
Реализация своих итераторов в C++
NullReferenced 05.05.2025
Итераторы в C++ — это абстракция, которая связывает весь экосистему Стандартной Библиотеки Шаблонов (STL) в единое целое, позволяя алгоритмам работать с разнородными структурами данных без знания их. . .
Разработка собственного фреймворка для тестирования в C#
UnmanagedCoder 04.05.2025
C# довольно богат готовыми решениями – NUnit, xUnit, MSTest уже давно стали своеобразными динозаврами индустрии. Однако, как и любой динозавр, они не всегда могут протиснуться в узкие коридоры. . .
Распределенная трассировка в Java с помощью OpenTelemetry
Javaican 04.05.2025
Микросервисная архитектура стала краеугольным камнем современной разработки, но вместе с ней пришла и головная боль, знакомая многим — отслеживание прохождения запросов через лабиринт взаимосвязанных. . .
Шаблоны обнаружения сервисов в Kubernetes
Mr. Docker 04.05.2025
Современные Kubernetes-инфраструктуры сталкиваются с серьёзными вызовами. Развертывание в нескольких регионах и облаках одновременно, необходимость обеспечения низкой задержки для глобально. . .
Создаем SPA на C# и Blazor
stackOverflow 04.05.2025
Мир веб-разработки за последние десять лет претерпел коллосальные изменения. Переход от традиционных многостраничных сайтов к одностраничным приложениям (Single Page Applications, SPA) — это. . .
Реализация шаблонов проектирования GoF на C++
NullReferenced 04.05.2025
«Банда четырёх» (Gang of Four или GoF) — Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес — в 1994 году сформировали канон шаблонов, который выдержал проверку временем. И хотя C++ претерпел. . .
C# и сети: Сокеты, gRPC и SignalR
UnmanagedCoder 04.05.2025
Сетевые технологии не стоят на месте, а вместе с ними эволюционируют и инструменты разработки. В . NET появилось множество решений — от низкоуровневых сокетов, позволяющих управлять каждым байтом. . .
Создание микросервисов с Domain-Driven Design
ArchitectMsa 04.05.2025
Архитектура микросервисов за последние годы превратилась в мощный архитектурный подход, который позволяет разрабатывать гибкие, масштабируемые и устойчивые системы. А если добавить сюда ещё и. . .
Многопоточность в C++: Современные техники C++26
bytestream 04.05.2025
C++ долго жил по принципу «один поток — одна задача» — как старательный солдатик, выполняющий команды одну за другой. В то время, когда процессоры уже обзавелись несколькими ядрами, этот подход стал. . .
Материалы по курсу «Основы программирования»
Основные понятия: файлы, пути к файлам
Файл — именованная область в долговременной памяти (на диске). Правила,
определяющие допустимые имена файлов, зависят от используемой файловой
системы.
Если имя файла включает как минимум один знак ., то часть имени
от последнего знака . и до конца называется расширением. Как правило,
расширение указывает тип содержимого файла: .jpg — картинка в формате JPEG,
.txt — текстовый файл, .exe — исполнимый файл ОС Windows, .py — исходный
текст на языке Python и т.д.
Часто интерфейс операционной системы скрывает расширения файлов, т.е. файлы
с именами input.txt и program.py будут отображаться как input
и program. Распространённая ошибка — забыть о том, что расширения скрываются
и добавить «лишнее» расширение, создав, например, текстовый файл
input.txt.txt — в папке он будет показываться как input.txt,
т.к. расширение будет скрыто. Но при этом программа на Python не сможет найти
файл input.txt, т.к. реально его в папке нет — есть файл с именем
input.txt.txt.
Файловая система — способ хранения данных в долговременной памяти,
использующий именованные файлы.
Папка (каталог, «директория») — именованная группа файлов. Как правило,
файл находится только в одной папке. Некоторые файловые системы позволяют
одному файлу находиться в нескольких папках одновременно, но это редкая
и специфическая практика.
Папки тоже являются файлами (специального вида) и могут находиться внутри
других папок. Если папка A находится внутри папки B, то папка B для
папки A считается родительской. Корневая папка (корневой каталог) —
папка, у которой нет родительской.
Путь к файлу — строка, описывающая расположение файла в файловой системе.
У запущенной программы одна из папок файловой системы является текущей —
это своего рода неявная глобальная переменная.
Пути к файлам могут быть относительными и абсолютными. Относительный путь
к файлу указывается, начиная с текущей папки, абсолютный путь к файлу —
начиная с корневой папки.
В каждой папке (кроме корневой) неявно присутствуют две имени файла — ссылка
на текущую папку (записывается как одинарная точка .) и ссылка на родительскую
папку (записывается как две точки: ..). Они используются, чтобы
в относительном пути ссылаться на текущую папку или на родительскую.
Путь к файлу включает в себя перечисление имён папок, в которые нужно перейти
относительно текущей или корневой папки, чтобы добраться до файла плюс в конце
само имя файла.
Если файл находится в текущей папке, то относительный путь к нему состоит
из одного имени файла.
Правила записи пути к файлу зависят от операционной системы.
Особенности путей к файлам на Windows
На Windows компоненты пути разделяются знаками прямой / и обратной \
косой черты. Причём в интерфейсе (командная строка, элементы графического
интерфейса) обратная косая черта \ работает всегда, прямая / не всегда
(из соображений совместимости по историческим причинам).
Относительный путь к файлу — перечисление имён папок с именем целевого
файла в конце, разделителем служат знаки / или \. Текущих папок
на Windows может быть несколько, у каждой буквы диска — своя текущая папка,
поэтому относительный путь на Windows может начинаться с буквы диска
и двоеточия.
Абсолютный путь к файлу начинается с разделителя папок (т.е. / или \),
который может предваряться буквой диска. Если буква диска не указана,
то подразумевается текущая буква диска.
Абсолютные пути к файлу:
• D:\Mazdaywik\Documents\Преподавание\L4\b-02 Файловый ввод-вывод.md
• D:/Mazdaywik/Documents/Преподавание/L4/b-02 Файловый ввод-вывод.md
• \Mazdaywik\Documents\Преподавание\L4\b-02 Файловый ввод-вывод.md
• C:\Program Files\Far Manager\Far.exe
• C:\Windows\System32\notepad.exe
Относительные пути:
• b-02 Файловый ввод-вывод.md
• D:b-02 Файловый ввод-вывод.md
• C:Far.exe
• 2020-2021\rk1.pdf
• ..\curricula.ref
• ../curricula.ref
Первые три примера — файл, который я сейчас пишу, а вы сейчас читаете.
Последние два примера означают переход в родительскую папку. Абсолютный путь
для него выглядит как
• D:/Mazdaywik/Documents/Преподавание/curricula.ref
(т.к. текущая папка — D:\Mazdaywik\Documents\Преподавание\L4).
В именах файлов на Windows запрещены знаки ", \, /, <, >, :, *,
?. Некоторые имена файлов зарезрвированы — nul, prn, con, com1,
lpt1, aux и ряд других — нельзя создать файл с этим именем и любым
расширением (например, nul.txt, con.jpg, aux.py и т.д.)
Имена файлов не чувствительны к регистру — заглавные и строчные буквы считаются
одинаковыми. Файл с именем штукатурка.txt можно открыть, используя имена
• Штукатурка.txt
• ШТУКАТУРКА.TXT
• ШтукаТурка.txt
• штукаТУРКА.tXt
• ШтУкАтУрКа.TxT
Особенности путей к файлам на unix-подобных ОС (Linux и macOS)
Здесь всё проще. Разделителем компонентов в пути является только знак /.
Корень у файловой системы один, букв диска нету. Вместо этого для подключаемых
устройств (например, флешек) создаются отдельные папки, внутри которых
отображается содержимое устройства.
Примеры путей к файлам:
Абсолютные пути:
• /usr/bin/python3
• /home/username/Documents/Таблица.docx
• /media/My Flash Drive/Music/Моргенштерн.mp3
Относительные пути:
• Documents/Таблица.docx
• ../Pictures/facepalm.jpg
На Linux в именах файлов допустимы любые знаки, кроме знака / (т.к. он
зарезервирован для разделителя путей), в отличие от Windows, зарезервированных
имён нет (кроме . — ссылки на текущую папку и .. — ссылка на родительскую).
На Linux имена файлов учитывают регистр символов — большие и маленькие буквы
считаются разными. Имена файлов hello.txt и HELLO.TXT — это имена двух
разных файлов.
На macOS регистр имён файлов, как и на Windows, не учитывается.
Текстовый файл — файл, хранящий в себе последовательность текстовых строк.
Строки в текстовом файле разделяются знаками перевода строки \n. Символы,
содержащиеся в строках, представляются байтами или последовательностями
байтов при помощи так называемых кодировок — наборов правил представления
символов в виде байтов.
Однобайтовые кодировки (вроде Windows-1251 для кириллицы) представляют каждый
символ в виде одного байта, поэтому поддерживают только небольшой набор символов
(не более 256 включая управляющие символы). Например, кодировка Windows-1251
для кириллицы поддерживает только кириллические и латинские буквы, цифры, набор
основных знаков пунктуации и математических знаков.
Многобайтные кодировки (UTF-8, UTF-16, UTF-32, некоторые кодировки для азиатских
языков с иероглифами) сопоставляют одному символу несколько байтов, позволяют
хранить гораздо больший набор символов. В частности, кодировка UTF-8
представляет все символы стандарта UNICODE, в частности, буквы алфавитов
большинства языков (включая иероглифы, деванагари и прочее), разнообразные
знаки пунктуации и математические знаки, эмодзи и т.д. В UTF-8 латинские буквы,
цифры и основные знаки пунктуации кодируются 1 байтом, кириллические, греческие
буквы, иврит и некоторые другие алфавиты — 2 байтами, иероглифы и эмодзи —
3 или 4 байта.
Python поддерживает работу с символами стандарта UNICODE.
Работа с файлами в Python
Для того, чтобы взаимодействовать с файлом, его нужно сначала открыть —
создать объект файла, через который можно с ним взаимодействовать (читать
или писать).
После работы с файлом, его нужно обязательно закрыть — сообщить операционной
системе, что мы с ним больше не работаем.
Несколько программ могут одновременно открыть файл для чтения и читать его.
Но если одна из программ открыла файл для записи, то другие программы, как
правило, не могут этот файл открыть, как на чтение, так и на запись (детали
поведения зависят от операционной системы).
На Windows, в частности, если файл открыт в программе, то его нельзя удалить —
получим сообщение о том, что файл занят. Если открыт файл на флешке,
то Windows не даст эту флешку безопасно извлечь.
Максимально число одновременно открытых файлов ограничено, количество зависит
от настроек операционной системы (как-то я делал опыт, на Windows не удалось
открыть более 4000 файлов одновременно).
Запись в файлы буферизуется — данные, которые записываются в файл, сначала
записываются в область оперативной памяти, т.н. «буфер», когда «буфер»
заполняется, выполняется реальна запись на диск. Буфер сбрасывается при закрытии
файла.
По этим причинам файл нужно не забывать закрывать.
Открытие файлов
Файл открывается при помощи встроенной функции open(). Синтаксис:
open(‹путь-к-файлу›, ‹режим›, encoding=‹кодировка›)
- Первый параметр (обязательный) — путь к файлу (строка).
- Второй параметр — режим. Режим может быть
r(read) — чтение текстового файла,w(write) — перезапись текстового файла,a(append) — дозапись в конец текстового файла,rb,wb,ab(binary) — чтение, перезапись или дозапись двоичного
файла.- Если режим файла не указан, то подразумевается
r.
- Параметр
encodingзадаёт кодировку файла, используется для текстовых
файлов. Рекомендуется всегда указывать кодировку'utf-8'. Кодировку
можно не указывать, если в файл пишутся только символы с кодами меньше
127 (латинские буквы, цифры, основные знаки препинания)
Примеры:
# Текстовый файл открыт для чтения
f1 = open('file1.txt', 'r', encoding='utf-8')
# Тоже самое, режим 'r' подразумевается
f2 = open('file1.txt', encoding='utf-8')
# Здесь будет использована кодировка по умолчанию
# (1251 на Windows, UTF-8 на Linux и macOS)
f3 = open('file1.txt')
# Текстовый файл открыт для перезаписи
f4 = open('file2.txt', 'w', encoding='utf-8')
# Текстовый файл открыт для дозаписи в конец
f5 = open('file3.txt', 'a', encoding='utf-8')
# Двоичный файл открыт для чтения
f6 = open('picture.jpg', 'rb')
# Закрытие всех файлов
f1.close()
f2.close()
f3.close()
f4.close()
f5.close()
f6.close()
Файл закрывается при помощи вызова метода .close(). Python может сам
закрывать файлы, но не всегда, поэтому .close() лучше вызывать явно.
Чтение файлов
Мы будем рассматривать только чтение текстовых файлов.
Для чтения файлов используются следующие методы:
.read(count)— прочитатьcountсимволов из файла,.read()— прочитать всё содержимое файла от текущей позиции до конца,.readline()— прочитать очередную строку из файла,.readlines()— прочитать все строки из файла, функция возвращает
список строк.
Строки, которые возвращают .readline() и .readlines(), завершаются
символом перевода строки \n. Для того, чтобы эти символы стереть,
можно использовать вызов метода .rstrip('\n'), либо вообще .strip(),
если нас не интересуют ни начальные, ни конечные пробельные символы.
Сам объект текстового файла, открытого на чтение, является итератором,
который при переборе читает очередную строку (неявно вызывая .readline()).
Пример функции, которая распечатывает содержимое файла с заданным именем.
def print_file(filename):
fin = open(filename, 'r', encoding='utf-8')
for line in fin:
print(line.rstrip('\n'))
fin.close()
Здесь мы не пользуемся методами для чтения файла, мы пользуемся тем, что
объект файла является итератором — может быть прочитан в цикле for. Цикл
будет перебирать все строки файла, как если бы они были прочитаны при помощи
метода .readline().
В конце прочитанных строк будет находиться знак перевода строки (\n), кроме,
возможно, последней — она на знак \n может не завершаться. Поэтому для
удаления \n на конце мы используем метод .rstrip('\n'), указывая в качестве
параметра удаляемый символ.
Альтернативное решение — вручную проверять, что если строка заканчивается
на \n, то мы её обрезаем, иначе не трогаем. Обрезать последний символ без
проверки нельзя, т.к. в последней строке знак перевода может отсутствовать.
Если знаки \n не обрезать, то на печать будут выводиться лишние пустые
строки, т.к. print() при печати строки сам добавляет в конец знак \n.
Другой, более короткий способ прочитать всё содержимое файла — использовать
метод .read():
def print_file(filename):
fin = open(filename, 'r', encoding='utf-8')
print(fin.read())
fin.close()
Мы при помощи метода .read() читаем весь файл до конца, всё содержимое
файла загружается в оперативную память в виде одной большой строки — эту
строку распечатываем при помощи print(). Такое решение нормально работает
только с небольшими файлами — файлами, размер которых измеряется не более
чем мегабайтами. Для гигабайтных файлов так делать не надо — памяти может
не хватить.
Запись файлов
Для того, чтобы записывать в файл, его нужно открыть в соответствующем режиме.
Режимов записи два:
- Режим перезаписи файла —
'w'(от слова write). Если файл не существовал,
то он при открытии будет создан и его размер будет равен нулю. Если файл
уже существовал, то он будет перезаписан — всё его содержимое сотрётся,
его размер станет равным 0 байт. - Режим дозаписи в файл —
'a'(от слова append). Если файл не существовал,
он будет создан и будет иметь размер 0 байт. Если файл уже существовал,
то данные в нём останутся как есть, новая запись будет осуществляться
в конец файла.
Пример:
>>> f = open('example.txt', 'w', encoding='utf8')
>>> f.write('Один!')
5
>>> f.close()
>>> f = open('example.txt', 'w', encoding='utf8')
>>> f.write('Два!')
4
>>> f.close()
После этих манипуляций в файле example.txt будет записано только слово
т.к. мы открывали файл для перезаписи. После первого «сеанса» работы с файлом
(вызовы open(), .write() и .close()) в файле оказалось слово Один!.
Когда мы открыли файл второй раз для перезаписи (в режиме 'w'), всё
содержимое файла стёрлось, и вместо него мы записали слово Два!.
Если мы файл удалим, а затем сделаем тоже самое, используя уже режим 'a'
(дозапись):
>>> f = open('example.txt', 'a', encoding='utf8')
>>> f.write('Один!')
5
>>> f.close()
>>> f = open('example.txt', 'a', encoding='utf8')
>>> f.write('Два!')
4
>>> f.close()
то в файле example.txt у нас окажется
При первом открытии (несуществующего) файла файл будет создан пустым и в него
будет записано Один!. При втором открытии содержимое файла не изменится,
новая запись будет осуществляться в конец — в конце допишется Два!.
Метод .write()
Метод .write(s) принимает в качестве параметра строку и записывает её в файл.
Возвращаемое значение — количество записанных символов. Обычно возвращаемое
значение совпадает с длиной строки. Редко, если на диске закончилось доступное
место, строка может записана не полностью и возвращаемое значение будет меньше
длины. Обычно об этом не задумываются и возвращаемое значение не анализируют.
Нужно помнить, что метод .write() при записи строки в файл не добавляет
в конце знак перевода строки '\n'. Если формировать файл при помощи метода
.write(), переводы строк нужно добавлять явно.
Функция print()
С функцией print() мы уже знакомы, мы ею пользовались для вывода данных
на консоль. Однако, её можно использовать и для записи в файл.
Расширенный синтаксис функции print():
print(‹значение›, ‹значение›,… sep=‹разделитель полей›,
end=‹разделитель строк›, file=‹объект файла›)
Функция print(), помимо списка значений для записи, принимает несколько
необязательных параметров. Это параметры:
sep=‹разделитель полей›— строка, которая разделяет выводимые значения,
по умолчанию — один пробел' '.end=‹разделитель строк›— строка, которая завершает вывод строки,
по умолчанию — перевод строки'\n'.file=‹объект файла›— открытый для записи (дозаписи или перезаписи)
файл или устройство вывода, по умолчанию — вывод на консольsys.stdout.
Таким образом, функция print() выводит значения, разделяя их пробелом,
в конце выводимой строки печатает перевод строки, вывод осуществляется в консоль
(т.е. на экран). Однако, это поведение можно изменить, указывая необязательные
параметры.
Вывод по умолчанию:
Сделаем разделителем другие строки:
>>> print(1, 2, 3, sep=';')
1;2;3
>>> print(1, 2, 3, sep='*****')
1*****2*****3
Выведем две строки, используя в качестве разделителя '!' — перевод на новую
строку осуществлён не будет:
>>> print(1, 2, 3); print(4, 5, 6)
1 2 3
4 5 6
>>> print(1, 2, 3, end='!'); print(4, 5, 6, end='!')
1 2 3!4 5 6!
В качестве разделителя полей укажем вместо пробела по умолчанию перевод строки —
каждое значение будет распечатано на новой строчке.
>>> print(1, 2, 3, sep='\n')
1
2
3
Пример записи в файл при помощи функции print():
>>> f = open('example.txt', 'w', encoding='utf8')
>>> print('Пишем в файл при помощи print()', file=f)
>>> print(1, 2, 3, 4, 5, 6, file=f)
>>> print(1, 2, 3, 4, 5, 6, sep=',', file=f)
>>> f.close()
Функция print() сначала напечатала строчку, потом шесть чисел, разделяя их
пробелами (по умолчанию), затем шесть чисел, разделяя их запятой (явно указан
sep=','). Содержимое файла:
Пишем в файл при помощи print()
1 2 3 4 5 6
1,2,3,4,5,6
Использование функции print() для вывода в файл удобнее, чем метод .write().
Важность закрытия файлов, оператор with
Для примера создадим файл, запишем в него строку и пока не закроем:
>>> f = open('example.txt', 'w', encoding='utf8')
>>> print('Пишем строчку в файл', file=f)
Файл в папке появился, но он почему-то пустой. Размер отображается как 0 байт,
если откроем блокнотом, то увидим пустой файл.
Причина в том, что данные при записи буферизуются — для открытого файла
в оперативной памяти выделяется участок, куда помещаются данные, подлежащие
записи. Когда эта область памяти заполняется, происходит запись данных на диск.
Буфер необходим для ускорения работы программы, т.к. операция записи на диск
гораздо более медленная, чем запись в оперативную память и имеет смысл её
выполнять реже.
Если мы файл закроем, то содержимое буфера принудительно выпишется на диск.
После закрытия файла его размер уже не нулевой, и если откроем его блокнотом,
то увидим ожидаемое содержимое — строку
Таким образом, файлы, открытые для записи, нужно обязательно закрывать, т.к.
иначе есть риск потери данных. Если, например, интерпретатор Python’а будет
аварийно завершён, данные в буфере на диск не выпишутся.
Другая причина закрытия файлов — на Windows открытые файлы блокируются
операционной системой. Если мы откроем файл и попробуем его удалить:
>>> f = open('example.txt', 'r', encoding='utf8')
то удалить его не удастся:
Если файл закрыть, то он успешно удалится:
На Linux и macOS удалять открытые файлы можно.
Третья причина — нельзя одновременно открыть очень много файлов. Для примера
напишем функцию:
def many_open_files(count):
files = []
for i in range(count):
files.append(open('file' + str(i) + '.txt', 'w'))
return files
Эта функция открывает для записи указанное количество файлов, имена файлов
при этом имеют вид file0.txt, file1.txt, …, функция возвращает список
открытых файлов.
При попытке открыть 10 тысяч файлов получим следующее сообщение об ошибке:
>>> fs = many_open_files(10000)
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
fs = many_open_files(10000)
File "D:/…/example.py", line 4, in many_open_files
OSError: [Errno 24] Too many open files: 'file8189.txt'
Таким образом, в Windows по умолчанию нельзя открыть более 8188 файлов
одновременно.
Python часто «прощает» незакрытые файлы — он их может закрыть сам. Но лучше
на это не полагаться. Не забывать каждый раз вызывать метод .close()
довольно утомительно, поэтому разработчики Python предусмотрели синтаксис,
гарантирующий обязательное закрытие файла:
with open(‹параметры›) as ‹перем›:
‹блок кода›
Здесь ‹параметры› — параметры функции open(): имя файла, режим, кодировка,
‹перем› — имя переменной, которой будет присвоен отрытый файл, ‹блок кода› —
некоторый блок кода. Семантика этой конструкции следующая.
- Открывается файл,
- объект файла присваивается переменной
‹перем›, - выполняется
‹блок кода›. - При завершении выполнения блока кода, причём любым способом — при достижении
его конца, либо завершение функции при помощиreturn, либо, если конструкция
withнаходится внутри цикла, то при прерывании итерации при помощиbreak
илиcontinue, либо при генерации исключения (возникновении ошибки) файл,
присвоенный переменной‹перем›обязательно автоматически закроется.
Пример. Подсчитаем число строк в файле.
def count_lines(filename):
with open(filename, 'r', encoding='utf8') as f:
return len(f.readlines())
Функция count_lines(filename) принимает имя файла и возвращает число строк
в нём. Для открытия файла используется конструкция with, которая присваивает
открытый функцией open() файл в переменную f. Чтобы подсчитать строки
в файле, мы считываем все строки при помощи метода .readlines()
и возвращаем длину полученного списка. Заметим, что блок кода, управляемый
with, завершается при помощи return. Однако, файл будет закрыт, т.к. with
это гарантирует.
