PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ Files einlesen


rotalever
2009-03-31, 18:02:44
Was gibt es da für elegante Wege um Dateien einzulesen, die Strings enthalten.
In jeder Zeile steht zum Beispiel
Text AAA#Text BBB#Text CCC
wobei # ein Trennzeichen ist. Ich will eigentlich nur Zeile für Zeile jeweils alle Teile (in diesem Fall 3) in Variablen haben. also eine Variable mit "Text AAA", eine mit "TEXT BBB", etc. als Inhalt.

In Python würde ich das zum Beispiel so machen (damit ihr wisst, wie ich es meine:smile:)

tmp = open("datei").readlines()
for line in tmp:
a,b,c = line.strip().split("#")
# mach was mit a,b,c..

, aber wie geht es elegant in C++? Am liebsten kein Boost oder ähnliches. :smile:

Coda
2009-03-31, 18:23:15
http://www.boost.org/doc/libs/1_38_0/doc/html/string_algo.html

Sorry, aber das ist einfach die Antwort. Es gibt keine C++ eigene Funktion dafür. Der Boost-Vorschlag wird aber wohl in C++0x landen, von daher ist es keine schlechte Idee das doch erstmal einzusetzen.

rotalever
2009-03-31, 18:28:48
Sorry, aber das ist einfach die Antwort. Es gibt keine C++ eigene Funktion dafür.
Hmm, okay...

Gast
2009-03-31, 18:44:47
File lesen
http://www.bgsu.edu/departments/compsci/docs/read.html

File splitten
http://www.oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Gast
2009-03-31, 18:48:00
wobei du zuerst nach newline splittest und danach die so gewonnenen zeilen die du in ein datenobjekt deiner wahl schauffelst (vektor!?) mit deinem trennzeichen abermals splittest.

was der poster ueber mir meinte, dass es sowas in c++ nicht gibt, weiss ich nicht. gibt es wohl fuer ihn nicht...

Coda
2009-03-31, 18:51:31
Natürlich kann man sich sowas selber schreiben, ich rede von der Standard-Library, die in C++ ganz sicher keine Split-Funktion hat.

Ich halte es für eine gute Idee etwas zu verwenden was später eh im Standard ist anstatt sich selber was zusammen zu pfuschen.

rotalever
2009-03-31, 18:57:04
Natürlich kann man sich sowas selber schreiben, ich rede von der Standard-Library, die in C++ ganz sicher keine Split-Funktion hat.

Ich halte es für eine gute Idee etwas zu verwenden was später eh im Standard ist anstatt sich selber was zusammen zu pfuschen.
Generell ja, später wird dieses Programm aber eh nicht mehr verwendet...

@Gast: Werd ich mal anschauen, danke.

ScottManDeath
2009-03-31, 19:00:04
So

template<typename T>
std::vector<T> Tokenize(const std::string& line, const std::string& separators = " \t")
{
const boost::char_separator<char> sep(separators.c_str());
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
std::vector<T> result;

const tokenizer tok(line, sep);
for(tokenizer::const_iterator beg =tok.begin(); beg!=tok.end();++beg)
{
try
{
result.push_back(boost::lexical_cast<T>(*beg));
}
catch(boost::bad_lexical_cast& )
{
result.push_back(T());
}
}

return result;

}


oder so

#include "Precompiled.h"

#include "MeshReaderWavefrontObjFile.h"
#include "MaterialReaderWavefrontMtlFile.h"
#include <Shared/FileIO/ResourceLocator.h>
Mesh MeshReaderWavefrontObjFile::Read( const boost::filesystem::path& path ) const
{
PrintMember();

using namespace Shared::Math;
using namespace boost::algorithm;

const boost::filesystem::path located_obj_file = Shared::FileIO::ResourceLocator::FindFile(path);

std::ifstream obj_file ( located_obj_file.file_string().c_str());
std::string raw_line = "";

std::vector<Shared::Math::Vector3<float> > positions;
std::vector<Shared::Math::Vector3<float> > normals;
std::vector<Shared::Math::Vector2<float> > texture_coordinates;

std::vector<Group> groups;
std::vector<VertexIndex> current_group_triangle_indices;

MaterialReaderWavefrontMtlFile material_reader;

std::map<std::string, MaterialDescription> material_library;

MaterialDescription current_material;

Group current_group;
current_group.Name("default");

while (std::getline(obj_file, raw_line))
{
const std::string line = trim_copy(raw_line);

if(starts_with(line,"#"))
continue;
if(line.empty())
continue;

std::vector<std::string> line_tokens;
split(line_tokens,line,is_space());

if(line_tokens.empty())
continue;


// separate the commend and the rest of the tokens
const std::string cur_command = line_tokens[0];
line_tokens = std::vector<std::string> ( line_tokens.begin() + 1, line_tokens.end());

if("mtllib" == cur_command)
{
const boost::filesystem::path material_library_name = line_tokens[0];
const std::vector<MaterialDescription> materials = material_reader.Read( Shared::FileIO::ResourceLocator::FindFileRelative(material_library_name,located_ obj_file));

for(size_t i = 0; i < materials.size(); ++i)
{
material_library[materials[i].Name] = materials[i];
}
}
else if ("usemtl" == cur_command)
{
//Print(raw_line.c_str());
const std::string material_name = line_tokens[0];
const std::map<std::string,MaterialDescription>::const_iterator it = material_library.find(material_name);
if( material_library.end() == it)
{
current_material = MaterialDescription();
Print("material %s not in material library", material_name.c_str());
}
else
{
current_material = it->second;
}
}
else if("g" == cur_command)
{
//Print(raw_line.c_str());
// the group can contain spaces, so remove the command character 'g' from it and take the rest as the group name
const std::string group_name = trim_copy(line.substr(1));


// handle the case where triangles have been added to the default group which is active before the first "g" command
if(!current_group_triangle_indices.empty())
{
current_group.TriangleIndices(current_group_triangle_indices);
current_group.Material(current_material);
groups.push_back(current_group);

current_group = Group();
current_group_triangle_indices.clear();

//Print("group \"%s\" added",group_name.c_str());
}


current_group.Name(group_name);

}
else if("v" == cur_command)
{
const float x = boost::lexical_cast<float>(line_tokens[0]);
const float y = boost::lexical_cast<float>(line_tokens[1]);
const float z = boost::lexical_cast<float>(line_tokens[2]);

positions.push_back(Vector3<float>(x,y,z));

}
else if ("vn" == cur_command)
{
const float x = boost::lexical_cast<float>(line_tokens[0]);
const float y = boost::lexical_cast<float>(line_tokens[1]);
const float z = boost::lexical_cast<float>(line_tokens[2]);

normals.push_back(Vector3<float>(x,y,z));

}
else if ("vt" == cur_command)
{
const float x = boost::lexical_cast<float>(line_tokens[0]);
const float y = boost::lexical_cast<float>(line_tokens[1]);

texture_coordinates.push_back(Vector2<float>(x,y));

}
else if ("f" == cur_command)
{
const size_t num_faces = 3;
std::vector<std::string> faces (line_tokens.begin(), line_tokens.begin() + num_faces);


for(size_t i = 0; i < faces.size(); ++i)
{
std::vector<std::string> face_tokens;
split(face_tokens,faces[i],is_any_of("/"));

const size_t vertex_index = boost::lexical_cast<size_t>(face_tokens[0]);
const size_t texture_coordinate_index = face_tokens[1].empty() ? ~0 : boost::lexical_cast<size_t>(face_tokens[1]);
const size_t normal_index= boost::lexical_cast<size_t>(face_tokens[2]);

current_group_triangle_indices.push_back
(
VertexIndex
(
vertex_index - 1,
normal_index - 1,
texture_coordinate_index != ~0 ? texture_coordinate_index - 1 : ~0,
current_material
)
);

}
}
else
{
Trace("skipping line \"%s\" with unknown command \"%s\"",raw_line.c_str(),cur_command.c_str());
}
}

// add the last group since it does not have a "g" command
current_group.TriangleIndices(current_group_triangle_indices);
groups.push_back(current_group);

watch.Stop();

size_t num_indices = 0;
for(size_t i = 0; i < groups.size(); ++i)
{
num_indices += groups[i].TriangleIndices().size();
}
return Mesh(positions,normals,texture_coordinates,groups);

}


Alternativ kann man sich auch mit strtok selbst vergewaltigen :rolleyes:

Wobei mir auffaellt das split und tokenize irgendwie das Gleiche machen. Muss mal Refactoring machen =)

Tesseract
2009-03-31, 20:15:02
du könntest mit fstream die datei einlesen und dann einfach eine kleine schleife bauen, die die strings zwischen den delimitern ausliest. sollte nicht all zu komplex sein.

Gast
2009-04-01, 12:08:27
Coda loescht hier immer posts die ihm nicht in den kram passen. sehr gross...

auf jeden fall gibt es sehr wohl split in der standard lib und der c++ code da oben ist fuer n arsch!

ShadowXX
2009-04-01, 12:40:38
Coda loescht hier immer posts die ihm nicht in den kram passen. sehr gross...

auf jeden fall gibt es sehr wohl split in der standard lib und der c++ code da oben ist fuer n arsch!
Jetzt kommt es darauf an inwieweit split und tokenize auseinanderliegen....tokenize gibt es tatsächlich in der Standard-C Lib:
char *strtok(char *strToken, const char *strDelimit);


char string[] = "A string\tof ,,tokens\nand some more tokens";
char seps[] = " ,\t\n";
char *token;

int main( void )
{
printf( "Tokens:\n" );

// Establish string and get the first token:
token = strtok( string, seps );

while( token != NULL )
{
// While there are tokens in "string"
printf( " %s\n", token );

// Get next token:
token = strtok( NULL, seps );
}
}

Ausgabe dann:

Tokens:
A
string
of
tokens
and
some
more
tokens

The_Invisible
2009-04-01, 13:15:59
ich hab das mitn NULL Pointer und strtok() nie kapiert.

gibts da ne static variable die bei übergabe von NULL nicht überschrieben wird oder so?

wenn mir wer die erlösung geben will, bitteschön :D

mfg

Gast
2009-04-01, 13:28:06
ich hab das mitn NULL Pointer und strtok() nie kapiert.

gibts da ne static variable die bei übergabe von NULL nicht überschrieben wird oder so?

wenn mir wer die erlösung geben will, bitteschön :D

mfg

keine ahnung, was die funktion macht, aber es wird lediglich der function als erster parameter der Wert NULL uebergeben...

ShadowXX
2009-04-01, 13:42:53
ich hab das mitn NULL Pointer und strtok() nie kapiert.

gibts da ne static variable die bei übergabe von NULL nicht überschrieben wird oder so?

wenn mir wer die erlösung geben will, bitteschön :D

mfg
Das hier ist die Lösung:

On the first call to strtok, the function skips leading delimiters and returns a pointer to the first token in strToken, terminating the token with a null character. More tokens can be broken out of the remainder of strToken by a series of calls to strtok. Each call to strtokmodifies strToken by inserting a null character after the token returned by that call. To read the next token from strToken, call strtok with a NULL value for the strToken argument. The NULL strToken argument causes strtok to search for the next token in the modified strToken. The strDelimit argument can take any value from one call to the next so that the set of delimiters may vary.

In Kurz: Du übergibst NULL ab dem zweiten Aufruf, damit er weiter in dem String sucht den du beim ersten Aufruf übergeben hat.

Da ist dann sowas in der Art im Code von strtok:

char *strtok(char *strToken, const char *strDelimit);
{
.
.
if(strToken == NULL)
{
-> benutze alten string ab der stelle weiter wo letztes mal aufgehört wurde, falls das ende des stings erreicht wurde gib NULL zurück
}
else
{
-> benutze einen neuen string
}
.
.
.
}