Как указать путь к файлу в c
Перейти к содержимому

Как указать путь к файлу в c

  • автор:

Как указать путь к файлу в c

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

Как указать путь к файлу в c

Подобно паре Directory/DirectoryInfo для работы с файлами предназначена пара классов File и FileInfo . С их помощью мы можем создавать, удалять, перемещать файлы, получать их свойства и многое другое.

FileInfo

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

CopyTo(path) : копирует файл в новое место по указанному пути path

Create() : создает файл

Delete() : удаляет файл

MoveTo(destFileName) : перемещает файл в новое место

Свойство Directory : получает родительский каталог в виде объекта DirectoryInfo

Свойство DirectoryName : получает полный путь к родительскому каталогу

Свойство Exists : указывает, существует ли файл

Свойство Length : получает размер файла

Свойство Extension : получает расширение файла

Свойство Name : получает имя файла

Свойство FullName : получает полное имя файла

Для создания объекта FileInfo применяется конструктор, который получает в качестве параметра путь к файлу:

Класс File реализует похожую функциональность с помощью статических методов:

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

Create() : создает файл

Delete() : удаляет файл

Move : перемещает файл в новое место

Exists(file) : определяет, существует ли файл

Пути к файлам

Для работы с файлами можно применять как абсолютные, так и относительные пути:

Получение информации о файле

Удаление файла

Перемещение файла

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

Копирование файла

Метод CopyTo класса FileInfo принимает два параметра: путь, по которому файл будет копироваться, и булевое значение, которое указывает, надо ли при копировании перезаписывать файл (если true , как в случае выше, файл при копировании перезаписывается). Если же в качестве последнего параметра передать значение false , то если такой файл уже существует, приложение выдаст ошибку.

Метод Copy класса File принимает три параметра: путь к исходному файлу, путь, по которому файл будет копироваться, и булевое значение, указывающее, будет ли файл перезаписываться.

Чтение и запись файлов

В дополнение к вышерассмотренным методам класс File также предоставляет ряд методов для чтения-записи текстовых и бинарных файлов:

AppendAllLines(String, IEnumerable<String>) / AppendAllLinesAsync(String, IEnumerable<String>, CancellationToken)

добавляют в файл набор строк. Если файл не существует, то он создается

AppendAllText(String, String) / AppendAllTextAsync(String, String, CancellationToken)

добавляют в файл строку. Если файл не существует, то он создается

byte[] ReadAllBytes (string path) / Task<byte[]> ReadAllBytesAsync (string path, CancellationToken cancellationToken)

считывают содержимое бинарного файла в массив байтов

string[] ReadAllLines (string path) / Task<string[]> ReadAllLinesAsync (string path, CancellationToken cancellationToken)

считывают содержимое текстового файла в массив строк

string ReadAllText (string path) / Task<string> ReadAllTextAsync (string path, CancellationToken cancellationToken)

считывают содержимое текстового файла в строку

IEnumerable<string> ReadLines (string path)

считывают содержимое текстового файла в коллекцию строк

void WriteAllBytes (string path, byte[] bytes) / Task WriteAllBytesAsync (string path, byte[] bytes, CancellationToken cancellationToken)

записывают массив байт в бинарный файл. Если файл не существует, он создается. Если существует, то перезаписывается

void WriteAllLines (string path, string[] contents) / Task WriteAllLinesAsync (string path, IEnumerable<string> contents, CancellationToken cancellationToken)

записывают массив строк в текстовый файл. Если файл не существует, он создается. Если существует, то перезаписывается

WriteAllText (string path, string? contents) / Task WriteAllTextAsync (string path, string? contents, CancellationToken cancellationToken)

записывают строку в текстовый файл. Если файл не существует, он создается. Если существует, то перезаписывается

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

Например, запишем и считаем обратно в строку текстовый файл:

Стоит отметить, что при добавлении текста я добавил в строку последовательность «\n», которая выполняет перевод на следующую строку. Благодаря этому добавляемый текст располагается в файле на новой строке.

Если мы хотим, что в файле изначально шло добавление на новую строку, то для записи стоит использовать метод WriteAllLines/ WriteAllLinesAsync , а для добавления — AppendAllLines / AppendAllLinesAsync

Аналогично при чтении файла если мы хотим каждую строку файла считать отдельно, то вместо ReadAllText / ReadAllTextAsync применяется ReadAllLines / ReadAllLinesAsync .

Пути к файлам

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

std::string filepath(«C:\\тест»);
std::ofstream file(filepath.c_str());

Если кратко, то использование не ASCII символов в строковых константах char может привести к печальным последствиям. Я уже обсуждал этот вопрос в посте о кодировках. В данном случае название файла напрямую зависит от кодировки исходника и если кто-то напишет подобное в utf-8, в windows-xp можно получить файл с запрещенными символами, с которым невозможно будет ничего сделать. Можно не использовать не ASCII. Но вы же не можете запретить это пользователю (потоку или БД из которого получен путь). Это же дискриминация по национальному признаку! Срочно исправляемся:

std::wstring filepath= L»C:\тест»
std::ofstream file(filepath.c_str());

Несведущие в стандарте пользователи Visual Studio могут успокоиться, пока нужда не заставит сменить компилятор (точнее STL). И тут начинается…

Дело в том, что текущий стандарт и не предполагает его наличия (не трогаем пока C++0x). Это в чистом виде энтузиазм мелкомягких.

Что же делать?

  • Использовать стороннюю библиотеку для работы с путями (к примеру boost::filesystem)
  • Использовать std::locale
  • Придумывать свой

С третьим вариантом все ясно, с первым тоже ничего сложного:

  1. #include <boost/filesystem/fstream.hpp>
  2. #include <string>
  3. namespace fs = boost :: filesystem ;
  4. int main ( int argc, char * argv )
  5. <
  6. std :: wstring filepath ( L «C:\тест» ) ;
  7. fs :: ofstream ( filepath ) ;
  8. return 0 ;
  9. >

А вот по поводу второго у тех, кто не учил матчасть, могут возникнуть проблемы.

Пользуемся std::locale

Отступление.
Огорчу пользователей mingw: вам придется использовать стороннюю реализацию STL (к примеру stlport) из-за отсутствия в родной правильной поддержки локализации. А точнее, функция std::locale(«») всегда возвращает std::locale(«C»), что бы там у вас не стояло. Тот же stlport лишен подобного недостатка. О том как слепить связку mingw+stlport+boost я отписал тут.

Все что нам нужно сделать это следовать простым правилам — с не ASCII работаем в «расширенном» виде. То есть, читаем путь в std::wstring, используя соответствующим образом локализованный поток, а при использовании, сужаем по пользовательской локализации. Эта идея основана на том, что раз пользователь правильно видит символы своего языка в консоли, то его пользовательская локализация знает в какую кодировку надо сузить широкую строку, чтобы правильно интерпретировать путь. Итак, пример. Допустим у нас есть файл в кодировке cp866, содержащий путь. Нам необходимо создать файл по этому пути. Что мы делаем:

  1. #include <iostream>
  2. #include <string>
  3. #include <fstream>
  4. #include <locale>
  5. #include <memory>
  6. #include «facet/codecvt/codecvt_cp866.hpp»
  7. /**@brief Сужает широкую строку, используя локализацию loc
  8. @return Возвращает суженную строку или пустую суженную строку, в
  9. случае. если возникла ошибка*/
  10. std :: string narrow ( const std :: wstring & wstr, const std :: locale & loc )
  11. <
  12. const size_t sz = wstr. length ( ) ;
  13. if ( sz == 0 )
  14. return std :: string ( ) ;
  15. mbstate_t state = 0 ;
  16. char * cnext ;
  17. const wchar_t * wnext ;
  18. const wchar_t * wcstr = wstr. c_str ( ) ;
  19. char * buffer = new char [ sz + 1 ] ;
  20. std :: uninitialized_fill ( buffer, buffer + sz + 1 , 0 ) ;
  21. typedef std :: codecvt < wchar_t , char , mbstate_t > cvt ;
  22. cvt :: result res ;
  23. res = std :: use_facet < cvt > ( loc ) . out ( state, wcstr, wcstr + sz, wnext,
  24. buffer, buffer + sz, cnext ) ;
  25. std :: string result ( buffer ) ;
  26. if ( res == cvt :: error )
  27. return std :: string ( ) ;
  28. return result ;
  29. >
  30. /**@brief Расширяет строку, используя локализацию loc
  31. @return Возвращает расширенную строку или пустую расширенную строку, в
  32. случае, если возникла ошибка.*/
  33. std :: wstring widen ( const std :: string & str, const std :: locale & loc )
  34. <
  35. const size_t sz = str. length ( ) ;
  36. if ( sz == 0 )
  37. return std :: wstring ( ) ;
  38. mbstate_t state = 0 ;
  39. const char * cnext ;
  40. wchar_t * wnext ;
  41. const char * cstr = str. c_str ( ) ;
  42. wchar_t * buffer = new wchar_t [ sz + 1 ] ;
  43. std :: uninitialized_fill ( buffer, buffer + sz + 1 , 0 ) ;
  44. typedef std :: codecvt < wchar_t , char , mbstate_t > cvt ;
  45. cvt :: result res ;
  46. res = std :: use_facet < cvt > ( loc ) . in ( state, cstr, cstr + sz, cnext,
  47. buffer, buffer + sz, wnext ) ;
  48. std :: wstring result ( buffer ) ;
  49. delete [ ] buffer ;
  50. if ( res == cvt :: error )
  51. return std :: wstring ( ) ;
  52. return result ;
  53. >
  54. int main ( int argc, char * argv [ ] )
  55. <
  56. //Пусть имеется cp866 файл с путем
  57. std :: ofstream ofile ( «input.txt» , std :: ios :: binary ) ;
  58. if ( ! ofile )
  59. <
  60. std :: cerr << «Error open file» << std :: endl ;
  61. return 0 ;
  62. >
  63. std :: ostreambuf_iterator < char > writer ( ofile ) ;
  64. * ( writer ) = 0xe2 ; // т
  65. * ( ++ writer ) = 0xa5 ; // е
  66. * ( ++ writer ) = 0xe1 ; // с
  67. * ( ++ writer ) = 0xe2 ; // т
  68. ofile. close ( ) ;
  69. //Читаем путь
  70. std :: locale cp866 ( std :: locale ( ) , new codecvt_cp866 ) ;
  71. std :: wifstream ifile ( «input.txt» , std :: ios :: binary ) ;
  72. ifile. imbue ( cp866 ) ;
  73. std :: wstring wpath ;
  74. ifile >> wpath ;
  75. ifile >> wpath ;
  76. ifile. close ( ) ;
  77. //Создаем по этому пути файл
  78. std :: ofstream file ( narrow ( wpath, std :: locale ( «» ) ) . c_str ( ) ) ;
  79. file << «testing» ;
  80. file . close ( ) ;
  81. >

Фасеты можно взять на git-hub.

SUMMARY

Если вы используете путь из argv — можете смело с ним работать (пользователь знает что делает). Из «внешней среды» путь получайте с помощью правильно локализованного потока как широкую строку и сужайте ее с помощью пользовательской локализации.

С вопросами можно обращаться:
0. К стандарту
1. К книге Страуструпа (3-е специальное издание, приложение)
2. К документации по mingw.
3. К документации по boost.
4. К посту о фасетах и кодировках.

Всем прямых путей!

UPD: Ну и как правильно заметили Gorthauer87,Migun и naryl в комментариях, обратные слеши и платформо-специфичные пути тоже плохая идея.

Как прописать путь к txt-файлу?

Как указать путь к файлу который находится в той же директории что и сам проект, то есть чтобы можно было этот файл и проект перекинуть в другое место и он все таки находил этот файл не меняя пути к нему? FileStream f = new FileStream(@»C:\Users\User\Desktop\TaskOne\input.txt», FileMode.Open, FileAccess.Read);

Для это вам нужно оперировать двумя параметрами проекта Visual Studio:

  • Working directory
  • Output path

Возьмём для примера проект консольного приложения.

введите сюда описание изображения

Параметр Working directory определяет, какая рабочая директория будет задана по умолчанию текущему процессу консольного приложения.
Если этот параметр пустой, то рабочей директория будет директория из которой запущен exe файл. А его место создание определяется параметром проекта Output path :

введите сюда описание изображения

По умолчанию это поддиректорий текущего проекта bin\Debug.

Таким образом, в данном случае, чтобы обратиться к файлу input.txt находящемуся в директории проекта нужно обратиться на два директория выше, т.е. использовать «..» в пути файла:

Так как пути все указываются относительно директории проекта, то при переносе папки проекта изменять пути не надо будет.

Директорией проекта является директория, в которой лежит файл проекта .csproj .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *