File I/O in C++ works very similarly to normal I/O (with a few minor added complexities). There are 3 basic file I/O classes in C++: ifstream (derived from istream), ofstream (derived from ostream), and fstream (derived from iostream). These classes do file input, output, and input/output respectively. To use the file I/O classes, you will need to include the fstream header.
Unlike the cout, cin, cerr, and clog streams, which are already ready for use, file streams have to be explicitly set up by the programmer. However, this is extremely simple: to open a file for reading and/or writing, simply instantiate an object of the appropriate file I/O class, with the name of the file as a parameter. Then use the insertion (<<) or extraction (>>) operator to write to or read data from the file. Once you are done, there are several ways to close a file: explicitly call the close() function, or just let the file I/O variable go out of scope (the file I/O class destructor will close the file for you).
File output
To do file output in the following example, we’re going to use the ofstream class. This is extremely straighforward:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <fstream> #include <iostream> int main() { // ofstream is used for writing files // We'll make a file called Sample.dat std::ofstream outf{ "Sample.dat" }; // If we couldn't open the output file stream for writing if (!outf) { // Print an error and exit std::cerr << "Uh oh, Sample.dat could not be opened for writing!" << std::endl; return 1; } // We'll write two lines into this file outf << "This is line 1" << '\n'; outf << "This is line 2" << '\n'; return 0; // When outf goes out of scope, the ofstream // destructor will close the file } |
If you look in your project directory, you should see a file called Sample.dat. If you open it with a text editor, you will see that it indeed contains two lines we wrote to the file.
Note that it is also possible to use the put() function to write a single character to the file.
File input
Now, we’ll take the file we wrote in the last example and read it back in from disk. Note that ifstream returns a 0 if we’ve reached the end of the file (EOF). We’ll use this fact to determine how much to read.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <fstream> #include <iostream> #include <string> int main() { // ifstream is used for reading files // We'll read from a file called Sample.dat std::ifstream inf{ "Sample.dat" }; // If we couldn't open the output file stream for reading if (!inf) { // Print an error and exit std::cerr << "Uh oh, Sample.dat could not be opened for reading!" << std::endl; return 1; } // While there's still stuff left to read while (inf) { // read stuff from the file into a string and print it std::string strInput; inf >> strInput; std::cout << strInput << '\n'; } return 0; // When inf goes out of scope, the ifstream // destructor will close the file } |
This produces the result:
This is line 1 This is line 2
Hmmm, that wasn’t quite what we wanted. Remember that the extraction operator breaks on whitespace. In order to read in entire lines, we’ll have to use the getline() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <fstream> #include <iostream> #include <string> int main() { // ifstream is used for reading files // We'll read from a file called Sample.dat std::ifstream inf{ "Sample.dat" }; // If we couldn't open the input file stream for reading if (!inf) { // Print an error and exit std::cerr << "Uh oh, Sample.dat could not be opened for reading!\n"; return 1; } // While there's still stuff left to read while (inf) { // read stuff from the file into a string and print it std::string strInput; std::getline(inf, strInput); std::cout << strInput << '\n'; } return 0; // When inf goes out of scope, the ifstream // destructor will close the file } |
This produces the result:
This is line 1 This is line 2
Buffered output
Output in C++ may be buffered. This means that anything that is output to a file stream may not be written to disk immediately. Instead, several output operations may be batched and handled together. This is done primarily for performance reasons. When a buffer is written to disk, this is called flushing the buffer. One way to cause the buffer to be flushed is to close the file -- the contents of the buffer will be flushed to disk, and then the file will be closed.
Buffering is usually not a problem, but in certain circumstance it can cause complications for the unwary. The main culprit in this case is when there is data in the buffer, and then program terminates immediately (either by crashing, or by calling exit()). In these cases, the destructors for the file stream classes are not executed, which means the files are never closed, which means the buffers are never flushed. In this case, the data in the buffer is not written to disk, and is lost forever. This is why it is always a good idea to explicitly close any open files before calling exit().
It is possible to flush the buffer manually using the ostream::flush() function or sending std::flush to the output stream. Either of these methods can be useful to ensure the contents of the buffer are written to disk immediately, just in case the program crashes.
One interesting note is that std::endl; also flushes the output stream. Consequently, overuse of std::endl (causing unnecessary buffer flushes) can have performance impacts when doing buffered I/O where flushes are expensive (such as writing to a file). For this reason, performance conscious programmers will often use ‘\n’ instead of std::endl to insert a newline into the output stream, to avoid unnecessary flushing of the buffer.
File modes
What happens if we try to write to a file that already exists? Running the output example again shows that the original file is completely overwritten each time the program is run. What if, instead, we wanted to append some more data to the end of the file? It turns out that the file stream constructors take an optional second parameter that allows you to specify information about how the file should be opened. This parameter is called mode, and the valid flags that it accepts live in the Ios class.
Ios file mode | Meaning |
---|---|
app | Opens the file in append mode |
ate | Seeks to the end of the file before reading/writing |
binary | Opens the file in binary mode (instead of text mode) |
in | Opens the file in read mode (default for ifstream) |
out | Opens the file in write mode (default for ofstream) |
trunc | Erases the file if it already exists |
It is possible to specify multiple flags by bitwise ORing them together (using the | operator). Ifstream defaults to std::ios::in file mode. Ofstream defaults to std::ios::out file mode. And fstream defaults to std::ios::in | std::ios::out file mode, meaning you can both read and write by default.
Let’s write a program that appends two more lines to the Sample.dat file we previously created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <fstream> int main() { // We'll pass the ios:app flag to tell the ofstream to append // rather than rewrite the file. We do not need to pass in std::ios::out // because ofstream defaults to std::ios::out std::ofstream outf{ "Sample.dat", std::ios::app }; // If we couldn't open the output file stream for writing if (!outf) { // Print an error and exit std::cerr << "Uh oh, Sample.dat could not be opened for writing!\n"; return 1; } outf << "This is line 3" << '\n'; outf << "This is line 4" << '\n'; return 0; // When outf goes out of scope, the ofstream // destructor will close the file } |
Now if we take a look at Sample.dat (using one of the above sample programs that prints its contents, or loading it in a text editor), we will see the following:
This is line 1 This is line 2 This is line 3 This is line 4
Explicitly opening files using open()
Just like it is possible to explicitly close a file stream using close(), it’s also possible to explicitly open a file stream using open(). open() works just like the file stream constructors -- it takes a file name and an optional file mode.
For example:
1 2 3 4 5 6 7 8 9 |
std::ofstream outf{ "Sample.dat" }; outf << "This is line 1" << '\n'; outf << "This is line 2" << '\n'; outf.close(); // explicitly close the file // Oops, we forgot something outf.open("Sample.dat", std::ios::app); outf << "This is line 3\n"; outf.close(); |
![]() |
![]() |
![]() |
Shouldn't reading cycle be written as
instead of
Once inf is eofed (but not failed) no point to read from it.
`std::getline` sets the stream to a failed bit when it tries to extract from an ended file, so `while (inf)` will stop when we're done reading the file.
Both versions have to read from the ended file in order to determine that it has ended, so both versions run the same number of times.
so I have a question for importing 2d array. lets say my file looks like this:
2
2
1 5
4 6
my problem is, if I use a for loop I cant make the output look like it is shown above.
Use an auto-formatter, your code is hardly readable.
There are 6 numbers in your file, you're only extracting 4.
"open() works just like the file stream constructors"
But can you use it instead of the constructors for the first time?
You still need to construct a file stream object, but you don't have to initialize it with a path. You can later use `open` to actually open a file.
in a text file with only '1111 0000' gets me the output of '1111 (new line) 0000 (new line) 0000 (new line)' why?
Let's start by cleaning up your code.
The problem is still the same.
Let's rubber ducky the code.
Line 12-14 are definitions, nothing went wrong here as otherwise you wouldn't get output at all.
Line 20: `file` is valid, loop is entered.
Line 30: Add a 0000
Line 33: Override the 0000 with 1111
Line 20: `file` is valid, loop is entered.
Line 30: Add a 0000
Line 33: Override the 0000 with 0000
Line 20: `file` is valid, loop is entered.
Line 30: Add a 0000
Line 33: There's no more data in the stream, extraction fails.
Line 20: `file` is invalid, stop looping.
We're adding a 0000 even though no more data is available. That 0000 remains in `binaries`.
To fix this, we can check if there's more data available before we enter the loop.
That works, but only if you know that the format of your data is valid. If there is a trailing line feed for example, you'll run into the same problem.
To work around that, you could extract into a temporary and before push that temporary into `binaries`, check if the extraction was successful.
Ok thank you very much. This saved a lot of time. But why is the while loop (in the original example) executing 3 times anyway? It reached the end of the file so why is it executing a 3rd time?
The loop only stops when the stream is in a failed state. An empty stream isn't a failed stream. The stream fails when you try to extract from it when it's already empty, but that's not the case until line 33 in the 3rd iteration.
Ah ok I see. Thank you.
if the object 'strInput' is defined inside the loop, how come you don't get an error for redefining it?
`strInput` dies after every iteration. When `strInput` is re-created, the old one doesn't exist anymore.
Oh right. Thank you.
I guess you made a typo here:
" Remember that the extraction [should be insertion?] operator deals with “formatted output”....".
No, I am talking about the extraction operator here, but the "formatted output" comment is definitely misleading. I've removed it.
Oh, I see...
Yeah that kinda made me think that you were talking about the opposite thing..
Hi Alex,
I am posting this here because it involves reading from files. Here is what I am trying to do:
For now I am just concerned with the first three steps in the process. This works as it is, because I defined
instead of a 3D one. But what I really need is a 3D
. But when I do that I get a compiling error. I think that the problem is the way I am defining the 3D vector. The size of the vector should be 3 x data[0].size() x file.size(). Any suggestion?
data[0] holds all channels from the first file (Babar_D+_D-.dat).
data[0][0] holds the first column from the first file.
data[0][0][0] holds the first row from the first column from the first file.
That's how I understand the task.
Hi nascardriver,
Thanks for your reply. Yes, you are correct. If I print to the screen the output of the code I get something like
Which is correct. But this goes into a 2D vector. What I want now is to put all of this data into a 3D vector, with each set of data from each file into the third index of the vector. And that is where I fail. If I define the vector as a 3D vector
I get an error. How do I iterate through the third index to put the data there?
I didn't test it, but it should get you the idea.
Thank you! I will try it.
Hi, here I am again with the same question.
I tried doing indexing things in the following way:
which compiles, but it does not print out what I expected. It prints all the elements in the two files and then the first few elements of the first file, again. I really cannot understand where I am failing.
Line 23: You're not clearing @data after a file has been read.
* Don't use "using namespace".
* Initialize your variables with brace initializers.
* Don't use @std::exit.
* Variable names starting in an underscore are reserved names.
* @ifs is a stream already, there's no need for a separate @istringstream.
Thanks again for your corrections.
How do I clear @data?
Declare it inside of the for-loop.
I am so stupid. I did not think of that. Thanks again.
Alex, thank you for this site. It has helped me immensely!
The problem I currently have is trying to write a 2D vector to a CSV file in a way that leaves out the trailing comma generated at the end of every line. The problem is illustrated by executing the following code:
Is there a simple and efficient way to correct this in C++11 without reopening the file and re-writing each line with
?
Yup. Don't write the trailing comma.
Instead, write a preceding comma if this is not the first object in the row.
Something like this:
Thanks, Alex. I would have never thought of using a bool to falsify after the first element of each row. It works perfectly.
Hi Alex,
Are we alright using ifstream and ofstream for writing and reading files in something like SQLite to enable complete dBase solutions?
By the way, Using the code snippets/examples above created the file Sample.dat but was unable to read it back in. Using both VS2017 and VS2019. No error but the only result I could get was:
"Uh oh, Sample.dat could not be opened for reading!"
It seems that ofstream and ifstream won't accept full paths as input so I merged the top samples into one program as routines and got it working thus:
And then to also append the file:
A minor correction in the second paragraph:
"Then use the insertion (<<) or extraction (>>) operator to read/write to the file."
To be consistent (with the order of read/write), this should be:
"Then use the insertion (<<) or extraction (>>) operator to write/read to/from the file."
Lesson updated. Thanks!
"to write to or read data from the the file."
just one 'the' should be enough. :P
Thanks! I wanted to make sure you got the the point. :P
Amazing how our brains naturally overlook redundancies like this.
I have some tiny HUGE problems with files. Would you consider my 3 file:
f.inp:
main.cpp:
It was supposed to be 123 in my f.out, but instead
There's one plethora 3 in my out file.
Would you please help me as soon as you can? I'm going into a competition in Friday.
I use DevC++ 4.9.9.2 (don't know if it's any help, but better to be safe than sorry)
I copy pasted your program into my compiler, but the result is still "1230", not "123".
Ok so after some thoughts i come up with this:
Though it solved me problem in the mean time, it would be a real weight when i encounter a more complex challenge.
So i want to ask if you know any reason why is there a superfluous int in my output file?
Read the comments in my code. I told you what the problem is and what you need to do.
Dear Teacher, please let me ask you: In first paragraph you write: "These classes do file input, output, and input/output respectively.". By "do file output" do you mean "do create file"? Regards.
Hi Georges!
File input/output usually refer to reading and writing. @std::ofstream can be used to create files.
For filesystem operations see @std::filesystem.
References
* std::filesystem - http://en.cppreference.com/w/cpp/experimental/fs
Mr. nascardriver, please accept my thanks for you answered my question.
I understand that in first program by
(instead of std::) and
file with name "Sample" and extension ".dat" is created. Is it okay? Regards.
Correct. Following the rules introduced in earlier lessons the code should look like this
File output means writing data to the file. Whether a new file is created or an existing file opened for writing is independent.
Dear Teacher, please let me say you that in first paragraph you write: "There are 3 basic file I/O classes in C++: ifstream (derived from istream), ofstream (derived from ostream), and fstream (derived from iostream). These classes do file input, output, and input/output respectively". It means, ofstream just does file output. Indeed in following program by comment
you imply that it creates (makes) file. Regards.
If the filename passed into ofstream doesn't exist, ofstream will create a new file. However, when we say "do file output", that's talking solely about writing to a file. Whether the file was just created or previously existed is independent.
Dear Teacher, please accept my many thanks for you replied, and many more for your instructive answer. Regards.
Alex,
In the lesson you have mentioned: " If you opt to use fstream (which can do both input and output), you explicitly have to pass in ios::in and/or ios::out depending on which mode you’d like to use."
Then why does the following code work?
Compiled using g++ (MinGW.org GCC-6.3.0-1) 6.3.0 on Windows 10
Even ios::app works with fstream without the ios::out flag
Why does this code work?
The reference source I used for this article was incorrect. fstream defaults to ios::in | ios::out, so you can both read and write by default if you don't specify a file mode. I've updated the article accordingly. Thanks for pointing out the error.
Does this look alright?
Anything that could be improved?
I don't see anything amiss from inspection.
Hello,
In the beginning of the article, the text says:
To use the file I/O classes, you will need to include the fstream.h header.
However, later, in examples, fstream is included, not fstream.h.
if I include <fstream.h>, with g++ (mingw) I get:
fatal error: fstream.h: No such file or directory
Without ".h", everything works. So, I think that fstream.h is wrong, and it should be replaced with fstream.
Correct, it should be fstream (no .h). I've updated the article. Thanks for the fix.
How do you delete a record in a file?
with code example please. Oh and that works with the above examples, Thanks.
Hello Alex,
This is the first time I try to use ifstream, read numbers from a file and sum them up, but the program always missing the first number. also the sum is not right. please take a look, thanks;
the file I use is call sourcenum.txt, and it output individually right except for the first number, I have the numbers : 18 19 18.5 13.5 14 16 19.5 20 200 12 18.5 17.5 in the file.
This is the perfect kind of thing to use a debugger for. Step through each line of code line by line and watch what it reads in from the file and what it does with those values.
Thanks Alex, figured it out, the cout should place before the sourcenum>>num, that is why the program keep skip printing the first element.
Also, it seems sourcenum.good() will return false if the last element is right after the eof, in windows notes editor, I fix this by adding a space after the last element so the last number is not follow immediately by eof, it all work fine now.
Hi Alex,
Great site! I'm really enjoying learning about C++. I thought I'd try to write a little example for myself where I open an existing file and append values to it. Since you said I could combine flags, I thought I'd also add std::ios::nocreate. Something like this:
This does not compile. The compiler complains saying "error: no member named 'nocreate' in 'std::__1::basic_ios<char>'." Googling this error, it seems like maybe nocreate is no longer part of the standard library. If you can confirm this, then I guess this page should be updated to reflect that. :)
Let me know what you think!
I think I made a mistake. std::ios::nocreate and std::ios::noreplace are not officially part of the C++ standard, so not all compilers support them. I've removed them from the table.
If you want to check if a file exists, you can try opening it with an ifstream first (or an fstream using std::ios::in).
There isn't really a more compact way to do it, but you could read the file into a char array instead of a std::string if you wanted. You'd have to watch out for overflowing the array when you read from the file, and for memory leaks (if using a dynamic array). I wouldn't necessary recommend it unless performance is really critical in this section of code.
I'm trying to get fileread part of my program to separate function. The problem is I have to get my filecontent as const char*. Sofar i've come only with this solution:
to this:
does not look much better =(
Why do you need your file content as const char *? If you have a std::string, you can access the underlying const char* by using member function c_str(), like this:
That's what i used...
I need char * for glShaderSource() functuion.
I just thought that there is more compact way to do this. Like not using std::string at all.
I have a question. I have created a program where you can read from a file as well as write to it. I wrote something into the file (it didn't exist before), and it seemed to work. I then read from the file and everything i input appeared on the screen. however, when i look on the directory for the file that was created, it is nowhere to be seen. apparently the file exists because i can read and write to it, but i don't see it on my directory. I am using a mac, if that information is worth anything.
I agree, the file must exist. But where it went? I have no idea. Maybe give it a unique name and then do a filename search for it across your entire hard drive?
i searched for it, and the file was created in a different area than where the file is saved. i have my file saved in a folder for assignments, but when i searched for the file using spotlight, it said the file was located in my user directory. so it looked something like this "user > personnel.dat". i moved the file over to the same directory as the project, but now when i try to read from the file it says the file doesn't exist.
Sounds like maybe one of the code::blocks settings is set to your personal directory instead of the executable directory. Not sure which one though. See http://stackoverflow.com/questions/4017034/c-where-does-the-ofstream-class-save-the-files-to for more info.
Hello Leon,
While this answer comes quite late here it is:
On mac, assuming you are using Xcode, the file will be created in the build directory.
The path is set by an xcode preference called Derived data directory.
The default location is: /Users/<youuserhere>/Library/Developer/Xcode/DerivedData/
In this directory you will find: <yourprojectname>-somehashgeneratedautomatically/Build/Products/Debug/Sample.dat
You can also set to have this relative to the project directory.
In the first example, I see "if (!outf)...". I am a little confused by this. This seems to be trying to access the object outf directly, rather than calling a function like "if(outf.is_open())". Is this an operator overload for the "!" operator? Is this overloaded to make it behave more like C-style file IO? I don't understand how its possible to access an object directly like that...
Yes, if you look at a reference for ofstream (e.g. this one) you'll see that although ofstream doesn't contain an overloaded operator!, it does contain a overloaded conversion to bool, which is being invoked here. That conversion to bool is a synonym for (!outf.fail()). So we're essentially saying if (!!outf.fail()), which is the same thing as if (outf.fail()), which in English means "if an error has occurred ...".
How can I append a line to an existing file, but NOT at the end of the file? I mean, with your example, I'd like to see:
This is line 1
This is line 3
This is line 2
I just spent the past several hours confused about this file i/o until I realised - I'M MIXING UP INPUT AND OUTPUT LOL
y
Hello Alex
Suppose you have a loop outputting x and y variables for a gnuplot .dat file. Is the process going to be buffered (automagically) or not? If I were a harddisk I wouldn't see one single benefit in writing char by char (or group chars) to a file, or a user waiting for 1e5 data entries times ~2~3ms (or however much is a HD access time)... But I'd like to be sure about this.
As a corollary, is there some additional setting that allows us to change the buffer?
Disk I/O is usually buffered. You can change the buffer size -- see documentation for pubsetbuf.
Hello. When compiling and running your code with Visual Studio 2015 and I'm getting the following output:
´╗┐This is Line 1
This is Line 2
Do you know where that weird character in the beginning is coming from? I created the file from scratch on notepad and saved is as Sample.dat. The code is literally copy+paste from yours. Can you help? Thanks.
When you say you created the file from scratch on notepad, you mean you typed "This is line 1" etc... into the file using notepad?
If so, it may have been saved in some variant of unicode format, and this is a byte-order mark that is used to indicate what format it is. If so, your console probably doesn't know how to print this character, so it's picking a placeholder symbol.
test
Referring to your comment:
"performance conscious programmers will often use ‘\n’ instead of std::endl to insert a newline into the output stream, to avoid unnecessary flushing of the buffer"
As I know, stream stdout is line-bufferred and so using '\n' will also flush the output.
There seems to be a lot of confusion on this point, largely due to how std::cout interacts with the underlying C streams. The behavior of std::cout can also be different depending on what it's connected to. As best I can tell, there's no requirement that std::cout be buffered or unbuffered, or that '\n' flush anything. But in most implementations, std::cout probably does flush on '\n' when connected to a console.
Hello Alex,
Thanks for your tutorials. Is it possible to edit a file in c++, like deleting the last input into a file or any other section of the file? Thanks.
Yes, you can alter files in C++.
The easiest way to do this (for files that aren't huge) is to read in the entire file, make your alternations in memory, and then overwrite the contents of the file with the contents in memory.
Please HELP
I need to read this data from a file :
12,alex hales,23,41,12
23,brandon mc,12,123,41
I need to store them in
roll number, name ,quiz1,quiz2,final
Please HELP