File isn’t changed.

Sep 13, 2019 at 2:12pm
I have a class called 'SaveStream' that takes data and saves it with a name next to it as a label, but when I attempt to save something using my 'store_as' function, it doesn’t work, but doesn’t show any errors. Mind helping me out?


1
2
3
4
5
6
7
8
9
#include <iostream>
#include "SaveStream.h"

int main(){
  std::cout << "Hello World!\n";
  int i = 17530;
  SaveStream saveVars("SAVS.txt");
  saveVars.store_as("i",i);
}


SaveStream.cpp
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
#include "SaveStream.h"

SaveStream::SaveStream(std::string saveTo){
  saveFile.open(saveTo); 
  if(!saveFile.is_open()){ // if the file doesn’t exist
    std::ofstream temp;
    temp.open(saveTo);     // make it as a new file
    saveFile.open(saveTo); // and then open the new file
  }
}

bool SaveStream::exists(std::string varName){
  bool does_exist = false;
  while(!saveFile.eof()){
    saveFile >> currVar;
    if(currVar == (varName + ":")){
      does_exist = true;
      break;
    }
  }
  return does_exist;
}

template <typename T>
void SaveStream::store_as(std::string varName,T& var){
  if(exists(varName)){
    int beginningPos = tellg();
    saveFile.seekp(beginningPos);
    char endingSymbol = ' ';
    while(endingSymbol == ';'){ // clears the line up to and including ending ';'
      saveFile.read(&endingSymbol,1);
      saveFile << ' ';
    }
    saveFile.seekp(beginningPos);
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
  else {
    char dump;
    while(){
      // trying to find end so I can append stuff.
      saveFile.read(&dump,1);
    }
    saveFile.seekp( (saveFile.tellg() );
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
}

template<int>SaveStream::store_as(std::string varName,int& var);



SaveStream.h
1
2
3
4
5
6
7
8
9
10
#include <fstream>

class SaveStream{
  std::fstream saveFile;
  public:
  SaveStream(std::string saveTo);
  bool exists(std::string varName);
  template <typeName T>
  void store_as(std::string varName,T& var);
};
Sep 13, 2019 at 5:29pm
but doesn’t show any errors

Really? So why I get a considerable amount of errors from your code copied inside a single file?
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
#include <fstream>
#include <iostream>


class SaveStream{
  std::fstream saveFile;
  public:
  SaveStream(std::string saveTo);
  bool exists(std::string varName);
  template <typeName T>
  void store_as(std::string varName,T& var);
};


SaveStream::SaveStream(std::string saveTo){
  saveFile.open(saveTo); 
  if(!saveFile.is_open()){ // if the file doesn’t exist
    std::ofstream temp;
    temp.open(saveTo);     // make it as a new file
    saveFile.open(saveTo); // and then open the new file
  }
}

bool SaveStream::exists(std::string varName){
  bool does_exist = false;
  while(!saveFile.eof()){
    saveFile >> currVar;
    if(currVar == (varName + ":")){
      does_exist = true;
      break;
    }
  }
  return does_exist;
}

template <typename T>
void SaveStream::store_as(std::string varName,T& var){
  if(exists(varName)){
    int beginningPos = tellg();
    saveFile.seekp(beginningPos);
    char endingSymbol = ' ';
    while(endingSymbol == ';'){ // clears the line up to and including ending ';'
      saveFile.read(&endingSymbol,1);
      saveFile << ' ';
    }
    saveFile.seekp(beginningPos);
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
  else {
    char dump;
    while(){
      // trying to find end so I can append stuff.
      saveFile.read(&dump,1);
    }
    saveFile.seekp( (saveFile.tellg() );
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
}

template<int>SaveStream::store_as(std::string varName,int& var);


int main(){
  std::cout << "Hello World!\n";
  int i = 17530;
  SaveStream saveVars("SAVS.txt");
  saveVars.store_as("i",i);
}



Could you please clarify what you are trying to achieve?

I’ve tryed to remove some syntax errors and to simplify the logic, but I’m not sure if it could be of any help since I don’t understand what your code is expected to do.
Please note you’re not opening your fstream in binary mode, so you’d better avoid methods like read(), tellg(), seekp()…
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
#include <fstream>
#include <iostream>


// ============================================================================
//                               CLASS SAVESTREAM                              
// ============================================================================
class SaveStream {
public:
    SaveStream(std::string save_to);
    bool exists(const std::string& var_name);

    template <typename T>
    void store_as(const std::string& var_name, T& var);

private:
    std::fstream save_file;
};


SaveStream::SaveStream(std::string save_to)
{
    save_file.open(save_to); 

    if( !save_file ) {
        {
            std::ofstream temp(save_to);
            // What if you are working in a read-only directory?
        }
        save_file.open(save_to);
        // So now you are sure the file is correctly opened?
    }
}


bool SaveStream::exists(const std::string& var_name)
{
    for( std::string curr_var; save_file >> curr_var; ) {
        if( curr_var == (var_name + ":") ) {
            return true;
        }
    }
    return false;
}


template <typename T>
void SaveStream::store_as(const std::string& var_name, T& var)
{
    if( exists(var_name) ) {
        auto beginning_pos { save_file.tellg() };
        save_file.seekp(beginning_pos);     // ??

        char ending_symbol { ' ' };

        // clears the line up to and including ending ';' :
        while( ending_symbol == ';' ) {
            save_file.read( &ending_symbol, 1 );
            save_file << ' ';
        }
        save_file.seekp(beginning_pos);
        save_file << var_name << ": " << var << ";\n\n\n";
    }

    else {
        char dump;
        
        for(int i {}; i < 5; ++i) {
            // trying to find end so I can append stuff.
            save_file.read( &dump, 1 );
            std::cout << "Welcome in a never ending loop!\n";
        }
        
        save_file.seekp( save_file.tellg() );
        save_file << var_name << ": " << var << " ;\n\n\n";
    }
}
// end class SaveStream


int main()
{
    std::cout << "Hello World!\n";
    SaveStream save_vars( "SAVS.txt" );
    constexpr int i { 17530 };
    save_vars.store_as("i", i);     // save_vars.store_as<const int>("i", i);
}

Sep 13, 2019 at 11:29pm
I tried using your changes, but my file is still not changing and no. I’m not in a read only directory, so it’s not that.

What I am doing is I am making a class to permanently store stuff with labels to make it easier for me to get stuff back. In a perfect world the file would look something like this:
var_save_file:
1
2
3
4
var1Name: 534172;
var2Name: hdbdtwy74#$a);
var3Name: 1;
var4Name: 348.227;


and I could simply get my info back by calling a set_to function like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "SaveStream.h"

int main(){
  int var1;
  std::string var2;
  bool var3;
  double var4;
  SaveStream mainVarsSaver("var_save_file");
  mainVarsSaver.set_to("var1",var1);
  mainVarsSaver.set_to("var2",var2);
  mainVarsSaver.set_to("var3",var3);
  mainVarsSaver.set_to("var4",var4);
}
Sep 14, 2019 at 1:18pm
I am making a class to permanently store stuff with labels to make it easier for me to get stuff back.

What about a logic like this (not tested!)?

SettingManager.hpp:
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
#ifndef SETTINGMANAGER_HPP
#define SETTINGMANAGER_HPP


#include <iostream>
#include <map>
#include <string>


class SettingManager {
public:
    std::string settings_f_name;

    explicit SettingManager(std::string f_name = "highwayman.dat");
    ~SettingManager();

    bool readSettings();
    bool writeSettings(bool newfile = false);
    void askForNewFileName();
    bool replaceValueAt(const std::string& key, const std::string& new_val);
    void at(const std::string& key, int& value);
    void at(const std::string& key, double& value);
    void at(const std::string& key, std::string& value);

    explicit operator bool() const;

private:
    std::map<std::string, std::string> settings;
    bool settings_read { false };
    bool settings_saved { true };

    void replaceSpacesWithUnderscores(std::string& str);

//friend:
    friend std::ostream& operator<< (std::ostream& os, const SettingManager& rhs);
};


// ===========================================================================
//                        NOT MEMBER GENERAL FUNCTIONS
// ===========================================================================

// Copied from:
// https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring

void ltrim(std::string &s);
void rtrim(std::string &s);
void trim(std::string &s);


#endif // SETTINGMANAGER_HPP 


SettingManager.cpp:
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include "SettingManager.hpp"
#include <algorithm>
#include <cctype>
#include <exception>
#include <fstream>
#include <locale>
#include <sstream>
#include <type_traits>


SettingManager::SettingManager(std::string f_name)
    : settings_f_name { f_name }
{
    if( !readSettings() ) {
        std::cout << "Warning: settings are yet to be read...\n";
    }
}


SettingManager::~SettingManager()
{
    if( ! settings_saved ) {
        std::cout << "It seems you're about to exit the program.\n"
                     "Settings has been modified.\n"
                     "Do you want to save them (y/n)? ";
        std::string line;
        std::getline(std::cin, line);
        if( line.at(0) == 'y' || line.at(0) == 'Y' ) {
            writeSettings(true);
        }
    }
}


bool SettingManager::readSettings()
{
    // Optional: ask if overwrite:
    if( !settings.empty() ) {
        std::cout << "Do you want to re-load settings from file (y/n)?\n"
                     "(Current settings will be overwritten)\n>>> ";
        std::string line;
        std::getline(std::cin, line);
        if( line.at(0) != 'y' && line.at(0) != 'Y' ) {
            return false;
        }
    }

    // Check setting file:
    std::ifstream inf(settings_f_name);
    if( !inf ) {
        if( settings.empty() ) {
            settings_read = false;
        }
        return false;
    }

    // Read settings in "settings":
    for(std::string line; std::getline(inf, line); /**/) {

        std::string key;
        std::istringstream iss { line };
        std::getline(iss, key, ':');

        // Better avoid spaces:
        trim(key);
        replaceSpacesWithUnderscores(key);

        std::string value;
        std::getline(iss, value, ';');
        trim(value);
        // replaceSpacesWithUnderscores(value);    // optional

        settings.insert_or_assign(key, value);
        // or settings[key] = value;
    }

    // The file could be empty:
    settings_read = !settings.empty();
    // Now written settings and settings in memory are the same:
    settings_saved = true;
    return settings_read;
}


bool SettingManager::writeSettings(bool newfile)
{
    // Optional: overwrite?
    if( newfile ) {
        askForNewFileName();
    }

    std::ofstream fout(settings_f_name);
    if( !fout ) {
        std::cout << "Cannot open " << settings_f_name << " for writing.\n";
        return false;
    }

    // Cannot use structured binding declaration yet, sorry:
    for(const auto& p : settings) {
        fout << p.first << ": " << p.second << ";\n";
    }
    settings_saved = true;
    return true;
}


void SettingManager::askForNewFileName()
{
    std::cout << "Do you want to save settings to a new file (y/n)? ";
    std::string line;
    std::getline(std::cin, line);
    if( line.at(0) != 'y' && line.at(0) != 'Y' ) {
        return;
    }

    std::cout << "New file? ";
    std::getline(std::cin, settings_f_name);
}


// Return false if 'key' cannot be found
bool SettingManager::replaceValueAt(const std::string& key,
                                    const std::string& new_val)
{
    try {
        settings.at(key) = new_val;
    } catch( std::out_of_range& ) {
        return false;
    }
    settings_saved = false;
    return true;
}


// Might throw!!
void SettingManager::at(const std::string& key, int& value)
{
    value = std::stoi( settings.at(key) );
}


// Might throw!!
void SettingManager::at(const std::string& key, double& value)
{
    value = std::stod( settings.at(key) );
}


// Might throw!!
void SettingManager::at(const std::string& key, std::string& value)
{
    value = settings.at(key);
}


SettingManager::operator bool() const
{
    return settings_read;
}


void SettingManager::replaceSpacesWithUnderscores(std::string& str)
{
    for(auto& c : str) {
        if ( c == ' ' ) { c = '_'; }
    }
}


// ===========================================================================
//                                 FRIENDS
// ===========================================================================
std::ostream& operator<< (std::ostream& os, const SettingManager& rhs)
{
    // Cannot use structured binding declaration yet, sorry:
    for(const auto& p : rhs.settings) {
        os << p.first << " --> " << p.second << '\n';
    }
    return os;
}


// ===========================================================================
//                        NOT MEMBER GENERAL FUNCTIONS
// ===========================================================================

// Copied from:
// https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring

// trim from start (in place)
void ltrim(std::string &s)
{
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}


// trim from end (in place)
void rtrim(std::string &s)
{
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}


// trim from both ends (in place)
void trim(std::string &s)
{
    ltrim(s);
    rtrim(s);
}


main.cpp:
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
#include "SettingManager.hpp"
#include <iostream>


// Assumes highwayman.dat looks like:
// var1Name: 534172;
// var2Name: hdbdtwy74#$a);
// var3Name: 1;
// var4Name: 348.227;
int main()
{
    SettingManager sm;
    std::cout << "Current settings:\n" << sm << '\n';

    int tmp_i;
    sm.at("var3Name", tmp_i);
    std::cout << "var3Name::value + 12 --> " << tmp_i + 12 << '\n';

    std::cout << "Please insert new value for variable var2Name: ";
    std::string line;
    while( std::getline(std::cin, line) ) {
        if( line.empty() ) {
            std::cout << "Empty line refused. Need a value.\nRetry: ";
            continue;
        }
        break;
    }
    sm.replaceValueAt("var2Name", line);
    std::cout << "\nCurrent settings:\n" << sm << '\n';

    if( sm.readSettings() ) {
        std::cout << "Settings has been re-read.\n"
                     "\nCurrent settings:\n" << sm << '\n';
    }
}

Sep 14, 2019 at 4:58pm
Hello highwayman,

Like Enoizat when I loaded your code I had several errors and even after some of the original errors I received some new errors.

Some things I have found that have not been mentioned yet.

In "main" I had to add the header file "<string>". Given the line std::cout << "Hello World!\n"; on the rhs of "<<" is a string and the header file is needed so that "cout" and "<<" will know how to process this string. "<iostream>" is not enough to make this line work.

In the header file and the ".cpp" file for "SaveStream" everything works until you get to the template part. I do not know a lot about templates, but I have learned that when you define a template in a header file the function that goes with the prototype needs to be in the same file. What I did was to copy and comment out the function in the ".cpp" file and paste it into the header file. Once all the template information was in the same file it compiled fine.

I am still not sure why all the template information needs to be in the same file, but I accept that this is the way it is done and it works.

In the "SaveStrea.cpp" file the first two functions are OK there.

Tn the "SaveStream::SaveStream: function what I see there is:
The code saveFile.open(saveTo); "saveFile" was defined as a fstream, so when you open the file you have to tell it if you are opening the stream for input, output or both. Also you are using "read" and "write" which work on binary files to read chunks of the file which means you would also have to open the file in "binary" mode. But since you are creatint a a text file you should not open the file in "binary" mode.

In the code:
1
2
3
4
5
6
if (!saveFile.is_open())
{ // if the file doesn’t exist
	std::ofstream temp;
	temp.open(saveTo);     // make it as a new file
	saveFile.open(saveTo); // and then open the new file
}

As Enoizat has showed you in his code the if statement can be simply written as if (!saveFile). It works just as well and works slightly better as a more generic form with out all the extra work of calling the function.

When you create "temp" as an "ofstream" it implies that you are opening the file for output and as you are using it will create the file if it does not exist. It may also be a good idea to close "temp" after you have created the file because you no longer need it.

In the open statement again you need to tell it how you want to open the fstream, input, output or both. "fstream" is not designed to default to any stream type and it can not read your mind to know what you want.

Over time I have found it to be a good practice that when opening a stream for input to check an make sure the stream is open. Nor quite as necessary for an output stream because if the file does not exist it will create it before writing to the file. If the output stream has a path to the file then it becomes more of a necessity to check if it is open because as I have found something wrong in the path to the file could cause the file stream not to open.

In the "SaveStream::exists" function the line while (!saveFile.eof()) does not work the way you are thinking. The way your code is written by the time the while condition determines that it has reached "eof" you have entered the while loop more more time than is needed.

To use the while condition as you have it this would work better:
1
2
3
4
5
6
7
8
9
10
11
12
saveFile >> currVar;

while (!saveFile.eof())
{
	if (currVar == (varName + ":"))
	{
		does_exist = true;
		break;
	}

	saveFile >> currVar;
}

By doing the first read before the while condition and then doing all the other reads at the end of the while loop !saveFile.eof()will work as it should and the while loop will end when it should. The other , more often used, method is:
1
2
3
4
5
6
7
8
while (saveFile >> currVar;)
{
	if (currVar == (varName + ":"))
	{
		does_exist = true;
		break;
	}
}

This way when the read sets "eof" the condition and the loop fail. This tends to be the best way to read a file of any size without knowing what is in the file.

If you are writing this to a file
var1Name: 534172;
var2Name: hdbdtwy74#$a);
var3Name: 1;
var4Name: 348.227;

you will need to account for the var1Name: part when reading the file.
I.E.,
while (saveFile >> junk >> currVar;). Where "junk" reads the heading and stops at the ":". This is a general idea because the input you are using has int, double and string as the variable.

The first thing you have to decide on is if you are going to work with the file as a text file or a binary file. It will make a difference how you read and write to the file. Be aware that the above example is for using a text file.

In the template function in the else part you have while (). This does not mean that it is to be an endless loop, but does produce an error because there needs to be something in the () for the condition to check. At this I am not sure what to put for the condition to get the loop to end.

I am thinking that the while loop could be replaced with saveFile.seekg(0, saveFile.end. In the past when I have used "seekp" to position the output file pointer it did not work as I expected, but "seekg" did work on both input and output when "fstream" was used as the stream type/

For now these are things I see and questions that need an answer before the program can be corrected to work the way that you want.

Also when you put headings in a file you need to account for them when reading the file however you open the file.

Hope that helps,

Andy
Sep 15, 2019 at 1:14am
Hello highwayman,

I finally figured it out. It is using the "fstream" for both input and output that through me off.

In the function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool SaveStream::exists(std::string varName)
{
	bool does_exist = false;
	std::string currVar, junk;

	while (saveFile >> currVar >> junk)
	{
		if (currVar == (varName + ":"))
		{
			does_exist = true;
			break;
		}
	}

	saveFile.clear();

	return does_exist;
}

The parts in bold I added. The underlined while condition is what I changed. The variable "currVar" was not defined in the function or passed in to the function. The "junk" variable will make the reading go a bit faster instead of reading each part of a line with two reads. In a small file you will not see any difference, but as the file get larger you will notice a difference.

When you open a "fstream" for input and output reading the file until "eof" is set also affects writing to the file because you are using the same stream for both.

For the function:
1
2
3
4
5
6
7
8
9
10
11
12
13
SaveStream::SaveStream(std::string saveTo)
{
	saveFile.open(saveTo, std::ios::in | std::ios::out);

	if (!saveFile)
	{ // if the file doesn’t exist
		std::ofstream temp;

		temp.open(saveTo);     // make it as a new file
		temp.close();
		saveFile.open(saveTo); // and then open the new file
	}
}

The parts in bole I added and the if condition I changed. Using "temp" to create a file I felt that it is a good idea to close the stream before you open the same file as a "fstream". Even though "temp" is an "ofstream".

In the
1
2
template <typename T>
void SaveStream::store_as(std::string varName, T& var)

function the else part I changed to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
else
{
	//char dump;

	
	//while (1)
	//{
	//	// trying to find end so I can append stuff.
	//	saveFile.read(&dump, 1);
	//}

	//saveFile.seekg(std::ios::end);

	saveFile << varName << ": " << var << ";\n\n\n";
}

The lines with the comment I left there to show that they are not needed. The line with "seekg" or "seekp" did not seem to make any difference if it is in or out of the code. Either way the file was still appended. This may have to do with the "exists" function reading the file to "eof" and leaving the file pointer at the end for append. At the moment it is a good guess, but I believe that is what has happened.

I think I have covered everything except removing the space before the ";" in line 14 in the last bit of code.

Almost forgot in "main" I changed the line saveVars.store_as<int>("i", i);. Since it is a template class you need to tell what type it will be. At least that is the way I understand it.

Hope that helps,

Andy
Sep 16, 2019 at 9:26pm
I apologize for taking so long to reply, for some reason it has been particularly hard for me to process your posts, but after going over them I completely flipped my class over and took from Enoizat’s concept and put my templates functions in the header file like Handy Andy was talking about. Now though I have a problem that is making my file’s format get messed up. Because ios::trunc doesn’t want to work. Give me a sec and I’ll send the code
Sep 17, 2019 at 1:36am
Update: now it’s not showing anything again. I think it’s just me or something I’ll have to test it out.

here’s the code:

SaveStream.h:
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
#ifndef SAVESTREAM_H
#define SAVESTREAM_H

#include <fstream>
#include <sstream>
#include <map>

class SaveStream{
  std::string fileName;
  std::map<std::string,std::stringstream> values;
  public:
  SaveStream(std::string file_name);
  template <typename T>
  bool get(std::string varName,T& var){
    return (values[varName] >> var);
  }
  template <typename T>
  bool put(std:string varName,T var){
    values[varName].str(""); // small change after the file stopped updating - I think it might help a bit.
    if(values[varName] << var){
      return true;
    }
    return false;
  }
  void flush();
}

#endif 


SaveStream.cpp:
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
#include "SaveStream.h"

SaveStream::SaveStream(std::string file_name){
  fileName = file_name;
  std::ifstream fin(fileName);
  if(fin){
    while(fin){
      fin >> varName;
      fin.ignore(1,' ');
      fin.ignore(3,':');
      fin.ignore(1,' ');
      std::getline(fin,var,'\n');
      values[varName] << var;
    }
  }
  else {
    std::ofstream temp(fileName);
    temp.close();
  }
}

void SaveStream::flush(){
  std::fstream fio;
  fio.open(fileName, std::ios::trunc);
  for(const auto& varPair : values) { // forgot to say before, but Enoizat you’re an fing genius.
    fio << varPair.first << " ::: " << varPair.second.str() << std::endl;
  }
}


main.cpp:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>

int main(){
  std::cout << "Hello World!\n";
  // test
  int i = 17530;
  SaveStream saveVars("ss.txt");
  saveVars.put<int>("i",i);
  saveVars.flush();
}
Last edited on Sep 17, 2019 at 1:37am
Sep 17, 2019 at 3:07am
closed account (SECMoG1T)
Hello, I edited just some bits, I have tested it on this file and it works as expected:
setting.dat:

custom : 1
error : 0
index : 17530
ite : null
loaded : pending
metadata : 1
os : linux
site : null
sources : unavailable


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
#include <string>
#include <fstream>
#include <sstream>
#include <map>


class SaveStream
{
      std::string fileName;
      std::map<std::string,std::stringstream> values;

      void clear_stream(std::stringstream& ss)
      {
          ss.str("");
          ss.clear();
      }

  public:
      SaveStream(std::string file_name);

      template <typename T>
      bool get(std::string varName,T& var)
      {
        return (values[varName] >> var);
      }

      template <typename T>
      bool put(std::string varName,T var)
      {
        clear_stream(values[varName]);//you have to clear the stream before reusing otherwise the previous data is still present.

        if(values[varName] << var)
        {
          return true;
        }
        return false;
      }

      void flush();
};

SaveStream::SaveStream(std::string file_name)
 :fileName(file_name)
{
      std::ifstream fin{fileName};
      if(fin)
      {
        std::string varName{},senitel{},value{};
        while(fin >> varName >> senitel >> value)
            values[varName] << value;
      }
      else
        std::ofstream temp{fileName};///closed automatically
}

void SaveStream::flush(){
  std::fstream fio{fileName};

  for(const auto& varPair : values) { // forgot to say before, but Enoizat you’re an fing genius.
    fio << varPair.first << " : " << varPair.second.str() << std::endl;
  }
}

int main(){
//  std::cout << "Hello World!\n";
  // test
  int index = 17530;
  SaveStream saveVars("setting.dat");
  saveVars.put<int>("index",index);
  saveVars.put<int>("metadata",1);
  saveVars.put<std::string>("sources","unavailable");
  saveVars.flush();
  return 0;
}
Last edited on Sep 17, 2019 at 3:36am
Sep 18, 2019 at 1:16am
Hello highwayman,

It took me awhile to correct the errors just to get it to compile. I have not tried to run the program yet.

To the question on "tuunc". Your code:
1
2
3
std::fstream fio;

fio.open(fileName, std::ios::trunc);

"fio" is defined as a "fstream" which can be used as in input or output stream or both. In the"open" statement you are tryint to "trunc" the file, but this only works with an output stream and you have not told the open statement how the stream should be opened.

http://www.cplusplus.com/reference/fstream/fstream/fstream/
http://www.cplusplus.com/reference/fstream/fstream/open/

For the errors I found when compiling the program:

In the file that contains "main" you did not include the header file "SaveStream.h". It is needed there.

In your header file you have the include files:
1
2
3
#include <fstream>
#include <sstream>
#include <map> 

Since you include this header file in both ".cpp" files these header files are included in each ".cpp" file whether they are needed or not. When compiled it will only compile these files once.

I am not sure why, but I had to move void flush(); above the two template functions for it to be seen properly by the ".cpp" file.

In the function:
1
2
3
template <typename T>
bool put(std:string varName, T var)
		{

You are missing a ":" after "std". Easy typo to make.

The class is missing the ";" after the closing brace.

In the SaveStream::SaveStream function:

In the function you are using ifstream and ofstream. This makes it easier to keep straight what you are using the stream for.

In the while loop the variables "varName" and "val" are both undefined. I added these after the definition of "fileName" in the class and it worked out. Not sure if that is where you want to define them or not.

Now all that is left is to give it a run and see what happens.

Just so you know there is a "std::flush" that can be used when you include "iostream", so be careful when defining a function with the same name. Your function may not be used.

Other than I do not like the idea of the header files in the "SaveStream.h" file it appears that it does work best that way.

I think I have covered all the small errors I have found. If I realize something else I will let you know.

Hope that helps,

Andy
Sep 18, 2019 at 2:53pm
Hello highwayman,

I started testing your revised program today. Ended up with some questions:

This is a new version of the program, but did the input file change? Or are you using the original input file?

Not knowing I have been using the original input file.

In "main" you call the function saveVars.put<int>("i", i);. This works except that the functions returns a "bool" that yo do not make use of. For now all I have done if capture the returned value. I am not sure what you want to do with it. The other problem I found is that you call the function with "i" as the first parameter, but if the input file is to follow the original format it should be "i:" and the line in the input file would be "i: ".

The next problem I found:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (fin)
{
	while (fin)
	{
		fin >> varName;
		fin.ignore(1, ' ');
		fin.ignore(3, ':');
		fin.ignore(1, ' ');
		std::getline(fin, var, '\n');
		values[varName] << var;
	}
}
else
{
	std::ofstream temp(fileName);
	temp.close();
}

The if condition works, but the while condition does not. The while condition is similar to while (!fin.eof()). Neither will work the way you expect them to. By the time the condition determines that you have read past "eof" and set the "eof" bit and also changing the "good" bit to zero (0) the line values[varName] << var; has used the last value read into the variable and processed one extra time that is not needed.

Then there is all the "ignore" statements. The code as is gives you a value for "var" of (72) when it should be (534172). Then unless you have changed the input file there is a ";" at the end of the line to deal with.

To get this to work with the input file that I used this worked better:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (fin)
{
	while (std::getline(fin, varName, ' '))
	{
		std::getline(fin, var, ';');
		fin.ignore(1);  // <--- ignores the new line at the end of the line.
		values[varName] << var;
	}
}
else
{
	std::ofstream temp(fileName);
	temp.close();
}

In line 3 the third parameter is to keep the ":" at the end of the variable name or you could change the third parameter to (':') to not include the ":". But you would need to put a copy of line 6 above line (5) to ignore the ":" and change line 5 to std::getline(fin >> std::ws, var, ';');. This will eat any white space before the next read. Or the "ignore" statement that yo put above line 5 would need to "ignore" (2) instead of (1).

In the header file the function bool put(std::string varName, T var) You are going about this function the wrong way. First it does not need to be a template because the variable "var" is never used. Line 4 was more a problem than a solution. The function worked better when I commented it. The other line that I have a comment, on line 6, I am not sure what that is doing, but it did not seem to work, at least early on.

This seems to work better:
1
2
3
4
5
6
7
8
9
10
11
12
13
//template <typename T>
bool put(std::string varName/*, T var*/)
{
	//values[varName].str(""); // small change after the file stopped updating - I think it might help a bit.

	//if (values[varName] << var)
	if (values.find(varName) != values.end())
	{
		return true;
	}

	return false;
}

Line 7 is the best way I know of to search a map. The returned value of the "find" function is an "iterator" that you can check with the end of the map. It returns true if it finds something otherwise false. You would use this value back in "main" in some way.

So far these changes have appeared to work, but I have not begun to work on the "flush" function until I am sure what your input file is like.

Hope that helps,

Andy
Topic archived. No new replies allowed.