Write then read text file

Good evening,

I realise there's the fstream option, but it doesn't seem to be working in this instance so would like some help.

How does one go about having the program create a new text file, write to it, then output the contents to screen?

I've tried fstream("name.txt", std::ios::out), but that won't read from the file (unless there's a way to read the file contents without using getline?

I've also tried ofstream but I get the same results, and fstream seems to not create a new file unless it's stated as being ios::out.

I did try to use ofstream, write to file, close file then reopen as an fstream, but the filename is use input so I can't hardcode it in there and was getting errors when using my 'outfile' variable.

Could anyone please help?

Thanks so much
Last edited on
How does one go about having the program create a new text file, write to it, then output the contents to screen?


You have to break these into distinct steps.
1. Create a new file.
2. Write to it.
3. Output the contents to a file

Step three is a little complex, mostly because the statement a little vague. It might better say, "Read the file content and display it on the screen."

Remember, the screen (std::cout) is a stream, just like a file.

One more thing, std::fstreams are objects and should be initialised in the constructor and close themselves on destruction.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int mian()
{
    std::string filename = "homework.txt";

    // create the file
    {
        std::ofstream file(filename, std::ios::trunc); // delete the old content of the file
        file << "write some" << std::endl;
        file << "text" << std::endl;
    }

    // copy the file to the screen
    {
        std::ifstream file(filename); // reopen the file for input

        std::string line;
        while (std::getline(file, line)) // read each line of the file
        {
            // copy the line to the screen yourself
        }
    }
}
Is there anyway to convert the filename of the ofstream into a string variable for use in the opening of the ifstream?
I ask because the string used to create the first filename can change and is allocated within a separate function so not allocated globally. Trying to figure out my options without needing to rewrite a stack of code.

Thanks, I appreciate your help & time.
Hello Airynoob72,

You can use fstream("name.txt", std::ios::out); to create the file, but you do not have a variable to represent the file stream. Better use would be something like: fstream outFile("name.txt", std::ios::out);. Then you could use outFile << "something to write" << std::endl; where "something to write" could be a text string or a variable of any type. Be sure to use outFile.close(); when you are done with the file.

When you are ready to read the file you can use fstream inFile("name.txt", std::ios::in);. Or as I like to use
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
33
34
35
36
37
38
39
40
41
42
std::string iFileName{ "" };  // <--- Put file name here.

std::ifstream inFile;

inFile.open(iFileName);

if (inFile.is_open())
{
        //  Comment out these lines when no longer needed.
	std::cout << "\n File " << iFileName << " is open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(2));  // <--- Needs header files chrono" and "thread".
}
else
{
	std::cout << "\n File " << iFileName << " did not open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread".
	exit(1);
}

//*****************************************************************

std::string oFileName{ "" };  // <--- Put file name here.

std::ofstream outFile;

outFile.open(oFileName, std::ios::app);  // <--- "app" to add to the end of the file.
//outFile.open(oFileName, std::ios::trunc | std::ios::ate);  // <--- Mainly used for testing when starting a program.
                                         //"trunc" keeps the output file short until ready for use. "ate" works better
                                         //with "trunc" and does the same as "app".

if (outFile.is_open())
{
        //  Comment out these lines when no longer needed. 
	std::cout << "\n File " << oFileName << " is open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(2));  // <--- Needs header files chrono" and "thread".
}
else
{
	std::cout << "\n File " << oFileName << " did not open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread".
	exit(1);
}

I like to use this to make sure the file(s) have opened before continuing with the program. Feel free to use if you like. Some of the code could be shortened, but when starting with using file streams it has its uses.

Something you could do is fstream ioFile("name.txt", std::ios::out | std::ios::in);. This would open the file for both input and output, you would have to keep track of the file pointer and move it to the beginning of the file to read and move to the end of the file to write. Easier to open and close the file as needed.

I did try to use ofstream, write to file, close file then reopen as an fstream, but the filename is use.

Closing the file stream for output and then open the stream for input should not be a problem.

Without seeing your code it is hard to say what is happening. It isonly a guess for now.

Hope that helps,

Andy

P.S. Better to post your code and point out where you think you are having a problem or state where the problem is.
http://www.catb.org/esr/faqs/smart-questions.html


So I just changed the section in bold to what it is there and it seems to have worked.
But now or some reason it's terminating the program when an incorrect infile or outfile name is entered and you re-enter a new one.

terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::substr

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Last edited on
At first glance:
Line 24: fstream outfile;
perhaps it should be ofstream outfile;
Unfortunately it can't be ofstream, I need to print to the screen after writing to it, I opened it as a fstream, in|out| trunc which has made it work with creating a new file if there's not one there.
It also works if I create a new file in the command line so I'm presuming that line has worked ok.
Hello Airynoob72,

I have not been through all of your code yet.

"fstream", "ofstream" and "ifstream" are all streams, but these streams work with files whereas "cin" and "cout" are streams that work with the keyboard and screen. They are all separate and different streams.

I think you are trying to use or think of "fstream" as being needed for a replacement for "cin" and "cout". This could be done, but I would say that it is more involved than you would want to get into. That is why there is "iostream" to deal with "cin", "cout" and "cerr".

I will look at your code and let you know if I see anything wrong.

The first part I find missing is the "exception.h" header file which gives me some errors I am not sure how to change because of what I do not have.

Hope that helps,

Andy

P.S. What do the command line arguments look like so I know what you are using and what to expect?
Last edited on
Hey Andy,

Thanks so much for your help.

I can get the program to work, but it won't write to the output file (but only when I have to go into the exception for an incorrect file name then input a correct one). If I use the correct filenames in the beginning then all is dandy. I don't know what I'm doing wrong and I'm getting frustrated.
I hope this can give some hints.
Apparently it runs, but of course I can't test it because I don't have your data:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <algorithm> 
#include <cctype>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>
#include <exception>
#include <utility>

class MyExcp : public std::exception {
public:
    MyExcp(std::string message_arg);
    const char* what() const throw() override;
    void setMessage(const std::string& value);
protected:
    std::string message;
};

MyExcp::MyExcp(std::string message_arg)
    : message {message_arg}
{}

const char* MyExcp::what() const throw()
{ return message.c_str(); }

void MyExcp::setMessage(const std::string& value)
{ message = value; }

void openFile(std::ifstream& myfile, const std::string& name); 
void openFile(std::ofstream& myfile, const std::string& name); 

bool checkIfDigits(const std::string& check);
void addNumbers(double temp_num, double& sum, double& mycount);
std::string get_string_component(char component, tm* AStruct);
void waitForEnter();

int main(int argc, char* argv[])
{
    if(argc < 3) {
        std::cout << "\nUsage: app_name data_file_name output_file_name\n";
        exit(EXIT_FAILURE); // this is not a program error: I'd use "return".
    }
    // No further controls needed: additional parameters will be ignored
    std::ifstream infile;
    bool fileopened {false};
    std::string name {argv[1]};
    do {
        try {
            openFile(infile, name);
            fileopened = true;
        } catch (MyExcp& error) {
            std::cout << error.what();
            std::getline(std::cin, name);
            fileopened = false; // redundant
        }
    } while(!fileopened);
    
    //read each line of input file
    std::string temp_str;
    double sum = 0;
    double mycount = 0; // count --> there're functions count() in std namespace
    while (!getline(infile, temp_str)) {
        std::string num_str = temp_str.substr(40,10);
        num_str.erase(num_str.find_last_not_of(" \n\r\t")+1);
        
        if (checkIfDigits(num_str)) {
            double tmpdouble = std::stod(num_str);
            addNumbers(tmpdouble, sum, mycount);
        } else {
            std::cout << "Number contains non digits: " << num_str 
                      << "\n Current Line will be Skipped!\n\n";
        }
    }
    infile.close();
    

    std::time_t now = std::time(0);
    std::tm* astruct = std::localtime(&now);
    std::mktime(astruct);
    double average = sum/mycount;
    
    //Write data to output file
    std::ofstream outfile;
    fileopened = false;
    name = argv[2];
    do {
        try {
            openFile(outfile, name);
            fileopened = true;
        } catch (MyExcp& error) {
            error.what();
            std::getline(std::cin, name);
            fileopened = false; // redundant
        }
    } while(!fileopened);
    outfile << "Date: " << get_string_component('c', astruct)
            << "\nTotal Payment: $" << sum
            << "\nAverage payment: $" << std::fixed << std::setprecision(2) 
            << average << std::endl;    
    
    //Display output file data
    outfile.close();
    infile.open(name); // name should be still equal to argv[2]
    std::string out_str;
    while (!std::getline(infile, out_str)) {
        std::cout << out_str << "\n";
    }
    void waitForEnter();
    return 0;
}


void openFile(std::ifstream& myfile, const std::string& name)
{
    myfile.open(name);
    if (!myfile.is_open()) {
        MyExcp myexcp("Cannot open data file " + name + 
                      ". Please input the correct data file name: ");
        throw myexcp;
    }
}


void openFile(std::ofstream& myfile, const std::string& name)
{
    myfile.open(name);
    if (!myfile.is_open()) {
        MyExcp myexcp("Cannot open data file " + name + 
                      ". Please input the correct data file name: ");
        throw myexcp;
    }
}


/* ==========================================================================
 * Question: should this function manage negative numbers too?
 * In that case, the first character should also be checked for minus sign.
 * ========================================================================== */
// Check if any non-digit characters in std::string
bool checkIfDigits(const std::string& check) 
{
    for(unsigned int k = 0; k < check.length(); ++k) {
        if(not isdigit(check[k])) {
            return false;
        }
    }
    return true;
}


// Calculate total of payments 
void addNumbers(double temp_num, double& sum, double& mycount)
{
    sum += temp_num;
    ++mycount;
}    


std::string get_string_component(char component, tm* astruct)
{
    char format[3];
    format[0] = '%';
    format[1] = component;
    format[2] = '\0';
    char ans [80];
    strftime(ans, 80, format, astruct);
    std::string strans(ans);
    return strans;
}


void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Last edited on
Thanks everyone for your help!
Topic archived. No new replies allowed.