我想为Wavefront OBJ文件格式,纯文本文件编写解析器。 例子可以在这里看到:people.sc.fsu.edu/~jburkardt/data/obj/diamond.obj。
大多数人使用旧的scanf逐行解析这种格式,但我宁愿一次加载整个文件以减少IO操作次数。有没有办法逐行解析这种缓冲数据?
void ObjModelConditioner::Import(Model& asset)
{
uint8_t* buffer = SyncReadFile(asset.source_file_info());
delete [] buffer;
}
或者最好将整个文件加载到字符串中并尝试解析它?
答案 0 :(得分:4)
过了一会儿似乎我找到了足够(简单)的解决方案。由于我的目标是创建资产调节管道,因此代码必须能够有效地处理大量数据。数据可以一次读入string
,加载后,stringstream
可以使用此字符串进行初始化。
std::string data;
SyncReadFile(asset.source_file_info(), data);
std::stringstream data_stream(data);
std::string line;
然后我只需致电getline()
:
while(std::getline(data_stream, line))
{
std::stringstream line_stream(line);
std::string type_token;
line_stream >> type_token;
if (type_token == "v") {
// Vertex position
Vector3f position;
line_stream >> position.x >> position.y >> position.z;
// ...
}
else if (type_token == "vn") {
// Vertex normal
}
else if (type_token == "vt") {
// Texture coordinates
}
else if (type_token == "f") {
// Face
}
}
答案 1 :(得分:2)
这是一个将char数组拆分为字符串向量的函数(假设每个新字符串以'\ n'符号开头):
#include <iostream>
#include <vector>
std::vector< std::string >split(char * arr)
{
std::string str = arr;
std::vector< std::string >result;
int beg=0, end=0;//begining and end of each line in the array
while( end = str.find( '\n', beg + 1 ) )
{
if(end == -1)
{
result.push_back(str.substr(beg));
break;
}
result.push_back(str.substr(beg, end - beg));
beg = end;
}
return result;
}
以下是用法:
int main()
{
char * a = "asdasdasdasdasd \n asdasdasd \n asdasd";
std::vector< std::string >result = split(a);
}
答案 2 :(得分:1)
如果您在char[]
(或unsigned char[]
)中获得原始数据,并且
你知道它的长度,只写一个输入,没有寻求是非常简单的
支持streambuf
,可让您创建std::istream
并在其上使用std::getline
。请致电:
setg( start, start, start + length );
在构造函数中。 (不需要其他任何东西。)
答案 3 :(得分:1)
这实际上取决于你将如何解析文本。一种方法是简单地将数据读入字符串向量。我假设你已经涵盖了诸如可扩展性/使用内存等问题。
std::vector<std::string> lines;
std::string line;
ifstream file(filename.c_str(), ios_base::in);
while ( getline( file, line ) )
{
lines.push_back( line );
}
file.close();
这会将您的文件缓存在lines
中。接下来你需要通过行
for ( std::vector<std::string>::const_iterator it = lines.begin();
it != lines.end(); ++it)
{
const std::string& line = *it;
if ( line.empty() )
continue;
switch ( line[0] )
{
case 'g':
// Some stuff
break;
case 'v':
// Some stuff
break;
case 'f':
// Some stuff
break;
default:
// Default stuff including '#' (probably nothing)
}
}
当然,这非常简单,主要取决于您对文件的处理方式。
您作为示例提供的文件大小几乎不会导致IO压力(除非您使用的是非常轻量级的设备),但如果您一次读取多个文件,我想它可能是问题。
我认为你关注的是最小化IO,我不确定这个解决方案真的会有多大帮助,因为你要在一个集合上迭代两次。 如果你需要一遍又一遍地继续阅读同一个文件,那么它肯定会加速将文件缓存到内存中但是有一些简单的方法可以做到这一点,比如内存映射文件并使用普通文件访问。如果您真的担心,那么尝试分析这样的解决方案,而不是在从IO读取时直接处理文件。