我正在为.obj文件编写一个解析器,并且该文件的一部分格式为
f [int] / [int] [int] / [int] [int] / [int]
并且整数长度未知。在每个[int] / [int]对中,它们都需要放在单独的数组中。将它们作为整数分离的最简单方法是什么?
答案 0 :(得分:2)
如果您使用< stdio.h>和scanf
或fscanf
来解析一行,请考虑使用其中一个FILE*
函数(sscanf
在内存缓冲区)。
所以,如果你有一个包含数据的缓冲区和两个这样的整数数组:
int first[3], second[3];
char *buffer = "f 10/20 1/300 344/2";
然后你可以写:
sscanf(buffer, "f %d/%d %d/%d %d/%d",
&first[0], &second[0], &first[1], &second[1], &first[2], &second[2]);
(sscanf
的输入模式中的空格不是必需的,因为%d
会跳过空格,但它们会提高可读性。)
如果您需要进行错误检查,请分析sscanf
的结果:此函数返回成功输入的值的数量(如果一切正确,则返回此示例中的6
。)
答案 1 :(得分:2)
你可以用fscanf:
来做int matched = fscanf(fptr, "f %d/%d %d/%d %d/%d", &a, &b, &c, &d, &e, &f);
if (matched != 6) fail();
或ifstream和sscanf:
char buf[100];
yourIfstream.getLine(buf, sizeof(buf));
int matched = sscanf(buf, "f %d/%d %d/%d %d/%d", &a, &b, &c, &d, &e, &f);
if (matched != 6) fail();
答案 2 :(得分:0)
#include <stdlib.h>
long int strtol(const char *nptr, char **endptr, int base);
long long int strtoll(const char *nptr, char **endptr, int base);
strtol
函数将解析输入中的整数,并返回整数在字符串中结束的位置。您可以像
char *input = "f 123/234 234/345 345/456"
char *c = input;
char *endptr;
if (*c++ != 'f') fail();
if (*c++ != ' ') fail();
long l1 = strtol(c, &endptr, 10);
if (l1 < 0) fail(); /* you expect them unsigned, right? */
if (endptr == c) fail();
if (*endptr != '/') fail();
c = endptr+1;
...
答案 3 :(得分:0)
我会使用正则表达式。如果你有一个C ++ 11兼容的编译器,你可以使用,否则你可以查看boost :: regex。在类似Perl的语法中,正则表达式模式看起来像这样:f ([0-9]+)/([0-9]+) ([0-9]+)/([0-9]+) ([0-9]+)/([0-9]+)
。然后依次获取子匹配(在parathesis中的内容)并将它们从string或char *转换为带有istringstream的整数。
答案 4 :(得分:0)
最简单的方法是使用C ++ 11正则表达式:
static const std::regex ex("f (-?\\d+)//(-?\\d+) (-?\\d+)//(-?\\d+) (-?\\d+)//(-?\\d+)");
std::smatch match;
if(!std::regex_match(line, match, ex))
throw std::runtime_error("invalid face data");
int v0 = std::stoi(match[1]), t0 = std::stoi(match[2]),
v1 = std::stoi(match[3]), t1 = std::stoi(match[4]),
v2 = std::stoi(match[5]), t2 = std::stoi(match[6]);
虽然这可能足以满足您的需求,但我无法帮助您添加更灵活的方法来阅读这些索引元组,从而更好地处理非三角形面和不同的面规格式。为此,我们假设您已将面线放入std::istringstream
并且已经吃掉了面部标记。这通常是这种情况,因为读取OBJ文件的最简单方法仍然是:
for(std::string line,tag; std::getline(file, line); )
{
std::istringstream sline(line);
sline >> tag;
if(tag == "v")
...
else if(tag == "f")
...
}
现在要读取面部数据(当然在"f"
的情况下),我们首先分别读取每个索引元组。然后我们只使用每个可能的索引格式的正则表达式解析这个索引并适当地处理它们,在3个元素std::tuple
中返回单个顶点,texcoord和普通索引:
for(std::string corner; sline>>corner; )
{
static const std::regex vtn_ex("(-?\\d+)/(-?\\d+)/(-?\\d+)");
static const std::regex vn_ex("(-?\\d+)//(-?\\d+)");
static const std::regex vt_ex("(-?\\d+)/(-?\\d+)/?");
std::smatch match;
std::tuple<int,int,int> idx;
if(std::regex_match(corner, match, vtn_ex))
idx = std::make_tuple(std::stoi(match[1]),
std::stoi(match[2]), std::stoi(match[3]));
else if(std::regex_match(corner, match, vn_ex))
idx = std::make_tuple(std::stoi(match[1]), 0, std::stoi(match[2]));
else if(std::regex_match(corner, match, vt_ex))
idx = std::make_tuple(std::stoi(match[1]), std::stoi(match[2]), 0);
else
idx = std::make_tuple(std::stoi(str), 0, 0);
//do whatever you want with the indices in std::get<...>(idx)
};
当然,这为性能引导的优化提供了可能性(如果需要),例如无需在每次循环迭代中分配新的字符串和流。但这是提供适当的OBJ加载器所需的灵活性的最简单方法。但也可能只有顶点和texcoords三角形的上述版本已经足够了。