Работа с текстовым файлом в C++ для начинающих

Работа с текстовыми файлами в C++ вызывала у меня, как и у многих моих читателей вызывает, много вопросов. В C++ с текстовыми файлами работают в основном в двух стилях: в C++ стиле и в C стиле. Так уж вышло, что язык C++ является надмножеством языка C и отчего многое поддерживает из C.

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

Поскольку этот сайт о C++, то описываться будет преимущественно C++ стиль. Параллельно будет показываться С стиль. Конечная программа в C стиле в листинге #10.4.

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

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

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

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

По умолчанию файл сохраняется туда, где находится файл выполняемой программы (экзешник, бинарник). Где именно он находится, может зависеть от того, как и где создавался проект программы. Здесь придется полазить, поискать, понять, куда сохраняются файлы. В моём случае файл сохранился туда, где сохранён исходный код программы из листинга #2.1. Так же путь может быть прописан в настройках вашей IDE.

В С стиле всё выглядит приблизительно также, только используются не файловые объекты, а указатели, привязываемые к файлам:

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

В листинге #3.1 ifstream и ofstream — это типы, эти типы — это классы, описанные в заголовочном файле fstream. У любых классов в C++ есть так называемые конструкторы классов. И такое указание значений в скобках при объявлении переменных чаще всего использование конструкторов.

В C стиле в этом случае использовалась обычная инициализация.

Для чтения файлов будем использовать переменную f2. В С стиле Это сейчас не обязательно, но мне это нужно для чёткой параллели, для отражения принципа.

  • В подавляющем числе случаев ошибки новичков с чтением файла связаны с неправильным указанием пути или имени файла.
Будьте очень внимательны к имени файла и к полному пути до него. Поскольку пока что файлы были созданы без явного указания пути, то и задействуем мы сейчас только имена файлов. В C++ стиле способ доступа к файлам определяется с помощью типов, а в C стиле с помощью специальных модификаторов. Запомнить модификаторы чтения и записи не сложно: "w" — означает write, т. е. запись, а "r" — означает read, т. е чтение. В С++ стиле тоже запись и чтение можно определить по одной букве: i означает input, т. е. устройство ввода (для нас сейчас клавиатура), а o означает output, т. е. устройство вывода (для нас сейчас экран)

  • Считывая что-то из файла, нужно это что-то запоминать в переменную. После записи значения в переменную со значением можно делать что угодно.
  • Сначала нужно читать файл, чем определять, существует ли он, иначе можно стереть данные файла.
ofstream в C++ по умолчанию перезаписывает файл, поэтому можно столкнуться с проблемой, что файл вроде как есть, но как будто данные с него не читаются, а не читаться они могут, потому что данные файла оказались стёрты:

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

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

В том случае, если файл создаётся не самой программой, обычно в случае ошибки открытия файла просто выводят сообщение о том, что файл не существут/не найден.

Такая проверка в первое время может вас неоднократно выручить.

Теперь можно вернуться к тому, что выводится текст из файла не полностью. Мы записывали Hello, world, а видим только Hello. Связано это с операцией >>: она читает до первого пробельного символа. Это значит, что из файла можно прочитать текст словесными порциями. Под словесными порциями сейчас подразумеваются слова, которые могут оказаться с прикленными к ним знаками препинания (поскольку слово от знака препинания пробелом не отделяется, оно вместе со знаком препинания в некотором смысле тоже слово, но чтобы не путаться, я назвал такую сущность словесной порцией).

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

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

  • Для чтения разделённых пробелами слов или чисел из файла можно использовать операцию >>
  • Для чтения непосредственно строк из файла нужно использовать функции чтения строк. Таких функций несколько. Одна — это функция getline объекта, а другая — это самостоятельная функция getline. Они имеют одинаковые названия, но работают разными способами.
Чтобы использовать функцию из объекта, нужно написать её через точку после объекта, а ей самой указать строку, в которую записывается значение, и указать число записываемых символов:


Для того, чтобы работать с самостоятельной функцией getline, нужно использовать не массив или указатель как строку, а string (или любой подтип basic_string)

  • Самостоятельная функция getline принимает в круглых скобках связывающую программу и файл переменную и строку, тип которой вхожит в множество подтипов basic_string (в начале подойдёт просто string).
  • Функция из объекта принимает в круглых скобках или массив символов, или указатель на символьную строку и число символов в строке.
Если вы пишете в современных компиляторах, то предпочтение отдавайте самостоятельной getline и string.

Вообще, работа с файлами в C++ стиле очень напоминает работу с объектами cin и cout.


И cin, и cout, и f1, и f2 в этих кодах — это потоковые объекты. То, что они потоковые, можно понять по названию заголовочных файлов, в которых содержится слово stream. Потоки могут быть разные, в нашем случае задействованы файловый поток и потоки ввода-вывода (их можно распознавать по первым символам перед stream в заголовочных файлах). А работают они приблизительно одинаково, потому что язык C++ так и проектировался, чтобы была одинаковая форма для операций с потоками.

В С стиле прочитать строку файла можно так (чтение строки без пробелов):

Как и в случае с C++ нас подстерегает опасность затирания файла. Этого затирания необходимо избегать. Понять, открылся файл или нет, можно с помощью проверки указательной переменной, связываемой с файлом, на NULL. Указательные переменные — это те переменные, которые объявляются со звёздочками. В нашем случае связанная с файлом переменная — это f2 (поскольку мы ей говорили путь для открытия, то она получилась связанной с файлом, который мы пытаемся использовать). Поскольку этот сайт сосредоточен на C++, форматирование языка C при прочтении данных будет опущено, иначе придётся много расписывать лишнего. Суть здесь, что чтение происходит почти также, как считывание символов с клавиатуры, только имя функции не scanf, a fscanf и первым параметром принимает указатель, связанный с файлом.
  • Скорее всего, вы сейчас имеете пустой файл, поэтому текст на эран может не выводится и текста об ошибке не происходить. В таком случае файл лучше перезаписать, а потом прочитать. Вы можете сделать это программно, а можете найти вручную, где находится файл, и внести в него данные.

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

Чтобы создать пустой файл (чтобы вернуться в ход темы), чтобы наверняка, можно поменять в листинге #10.1 строку с объявлением f2 на

И запустить программу. Файл должен будет быть перезаписан. Т. е. должен появиться пустой файл. Потом верните модификатор чтения на место: вместо w напишите r. И изучайте код ниже.

Теперь если у вас был пустой файл, то первый запуск листинга #10.2 создаст новый файл, запишет в созданный файл две строчки. А во время второго запуска программа определит, что файл существует и он не пустой, и прочитает одну строку, значение которой мы запишем S. Два раза пришлось запускать программу, потому что у нас так определена ветка if…else. Если else не писать, то всё, что нужно, произойдёт при первом запуске: т. е. файл сначала в любом будет создан или обновлён, а потом уже независим от того, был ли он вообще, или был он пустым, выполнится чтение.
Лучше не писать много раз имя или путь к файлу, а отвести такому значению свою переменную.

Обратите внимание, что закрытие связи f1 было перенесено. Если файл не открыт или окажется закрытым, то закрытие неоткрытого файла может повалить программу. И лучше проверять, что файл открыт: добавились проверки и перед проверкой на пустой файл и в других местах. Это может помочь хотя бы в то время, когда программа будет переписываться-улучшаться. Да. И как видите, чтобы записывать в файл строку, нужно разграничивать записываемые строки символом конца строки, иначе много строк будут записаны как одна строка, а это не то, что мы обычно делаем.

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

Обратите своё внимание, что мы записывали строки в файл, но читаем сейчас словесные порции одной строки. Это связано с тем, что scanf бьёт данные по разделителю, которым считает пробел. Это как с операцией >> в стиле C++. Чтобы прочитать строки, можно использовать fgets.

Подводим промежуточные итоги:

  • Работать с файлами в C++ можно в С стиле и в стиле С++. Пока вы пишете на C++, предпочтение лучше отдавать C++ стилю.
  • Файл может оказываться в трёх состояниях: «существует и пустой, существует и не пустой, не существует» — каждое из этих состояний возможно нужно проверить.
  • Сначала лучше открывать файл для чтения, проверять, что всё ОК, и только потом записывать данные в файл.
  • Для работы со строками, содержащими пробельные символы, как в C++, так и в C нужно использовать специальные функции, которые умеют считывать словесные порции не до первого встреченного пробельного символа, а до признака конца строки.
  • Одна из характерных проблем новичков — не делать проверки на успешное открытие файла. В связи с чем очень распространена ошибка — указаниt пути к файлу или имени файла.
  • По умолчанию, т. е. без указания полного пути, а с указанием только имени файла, файлы могут сохраняться в ту папку, где оказывается исполняемый файл. Путь сохранения файла можно указывать явно.

Если путь указывается явно, то в случае несуществования какой-то части папок в пути, файл создан не будет:

У меня есть диск A:/, но на нём нет папки cpp, поэтому попытка записи файла оказывается неудачной.

  • При указании полного пути нужно или чтобы путь уже существовал, или создать необходимые папки на пути для расположения сохраняемого файла в конечной папке.
Создавать путь можно как вручную, так и программно. Мы обойдёмся созданием пути вручную. Определитесь с диском (у мало кого из вас может оказаться логический диск A:\). Я же использую свой существующий. На своём диске я создам папку cpp. После этого запущу программу #11.1 и файл будет создан в той папке. Поскольку данные я не записывал, файл будет создан пустым.

17 комментариев на «“Работа с текстовым файлом в C++ для начинающих”»

  1. Edward:

    Спасибо огромное за урок!!Ваш урок очень сильно мне помог)

    4
    9
  2. Vasia:

    Говно это все.
    Херня, а не урок.

    13
    6
  3. Аноним:

    Прошу прощения, уважаемый, но использование «using namespace std;» не разумно и бывает опасно в проектах. А ещё это говорит о не самом лучшем вкусе программиста.

  4. Alexsandr:

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

    Ошибки:
    [C++ Error] UnitText.cpp(24): E2238 Multiple declaration for 'in'
    [C++ Error] UnitText.cpp(19): E2344 Earlier declaration of 'in'

    Автор сайта отвечает:
    Здесь только один файл в коде обрабатывается.
    В глаза бросается открыли как out, а закрыли как in
    ofstream out —> out.close() , а не in.close()

    как буду дома, допишу примером как с двумя файлами, пока возможности нет

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

    Автор сайта отвечает:
    Visual Studio под рукой нет, поэтому немного в старом стиле.
    в Visual Studio лучше использовать string

    Мой пример больше подойдет к этой моей статье.

    Спасибо большое! Полезная информация для работы с вашими самостоятельными заданиями.

  5. Сергей:

    😎
    Самый толковый урок, находил другие, где код в 30 строк и с применением 100500 других библиотек, хотя мне нужно было лишь вывести данные. Огромное спасибо!

    1
    1
  6. Dima:

    Большое спасибо за урок!

    Получилось скомпилить код в линуксе с небольшими изменениями. Если кому интересно то такой код работает в линуксе

     

    #include <fstream>
    #include <iostream>
    using namespace std;
    int main()
    {
    ofstream out(«/home/dima/Develop/lp/tmp/config_test.ini»); //ofstream — это тип данных. Внутри скобок параметр, принимаемый конструктором объявленного объекта    out<<«HelloWorld<<«\n»; //Записываем первую строчку
    out<<«UraRabotaet!!!»; //Записываем вторую строчку
    out.close(); //Закрываем файл
    return 0;
    }

  7. Дмитрий:

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

    Автор сайта отвечает:
    В Borland builder c++ скорее всего нужны небольшие поправки.

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

    Автор сайта отвечает
    Конкретно сейчас подсказать не могу. Я не дома и c++ под рукой нет. Могу утром (по Москве). Только какие переменные должны считываться и как они хранятся внутри файла?. В зависимости от этого ответ на вопрос может быть различен.

    Автор сайта отвечает:
    Наверное, вы уже нашли ответ и вам это теперь не нужно. Но может кому-то нужно. Так как я не дождался ответа на вопрос уточнений, то вот пример считывания double из файла с сохранением их в массив.

    Очень стандартный способ хранить сначала число переменных и потом сами переменные. От этого и оттолкнемся.

    Тут, конечно не вес ь проект, но основное все показал.
    Внутри файла только числа , первое количество чисел, второе числа а’ля переменные
    5
    12.333   6.89   -789
    12.33   43.567

  8. Сергей:

    Подскажите, к кому обратиться за помощью в написании программки на с++, краткий смысл — чтение числа из текстового файла, вывод его на лпт-порт, чтение числа с порта, запись в другой текстовый файл, подробнее в личке. Обращение к ЛПТ на с++ нашёл, а с чтением-записью не получается. Учить язык с++ времени нет, основная программа будет на другом языке, который с ЛПТ не дружит и считывать будет из текстовых файлов.

  9. Данила:

    Скажите пожалуйста, как в текстовый файл в определённую строку ввести текст (у меня 57 строк, а мне нужно в 26 или в какую-нибудь другую).

    • Допиливать надо. мне лень. Можно делать вообще по другому. Но так как условий мало, то берите, что даю.

  10. Тима:

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

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

    Заранее спасибо,

    • Простое форматирование.

      Какой текст и в каких местах его разбивать на колонки мне откуда знать?

  11. Данил:

    Такой вопрос, а как сделать так, чтобы мы сами в переменую задали путь к файлу в который будем вносить данные?

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

  12. Олег:

    отличный материал для новичков

  13. Сергей:

    Отличная статья

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

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

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/pol-deytel/android-dlya-programmistov-sozdaem-prilozheniya-4840674/?lfrom=15589587

Последние комментарии

Яндекс.Метрика