Sign in to follow this  
the_cyberlord

(unsolved) iostreams. Reading strings separated by spaces and returns AND SLASHES.

Recommended Posts

How can you read strings, for example into an array, separated by spaces,returns and slashes from a file, with iostreams? Thanks in advance, Adriaan [Edited by - the_cyberlord on November 13, 2004 10:22:10 AM]

Share this post


Link to post
Share on other sites
example.txt
I like pie.
C:\DOS
C:\DOS\RUN
RUN\DOS\RUN


string_at_a_time.cpp

#include <fstream>
#include <vector>
#include <string>

std::vector< std::string > results;
std::ifstream file;

int main ( void )
{
file.open("example.txt"); //note: example.txt must be in the current working directory for this to work, which may not happen in your IDE environment
while (file.good())
{
std::string temp;
file >> temp;
results.push_back(temp);
}
//results[0] == "I"
//results[1] == "like"
//results[2] == "pie."
//results[3] == "C:\\DOS"
//results[4] == "C:\\DOS\\RUN"
//results[5] == "RUN\\DOS\\RUN"
}


line_at_a_time.cpp

#include <fstream>
#include <vector>
#include <string>

std::vector< std::string > results;
std::ifstream file;

int main ( void )
{
file.open("example.txt"); //note: example.txt must be in the current working directory for this to work, which may not happen in your IDE environment
while (file.good())
{
std::string temp;
readline(file,temp);
results.push_back(temp);
}
//results[0] == "I like pie."
//results[1] == "C:\\DOS"
//results[2] == "C:\\DOS\\RUN"
//results[3] == "RUN\\DOS\\RUN"
}

Share this post


Link to post
Share on other sites
I managed to read strings by myself, but I need to separate in slashes too...


ifstream f_in;
f_in.open("man1.obj",ios_base::in);
while (!f_in.eof())
{
string f;
f_in >> f;
cout << f << endl;
}

Share this post


Link to post
Share on other sites
In fact, that doesn't solve my question, because it parses strings. I mean, if a file iostream can get strings separated by spaces and newlines, why isn't it posible to just add one there, and also make it separate on slashes?

Many thanks in advance!
Adriaan

Share this post


Link to post
Share on other sites

ifstream inf("man1.obj");
string name;
while(!getline(inf, name, '|').eof())
{
string inputstring;
getline(inf, inputstring, '/n');

}

this code gives these errors:

C:\Documents and Settings\Lord Myth\My Documents\3D\main.cpp(42) : error C2784: 'class std::basic_istream<_E,_Tr> &__cdecl std::getline(class std::basic_istream<_E,_Tr> &,class std::basic_string<_E,_Tr,_A> &,const _E)' : could not deduce template ar
gument for 'class std::basic_istream<_E,_Tr> &' from 'class std::basic_ifstream<char,struct std::char_traits<char> >'
C:\Documents and Settings\Lord Myth\My Documents\3D\main.cpp(42) : error C2782: 'class std::basic_istream<_E,_Tr> &__cdecl std::getline(class std::basic_istream<_E,_Tr> &,class std::basic_string<_E,_Tr,_A> &,const _E)' : template parameter '_E' is a
mbiguous
could be 'int'
or 'char'
C:\Documents and Settings\Lord Myth\My Documents\3D\main.cpp(42) : error C2780: 'class std::basic_istream<_E,_Tr> &__cdecl std::getline(class std::basic_istream<_E,_Tr> &,class std::basic_string<_E,_Tr,_A> &)' : expects 2 arguments - 3 provided
c:\microsoft visual studio\vc98\include\string(145) : see declaration of 'getline'


And BTW, you can't enter multiple separators like McCloud said, it gives an error. So what do I do???

Share this post


Link to post
Share on other sites
/n should be \n; as is, you're giving two characters - a forward slash and a lowercase n - which as you've already figured out is non-kosher.

If you have control over the file format, you might want to redesign it so that you don't have to worry about having several different separators. Otherwise you are stuck with either parsing things twice (once to read lines and again to split up individual lines using stringstreams), or reading a character at a time (and checking each time for each separating character; i.e. doing it all manually).

Share this post


Link to post
Share on other sites
Quote:
Original post by the_cyberlord
*** Source Snippet Removed ***
this code gives these errors:
*** Source Snippet Removed ***
And BTW, you can't enter multiple separators like McCloud said, it gives an error. So what do I do???

The multiple separators is for find_first_of, getline only wants one at a time. If you have a set format [so that you know which separator is next at any given time], getline should work. I've never figured out a solution to the problem where the odd separators can occur anywhere except to parse a string rather than a file. Traditionally, I've used strtok, but that function is pure, unadulterated, evil. Don't even consider that option.

I suspect that fixing the newline issue ['/n' rather than '\n'] will fix the problem you're having. The compiler appears confused as to what type '/n' is, but since its not a type at all that's not surprising.

CM

Share this post


Link to post
Share on other sites
If you wonted to keep the same file format you can try this:


#include <cstddef>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>
#include <vector>
#include <fstream>
#include <iostream>

int main(int argc, char* argv[]) {

typedef std::istreambuf_iterator<char> isbufiter;
typedef std::istream_iterator<std::string> issitr;
typedef std::ostream_iterator<std::string> ossitr;

if(argc == 1) {
std::cerr << argv[0] << " error usage: " << argv[0] << " <file-name>\n";
return EXIT_FAILURE;
}

std::ifstream ifs(argv[1]);

if(!ifs) {
std::cerr << argv[0] << " error could not open file\n";
return EXIT_FAILURE;
}

std::string buf;

std::replace_copy(isbufiter(ifs), isbufiter(), std::back_inserter(buf), '\\', ' ');

std::vector<std::string> vs;
std::istringstream iss(buf);

std::copy(issitr(iss), issitr(), std::back_inserter(vs));

std::copy(vs.begin(), vs.end(), ossitr(std::cout, ", "));

return EXIT_SUCCESS;
}


MaulingMonkey's input file:

I like pie.
C:\DOS
C:\DOS\RUN
RUN\DOS\RUN


output:

I, like, pie., C:, DOS, C:, DOS, RUN, RUN, DOS, RUN,

Share this post


Link to post
Share on other sites
Well, you could use boost. Syntax will depend on exact results desired. Here's the evil version I came up with which should read my example, seperating by:
1) Whitespace
2) \ style slashes
3) using Boost, which alas isn't iostreams, but it's somewhat related (and actually, although I'm not sure, the file iterators may indeed be using iostreams under the hood).

#include <boost/spirit.hpp>
#include <boost/spirit/phoenix/binders.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/format.hpp>
#include <string>
#include <vector>
#include <iostream>
using namespace boost;
using namespace boost::spirit;
using namespace phoenix;
using namespace std;

struct config_file_grammar : public grammar<config_file_grammar>
{
mutable std::vector< std::string > & results;

config_file_grammar( std::vector< std::string > & results ) : results(results) {}

void add_result( const std::string & result ) const { results.push_back( result ); }

template < typename ScannerT > struct definition
{
definition( config_file_grammar const& self )
{
optional_padding //this rule reads 0+ (*) whitespace (space_p).
= *space_p
;
seperator //this rule reads either optional padding, then(>>) a backslash('\\'), and then(>>) more optional padding, OR (|) 1+ (+) whitespace (space_p).
= ( optional_padding
>> '\\'
>> optional_padding
)
| +space_p
;
string_entry //this rule reads 1+ (+) characters of any value (anychar_p) that arn't (-) whitespace (space_p) or (|) backslashes('\\')
= ( +( anychar_p
- ( space_p
| '\\'
)
)
) [ bind( &config_file_grammar::add_result )(self,construct_<std::string>(arg1,arg2)) ] //all of which we then create a string with, which we then call config_file_grammar::add_result with.
;
top
= lexeme_d //this prevents phrase-level parsing, I currently prefer to work allways a character at a time.
[ optional_padding //we read optional padding, followed (>>) by...
>> ( string_entry //wait for it...
% seperator //...a LIST of string_entrys seperated with seperators (string_entry % seperator)
)
>> optional_padding //ending in optional padding...
>> end_p //we don't really need this here, but this reads 0 characters, but only succeeds if we've reached the end of the input.
]
;
}
rule<ScannerT> top //we now have to declare all those rules we made up
, optional_padding
, seperator
, string_entry
;

rule<ScannerT> const& start() const { return top; } //and the official starting rule must be provided
};
};

bool read_config_file( const std::string & filename , std::vector< std::string > & results )
{
file_iterator<> first( filename.c_str() );
if (!first)
{
std::cerr << format("File: %1% does not exist") % filename << std::endl;
return false;
}
file_iterator<> last = first.make_end();

if (!parse( first , last , config_file_grammar( results ) ).full) return false;

return true;
}

int main ( int argc , char ** argv )
{
std::string filename = "C:\\eclipse\\workspace\\test\\example.txt";
std::vector< std::string > results;
if (!read_config_file( filename , results ))
{
std::cerr << format("Error(s) in file: %1%.") % filename << std::endl;
}
std::cout << "What was read: " << std::endl;
for ( int i = 0 ; i < results.size() ; ++i )
{
std::cout << format("String %1% == %2%") % i % results[i] << std::endl;
}
std::cin.get();
return 0;
}



Which results in:
What was read:
String 0 == I
String 1 == like
String 2 == pie.
String 3 == C:
String 4 == DOS
String 5 == C:
String 6 == DOS
String 7 == RUN
String 8 == RUN
String 9 == DOS
String 10 == RUN

Share this post


Link to post
Share on other sites
ehm is it possible to convert strings to ints?
Because this code gives an error:

while (!f_in.eof())
{
f_in>> input;
//cout << input << endl;
while (input == "v") {
f_in >> value1; f_in >> value2; f_in >> value3;
coords temp(value1,value2,value3);
vertexdata.push_back(temp);
f_in >> input;}
while (input == "vt") {
f_in >> value1; f_in >> value2;
coords2D temp(value1,value2);
texturedata.push_back(temp);
f_in >> input;}
while (input == "vn") {
f_in >> value1; f_in >> value2; f_in >> value3;
coords temp(value1,value2,value3);
normaldata.push_back(temp);
f_in >> input;
cout << input<<endl;}
//FROM HERE, THE TOPIC PROBLEM STARTS
while (input == "f") {
f_in >> input;
while (fi < 3) {
GLint temp[3][3];
string tempstring;
cout << input <<endl;
GLint n = input.length();
GLint start, stop;
start = input.find_first_not_of("/");
while (vi < 3) {
stop = input.find_first_of("/", start);
if ((stop < 0) || (stop > n)) stop = n;
//this causes the error //this causes the error
temp[fi][vi] = input.substr(start, (stop - start));
cout << tempstring <<endl;
start = input.find_first_not_of("/", stop+1);}vi++;}fi++;}}


and this is the error:

C:\Documents and Settings\Lord Myth\My Documents\3D\main.cpp(72) : error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >' (or t
here is no acceptable conversion)

Share this post


Link to post
Share on other sites
Quote:
Original post by the_cyberlord
ehm is it possible to convert strings to ints?


The I/O streams already parse text to other types, if your trying to parse strings in memory to a particular type then use string streams i.e. std::istringstream/std::stringstream.

Post us the file format or example file your trying to read in, i will or someone else will post code.

Share this post


Link to post
Share on other sites
OK many thanks: here's the file format:

#
# Wavefront OBJ file
# Converted by the DEEP Exploration 3.5.7.1088
# Right Hemisphere, LTD
# http://www.righthemisphere.com/
#
# object Plane01
g Plane01
v -0.5 -0.5 0
v 0.5 -0.5 0
v -0.5 0.5 0
v 0.5 0.5 0
# 4 verticies
vt 0 1
vt 0 0
vt 1 1
vt 1 0
# 4 texture verticies
vn 0 0 1
# 1 normals
f 3/1/1 1/2/1 4/3/1
f 2/4/1 4/3/1 1/2/1
# 2 faces
// faces are stored like this: v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3


Share this post


Link to post
Share on other sites
Since your slash-separated data consists of text representations of numbers, you can read those items directly into an appropriate numeric value. The >> operator for cin is overloaded according to the type of the variable you're reading into, so that this works the way you want it to. Example:


std::string line_type;
float values[3];
char ignored;
// At the beginning of this loop, we're always at the beginning
// of a line.
while(cin >> line_type) {
// Don't nest while loops here; it just obfuscates things.
// Ideally you should build a look-up table mapping the string
// to an action to take. Look up std::map and functors.
if (line_type == "f") {
// Read in three vertices which are separated by '/' chars
// This is quite fragile and depends on your input being good.
// For more ideas, see
// http://www.augustcouncil.com/~tgibson/tutorial/iotips.html
for (int i = 0; i < 3; i++) {
cin >> values[0] >> ignored >> values[1] >> ignored >> values[2];
make_coord_with(values);
}
}
// Handle other cases similarly
// Do a std::getline and throw it away, to ignore garbage
// at end of line (optional)
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this