Jump to content

  • Log In with Google      Sign In   
  • Create Account

c++ file reading


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 BaneTrapper   Members   -  Reputation: 1200

Like
0Likes
Like

Posted 19 August 2012 - 09:17 AM

Hello.
I was using to read data from my files like this
/*text.txt*/
3
./Files/Maps/Map1.txt
./Files/Maps/Map2.txt
./Files/Maps/Map3.txt
FILE* tempFile = fopen("text.txt", "r");
if(tempFile == NULL)
{
    fclose(tempFile);
}
fscanf(tempFile, "%d", &MyInt);
for(int x = 0; x < MyInt; x++)
{
    char tempChar[60];
    fscanf(tempFile, "%s\n", tempChar);
    MyVector.push_back(tempChar);
}

But its really bugging me that i am limited to "char" cause the fscanf "%s" cant store to string.

What libraries can i use to read data from .txt files to string directly , and hopefully are simple to use

Current projects:
The Wanderer, 2d turn based rpg style game

www.gamedev.net/topic/641117-check-up-the-wanderer/


Sponsor:

#2 SiCrane   Moderators   -  Reputation: 9596

Like
10Likes
Like

Posted 19 August 2012 - 09:31 AM

Well you could use the iostream library.
  std::ifstream file("text.txt");
  std::vector<std::string> lines;
  while (file) {
    std::string line;
    std::getline(file, line);
    lines.push_back(line);
  }  


#3 Cornstalks   Crossbones+   -  Reputation: 6989

Like
6Likes
Like

Posted 19 August 2012 - 09:41 AM

Note that if you use the method SiCrane suggested (which I would if I were you), you need to include <fstream> for std::ifstream, and <string> for std::string and std::getline().

The way you're currently doing it is the C way. SiCrane's way is the C++ way.
[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#4 Serapth   Crossbones+   -  Reputation: 5465

Like
3Likes
Like

Posted 19 August 2012 - 10:03 AM

Too slow, I was about to propose:

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;
int main(int argc, char** argv)
{
ifstream stream;
stream.open("level.txt");
int numFiles;
stream >> numFiles;
cout << numFiles;
vector<string> results;
while(stream)
{
  string str;
  getline(stream,str);
  if(str != "") // Handle empty newlines
   results.push_back(str);
}
std::for_each(results.begin(),results.end(), [](string value) { cout << value << endl; });
return 0;
}


Wow, the C++ formatter is seriously broken...

Edited by Serapth, 19 August 2012 - 10:04 AM.


#5 Tudor Nita   Members   -  Reputation: 121

Like
-1Likes
Like

Posted 20 August 2012 - 05:38 AM

I wouldn't use iostream. Something like this works for me. Imagine an input file like:

# a comment
a 3.5 3.5 yay_some_text
a 3.2 4.5 yay_some_more_text

[source lang="java"]FILE *pFile = fopen(filePath, "r"); // the file to loadchar buffer[512]; // character bufferchar temp;// read each line into the bufferwhile (fgets(buffer, sizeof(buffer), pFile)){ // switch based on leading type switch (buffer[0]) { case '#': // do nothing, it's a comment [img]http://public.gamedev.net//public/style_emoticons/default/biggrin.png[/img] break; case 'a': // set ambient color ? sscanf(buffer, "%c %f %f %s", &temp, &someFloat, &someOtherFloat, &aString); break; }}[/source]

Edited by Tudor Nita, 20 August 2012 - 05:39 AM.


#6 Bregma   Crossbones+   -  Reputation: 5133

Like
0Likes
Like

Posted 20 August 2012 - 05:42 AM

I wouldn't use iostream.

Can you expand on that?
Stephen M. Webb
Professional Free Software Developer

#7 Tudor Nita   Members   -  Reputation: 121

Like
0Likes
Like

Posted 20 August 2012 - 05:55 AM

Just personal preference I guess. It's a bit verbose for my liking and enforces type safety at compile time. This nags at me a bit too: http://stackoverflow...answer-5165293

Seeing how I'm really used to the fgets way, this is all, of course, highly subjective. Still, it's a valid alternative.

Edited by Tudor Nita, 20 August 2012 - 05:59 AM.


#8 BitMaster   Crossbones+   -  Reputation: 4083

Like
3Likes
Like

Posted 20 August 2012 - 06:09 AM

Type safety is a good thing. Actually it is a great thing.

And while it's true that the iostream classes can be a bit on the slow side on a lot of implementations, I would not replace them with something handrolled like that. If performance of text file parsing is an issue (in a lot of cases it is not), I would rather use a proper lexer generator. It will usually be just as fast, much easier to maintain and much safer.

#9 SiCrane   Moderators   -  Reputation: 9596

Like
1Likes
Like

Posted 20 August 2012 - 06:27 AM

Two additional things: 1) the stdio API becomes just as verbose as iostreams, if not more so, when you actually do error handling. 2) your code doesn't actually address the OPs original complaint: not being able to work with string objects directly.

#10 Hodgman   Moderators   -  Reputation: 30384

Like
1Likes
Like

Posted 20 August 2012 - 07:11 AM

<br />This nags at me a bit too: http://stackoverflow...answer-5165293<br />

N.B. If you want to avoid the formatting overhead of fstream, you can shed one layer of abstraction by accessing it's internal filebuf object, though obviously you're now responsible for doing some of the work that the stream used to do for you.

#11 SIC Games   Members   -  Reputation: 617

Like
-1Likes
Like

Posted 20 August 2012 - 12:29 PM

If this could help some then cool! However, since you're working in text files - let's say for instance you have:

Level TItle: The Doom Room
Models: 25
Textures: 1000
....

if you're using fstream you can do:

char input;
fin = file.open("map1.txt");

fin.get(input);
while(input != ":") {
fin.get(input);
}
 
fin >> MapName;

fin.get(input);
while(input !=":") {
...
}


This is just a suggestion what I used previously for my model file until I converted the file to binary. Just throwing it out there. My example is pretty straightforward and easy. I'm sure others may have preffered ways of reading a text file.

Game Engine's WIP Videos - http://www.youtube.com/sicgames88


#12 Exessuz   Members   -  Reputation: 141

Like
0Likes
Like

Posted 23 August 2012 - 01:23 PM

try with something like this.

a 3.5 3.5 yay_some_text
a 3.2 4.5 yay_some_more_text

ifstream f;
f.open(file);
char dat[256];
for(;;){
	 if(!f){
	    break
	 }
	 f >> dat;
    if(strcmp(dat,"a") == 0){
		   float x, y;
		   char txt[256];
		   f >> x >> y >> txt;	 
    }
}


#13 SimonForsman   Crossbones+   -  Reputation: 6109

Like
2Likes
Like

Posted 23 August 2012 - 02:22 PM

try with something like this.

a 3.5 3.5 yay_some_text
a 3.2 4.5 yay_some_more_text

ifstream f;
f.open(file);
char dat[256];
for(;;){
	 if(!f){
		break
	 }
	 f >> dat;
	if(strcmp(dat,"a") == 0){
		   float x, y;
		   char txt[256];
		   f >> x >> y >> txt;	
	}
}


That code risks causing a buffer overflow though, you could do: f >> setw(256) >> dat to ensure it doesn't happen when you read data. (or just use c++ strings)

strcmp is also potentially dangerous if dat isn't null terminated. (This is exactly why one should use the C++ string and stream objects rather than the C functions)

Edited by SimonForsman, 23 August 2012 - 02:24 PM.

I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!

#14 Trienco   Crossbones+   -  Reputation: 2172

Like
0Likes
Like

Posted 23 August 2012 - 10:34 PM

This nags at me a bit too: http://stackoverflow...answer-5165293


Things to point out and quote from there would be:

"EDIT #2: I've found that the slow down for me was due to the set insert"

"Which gives a slowdown of a whooping 17%.
This takes into account:
automatic memory management (no buffer overflow)
automatic resources management (no risk to forget to close the file)
handling of locale"

Unless you deal with a huge text file, 17% are nothing compared to what you get in return and once you manually do the equivalent, chances are your own code would be as "slow" or even slower.

Edited by Trienco, 23 August 2012 - 10:35 PM.

f@dzhttp://festini.device-zero.de

#15 BaneTrapper   Members   -  Reputation: 1200

Like
0Likes
Like

Posted 25 August 2012 - 10:58 AM

Guess i should replay my fix ^^.

std::vector<string> MapList; //The vector where to hold what Maps to hold
int HowManyMaps = 0; //Keaping this for later
std::string tempString;

std::fstream tempStream("./Data/Map.txt");
if(tempStream.is_open)
{
	 tempStream >> HowManyMaps;
	 tempStream.ignore(2);
	 for(int a = 0; a < HowManyMaps; a++)
	 {
	 	 tempStream >> tempString;
	 	 tempStream.ignore(1);
	 	 MapList.push_back(tempString);
	 }
}
tempStream.close();

//Exit
for(int x = 0; x < HowManyMaps; x++)
{
	 MapList [x].clear();  //clear the strings
}
MapList.clear();

only problem i got is that i can save file as "Text.gam" then open it with fstream as "Text.gam"
but if i open it with Notepad... It becomes "Text.gam.txt" file =,= so anoing

Edited by BaneTrapper, 25 August 2012 - 10:59 AM.

Current projects:
The Wanderer, 2d turn based rpg style game

www.gamedev.net/topic/641117-check-up-the-wanderer/


#16 Cornstalks   Crossbones+   -  Reputation: 6989

Like
0Likes
Like

Posted 25 August 2012 - 01:38 PM

Guess i should replay my fix ^^.


std::vector<string> MapList; //The vector where to hold what Maps to hold
int HowManyMaps = 0; //Keaping this for later
std::string tempString;

std::fstream tempStream("./Data/Map.txt");
if(tempStream.is_open)
{
	 tempStream >> HowManyMaps;
	 tempStream.ignore(2);
	 for(int a = 0; a < HowManyMaps; a++)
	 {
	 	 tempStream >> tempString;
	 	 tempStream.ignore(1);
	 	 MapList.push_back(tempString);
	 }
}
tempStream.close();

//Exit
for(int x = 0; x < HowManyMaps; x++)
{
	 MapList [x].clear();  //clear the strings
}
MapList.clear();

Some critiques:
  • is_open is a function, not a variable. You need parenthesis after it (right now, you're not actually calling it), like so: is_open()
  • You should just use the function good() instead of is_open(), because good() will check for more errors than is_open().
  • You don't need to manually close() the stream. It will close itself automatically when it goes out of scope. There's nothing wrong with closing it, of course, but it's not necessary.
  • You don't need to manually clear() the strings or the vector. They will automatically be cleared and free their memory when they also go out of scope.
  • What happens if you've got a corrupt file (or someone maliciously edits it) so that HowManyMaps is incorrect? If you're going to loop over all the elements in a vector, use the vector's size() member to make sure you don't go over/under bounds (but here in this case the entire iterating and clearing over the vector is unnecessary, as stated). Also, for(int a = 0; a < HowManyMaps; a++) should also be checking if the tempStream is still good(), because it's possible you're trying to read more maps than the file has (if it's a corrupt/incorrect file), so it should be: for(int a = 0; a < HowManyMaps && tempStream.good(); a++)...

The hard thing about C++ is that because things might "work," but it doesn't mean they're right.

only problem i got is that i can save file as "Text.gam" then open it with fstream as "Text.gam"
but if i open it with Notepad... It becomes "Text.gam.txt" file =,= so anoing

Ditch Notepad and get Notepad++. It's infinitely better.
[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#17 Servant of the Lord   Crossbones+   -  Reputation: 19545

Like
0Likes
Like

Posted 25 August 2012 - 09:10 PM

When I don't care too much about speed, I handle that type of thing in distinct re-usable functions (from a lack of deep familiarity with proper stream usage).

In your case, I might do it like this:
//Load the file into a single string.
std::string fileContents = LoadFileAsString("text.txt");


//Remove comments.
fileContents = String::RemoveComments(fileContents, "//");
fileContents = String::RemoveComments(fileContents, "/*", "*/");

//Seperate the string into lines; this also eliminates empty lines via a callback function.
StringList lines = String::Seperate(fileContents, '\n', RemoveEmptyLines);
if(lines.empty())
{
	 //The file (barring empty lines and comments) is empty!
	 //Handle the error.
}

//Get the first line.
int line = 0;
int numberOfFiles = StringToInt(lines[line++]); //You can also use lines.at() and catch the exception for malformed files.

if(lines.size() > (line + numberOfFiles))
{
	 //Malformed file. Not as many lines as promised by 'numberOfFiles'.
	 //Handle the error.
}

//Find the iterators to the range of files we want.
auto beginningOfFiles = (lines.begin() + line);
auto endOfFiles = (beginningOfFiles + numberOfFiles);

//Copy the range of elements to their own vector.
StringList files;
std::copy(beginningOfFiles, endOfFiles, files.begin());

//Continue processing the file...
line += numberOfFiles;

//...more file processing...

Most of those functions exist already exist in my code base through repeated needs, though RemoveComments() does not (I've never had to deal with multi-line comments before, but that's easy enough to solve - and once solved, becomes a permanent tool in your library, ready to use).

String::Seperate() function. Currently just takes a boolean to check for empty lines, but after writing this post I realize I need a callback function to check for non-empty but whitespace-filled lines. I also have it on my todo list (but as low priority) to make a version for if 'divider' is a single char (which would be alot faster).
StringList is just a typedef for std::vector<std::string> since I use it so often.
	//Divides up a string into multiple segments seperated by 'divider', and returns each segment in a StringList.
	//If any segment is empty, and if 'ignoreEmptySegments' is true, the empty segments are not added to the StringList.
	StringList Seperate(const std::string &str, const std::string &divider, bool ignoreEmptySegments)
	{
		StringList stringList;
		//Check for empty string.
		if(str.empty() || divider.empty())
			return stringList;
		size_t start = 0;
		size_t end = str.find(divider, start);
		//Keep looping, as long as there are more segments.
		while(end != std::string::npos)
		{
			std::string subString = str.substr(start, end - start);
			if(subString.size() > 0 || ignoreEmptySegments == false)
			{
				stringList.push_back(subString);
			}
			start = end + 1;
			end = str.find(divider, start);
		}
		//Get the final (or the only) segment.
		std::string subString = str.substr(start, str.size() - start);
		if(subString.size() > 0 || ignoreEmptySegments == false)
		{
			stringList.push_back(subString);
		}
		return stringList;
	}

LoadFileAsString() function.
//Loads the entire file, newlines and all, and returns its contents as a string.
//Returns an empty string if the file doesn't exist.
std::string LoadFileAsString(const std::string &filename, ReadMode::Enum readMode)
{
	//Open the file for reading.
	std::ifstream file;
	if(readMode == ReadMode::Binary)
		file.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
	else
		file.open(filename.c_str(), std::ios_base::in);
	if(!file)
	{
		Log::Message(MSG_SOURCE("FileFunctions", Log::Severity::Error)) << "Failed to load '" << Log_HighlightCyan(GetFilenameFromPath(filename)) << "' at " << Log_DisplayPath(filename)
																		<< "\nDo I have sufficient privileges? Does the file even exist?" << Log::FlushStream;
		return "";
	}
	//Read the file into the stringStream.
	std::ostringstream stringStream;
	stringStream << file.rdbuf();
	//Convert the stringStream into a regular string.
	return stringStream.str();
}


Observant programmers will notice that most of my code suffers from Shmuel Schlemiel the Painter algorithms. Many of the functions could be individually optimized, and the use of them together could further be optimized into a stand-alone function, like a "LoadFileAsStringList()" since I frequently go from File to String to StringList (easy enough to add).
I typically prefer clean code that works before I start optimizing code, and I don't do the actual optimizations until I find them necessary (otherwise I find myself wasting inordinate amounts of time of things that don't matter much). With file parsing, when I occasionally need files parsed fast, I load it as binary and don't bother with text parsing anyway.

Edited by Servant of the Lord, 25 August 2012 - 09:16 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS