Dynamic filenames

I'm trying to figure out how to make a filename based on the following:
1
2
3
4
5
6
7
8
9
10
11
	char *tmp;
	const char file[] = {"datos/file_"}, ext[] = {".txt"};
	ofstream mySaveFile;
	
	for(int i = 1; i <= 50; i++){
		ostringstream number;
		number << i;
		strcpy(tmp, file);
		strcat(tmp, number.str());
		strcat(tmp, ext);
		mySaveFile.open(tmp);

But I'm returning the following error:
fileOperations.cpp: In function ‘int main()’:
fileOperations.cpp:19: error: cannot convert ‘std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ to ‘const char*’ for argument ‘2’ to ‘char* strcat(char*, const char*)’

For the purposes of this forum post, line 19 = line 9.
char pointers are not strings

Even if your code compiled, it would probably explode when you ran it because you're corrupting memory.

If you want a string, use a string.

But even a temp string here isn't necessary because you can just use the string stream:

1
2
3
4
5
6
for(...)
{
  ostringstream filename;
  filename << "datos/file_" << i << ".txt";
  mySaveFile.open(filename.str().c_str());
}
Last edited on
The output is perfect with your example, but for future reference, if I did want to strcat(), how would I go about it, since it doesn't seem to like accepting actual strings?
if I did want to strcat()


I really don't recommend it. But strcat works with c-style strings (char arrays).

strings (std::string) and stringstreams are completely different. And if you're using them, you don't need strcat at all.

Even in this case, if you're using char arrays, strcat wouldn't be a good idea. You'd be better off with sprintf. That way you don't have to mix the stringstream in there.

C-style approach (no strings, stringstreams):

1
2
3
4
5
6
7
char filename[30]; // note an array, not a pointer

for(...)
{
   sprintf(filename,"datos/file_%d.txt",i);
   mySaveFile.open(filename);
}


The problem with this is that there's a potential for overflow. Here, 'filename' can hold a string that is up to 29 characters long. Any longer and it will still compile OK, but you'll have memory corruption (disasterous!!!)

Overflowing a string or stringstream like this is pretty much impossible, so they're much safer and simpler to use, which is why I recommend them.

Now, if you want to mix stringstreams and char arrays for whatever reason (even worse idea), and you want to use strcat... then you could do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
char filename[30]; // note an array, not a pointer

for(...)
{
   ostringstream number;
   number << i;

   strcpy(filename,"datos/file_");
   strcat(filename,i.str().c_str());
   strcat(filename,".txt");

   mySaveFile.open(filename);
}


But again this is silly and computationally more expensive than the above alternatives. You also still have the risk of overflowing 'filename' if you're not careful.
Now the logical question to follow:
How do I open every file in a directory and read from it? Currently, I just use one 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
		case 4:

			myOpenFile.open("save.txt");

			it = characters.begin();

			while(!myOpenFile.eof()){

				myOpenFile >> name;

				for(int i = 0; i < 9; i++){

					myOpenFile >> tempInt;

					x.push_back(tempInt);

				}

				temp = new character(name, x);

				cout << "Successfully loaded " << temp->charName << endl;

				characters.push_back(temp);

				x.clear();

			}

			myOpenFile.close();

			break;
You're getting into filesystem stuff, which the standard lib doesn't really cover.

For this, you'll need to go platform specific, or you'll need to use a 3rd party lib.

Is this for Windows?
Ubunto 10.10, for the most part, but one (and I hate him for it) of my clients runs Windows Vista.
Well then it sounds like you need to go cross platform to accomidate your Vista client.

Boost has a crossplatform filesystem lib. I haven't used it in great detail so I'm afraid I can't be much help to explain how to use it.

You can check it out here:

http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/index.htm


Although since this is a small task, you might want to just write your own simplistic implementation that wraps around the *nix / Win platform specific approaches. I've done something like that in the past and am happy to share code, but I'm at work now. When I get home I can post some of it (maybe 7.5 hrs from now)
There's also the potential to actually save a file that contains a list of files that need to be opened, would that be a better solution?
Depends on the behavior you want. If the goal here is to open every file in a directory, creating a separate index file that lists every file in the directory would be redundant and extra work. I wouldn't recommend it.

On the other hand, if there are files in the directory that might be excluded, or files in another directory that might be included, then yes such an index file would be a good solution.


EDIT: It's also worth mentioning that boost's page makes this look complicated, but it really isn't. The code for iterating over all files in a directory isn't that bad. I just don't recall how to do it offhand so I can't post it from work.
Last edited on
Well, the only thing that's going to be loaded is in, and everything from, this directory. I would just save everything in the same file, but whereas right now I'm looking at a vector of length 9 to store integers plus a name in front, apparently I'm going to be expanding the save files to hold a total of about an hundred variables of different types per save file, and I don't want to have to do that and try to parse different class objects entirely from the same file.
You might want to look at this thread for an example on how to use boost for this:
http://www.daniweb.com/software-development/cpp/threads/74944
Okay, using boost now, but this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace boost::filesystem;
using namespace std;

void show_files(const path & directory, bool recurse_into_subdirs = true){
	if(exists(directory)){
		directory_iterator end;
		for(directory_iterator iter(directory); iter != end; ++iter ){
			if (is_directory(*iter)){
				cout << iter->native_directory_string() << " (directory)\n";
				if(recurse_into_subdirs){show_files(*iter);}
			}
			else cout << iter->native_file_string() << " (file)\n";
		}
	}
}
      
int main(){
	show_files("/home/*****/Documents/C & C++/*****'s Projects");
}

and this compile:
g++ -Wall -I/usr/local/include fileOperations.cpp -o a.out /usr/local/lib/libboost_filesystem.a

returns this compile time error:
fileOperations.cpp: In function ‘void show_files(const boost::filesystem3::path&, bool)’:
fileOperations.cpp:13: error: ‘class boost::filesystem3::directory_entry’ has no member named ‘native_directory_string’
fileOperations.cpp:16: error: ‘class boost::filesystem3::directory_entry’ has no member named ‘native_file_string’


I know with boost I'm a very, very basic beginner, but do you have any ideas why this could be happening?

EDIT: Apparently native_directory_string and native_file_string are deprecated. I'll have to research the new methods of finding this information cross-platform.
Last edited on
Okay, this is what I've got so far:
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
#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace boost::filesystem;
using namespace std;

case 4:
	path p("/home/*****/Documents/C & C++/*****'s Projects");
	try{
		if(exists(p) && is_directory(p)){
			typedef vector<path> vec;
			vec v;
			copy(directory_iterator(p), directory_iterator(), back_inserter(v));
			sort(v.begin(), v.end());
			for(vec::const_iterator iter(v.begin()); iter != v.end(); ++iter){
				myOpenFile.open(iter->path().filename());
				it = characters.begin();
				while(!myOpenFile.eof()){
					myOpenFile >> name;
					for(int i = 0; i < 9; i++){
						myOpenFile >> tempInt;
						x.push_back(tempInt);
					}
					temp = new character(name, x);
					cout << "Successfully loaded " << temp->charName << endl;
					characters.push_back(temp);
					x.clear();
				}
				myOpenFile.close();
			}
		}
		else {cout << p << "does not exist.\n";
	}
	catch(const filesystem_error& ex){cout << ex.what() << "\n";}
	break;
Last edited on
The question is, does it work?
Also, there is a bug: x.clear;
Unfortunately, I can't test it until I get home. I realize x.clear; should be x.clear(); Sorry. Here, I'll include the members of the class to whom this switch belongs.
1
2
3
4
5
6
7
8
9
10
11
12
class characterSet {
	vector<int> x;
	list<character*> characters;
	list<character*>::iterator it;
	character *temp;
	string name;
	int selection, dice, tempInt;
	ofstream mySaveFile;
	ifstream myOpenFile;
public:
	void showMenu();
};
Now, using the following code:
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
void characterSet::deleteChars(){
    vector<boost::filesystem::path> v;
    vector<boost::filesystem::path>::iterator iter;
    const boost::filesystem::path p("./save/");
    try{
        if(exists(p) && is_directory(p)){
            copy(boost::filesystem::directory_iterator(p), boost::filesystem::directory_iterator(), back_inserter(v));
            sort(v.begin(), v.end());
            for(iter = v.begin(); iter != v.end(); iter++){
                myOpenFile.open(iter->path().filename()); // Error is on this line
                it = characters.begin();
                while(!myOpenFile.eof()){
                    myOpenFile >> name;
                    for(int i = 0; i < 9; i++){
                        myOpenFile >> tempInt;
                        x.push_back(tempInt);
                    }
                tempChar = new character(name, x);
                cout << "Successfully loaded " << tempChar->charName << endl;
                characters.push_back(tempChar);
                x.clear();
                }
                myOpenFile.close();
            }
        }
        else {cout << p << "does not exist.\n";}
    }
    catch(const boost::filesystem::filesystem_error& ex){
        cout << ex.what() << "\n";
    }
}

I'm getting this error:
/home/*****/Documents/OOP/*****/main.cpp|94|error: invalid use of ‘class boost::filesystem3::path’|

Looking at boost documentation, apparently this is the proper syntax, so I don't get what I'm missing.
bump - Still no luck, and I've been reading the docs and reference material all day.
Topic archived. No new replies allowed.