如何将 scanf 精确匹配翻译成现代 c++字符串流读取
我目前正在开发一个项目,我想使用现代 cpp 而不是依赖旧的 c 来读取文件。 对于上下文,我正在尝试读取波前 obj 文件。
我有这个旧的代码片段:
const char *line;
float x, y, z;
if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
break; // quitting loop because couldn't scan line correctly
我已将其翻译为:
string line;
string word;
stringstream ss(line);
float x, y, z;
if (!(ss >> word >> x >> y >> z)) // "vn x y z"
break; // quitting loop because couldn't scan line correctly
不同之处在于我使用 string
跳过第一个单词,但我希望它匹配 "vn" 与 sscanf 相同。 这可以通过
stringstream
实现吗?还是我应该继续依赖 sscanf
进行精确的模式匹配?
另外,我正在尝试翻译
sscanf(line, " %d/%d/%d", &i1, &i2, &i3);
,但我遇到了困难,这再次引导我不使用现代 cpp 作为我的文件阅读器。
I am currenlty working on a project and I'd like to use modern cpp instead of relying on old c for reading files.
For context I'm trying to read wavefront obj files.
I have this old code snippet :
const char *line;
float x, y, z;
if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
break; // quitting loop because couldn't scan line correctly
which I've translated into :
string line;
string word;
stringstream ss(line);
float x, y, z;
if (!(ss >> word >> x >> y >> z)) // "vn x y z"
break; // quitting loop because couldn't scan line correctly
The difference is that I use a string
to skip the first word but I'd like it to match "vn"
the same as sscanf
does.
Is this possible with stringstream
or should I keep relying on sscanf
for exact pattern matching ?
Also I'm trying to translate
sscanf(line, " %d/%d/%d", &i1, &i2, &i3);
but I'm having a hard time which again orients me towards not using modern cpp for my file reader.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我也遇到了这个要求,并为流编写了一个小提取器,可以让您匹配文字。代码如下所示:
在您的情况下,您可以像这样使用它:
正如您从代码中看到的,它关注
skipws
状态,因此它将跳过前导空格当且仅当您设置了skipws。因此,如果您需要匹配包含精确数量的前导空格的模式,则需要关闭skipws
并将这些空格包含在模式中。I've run into this requirement as well, and wrote a little extractor for streams that lets you match literals. The code looks like this:
In your case, you'd use it something like this:
As you can see from the code, it pays attention to the
skipws
state, so it'll skip leading white space if and only if you have skipws set. So if you need to match a pattern that includes a precise number of leading spaces, you want to turnskipws
off, and include those spaces in your pattern.我认为
stringstream
不是“现代 C++”。 (我也承认我们没有很好的替代scanf
的东西;我们确实有std::format
,它非常好,可以替代std::cout <<
恶作剧的语法让人想起Python的格式字符串,而且速度要快得多,遗憾的是,它还没有达到标准库。)我实际上会说我认为你的 。
sscanf
代码比 stringstream 代码更干净、更理智:当解析行失败时,sscanf 使事情处于更容易理解的状态。您可以使用一些库来构建行解析器;
Boost::spirit::qi
可能是最著名的。您可以做这样的事情:如果此选项没有让您充满欢乐、对世界的热爱以及对无所不在的美丽的信念,那么您完全像我并找到它对于原始
scanf
所提供的紧凑性和易于理解性来说,这是一个糟糕的替代品。现在有
std::regex
库(自 C++11 起),这很可能就是您正在寻找的!但是,您需要自己编写“浮点数来了”的表达式,这也一点也不酷。无意伤害任何人,我认为 iostreams 库方法是唯一一个标准库功能,它使得 C++ 在与 IO 相关的任何事情上比大多数当代语言更难以使用。这对代码质量产生持久影响。
I'd argue that
stringstream
is not "modern C++"¹. (I'll also admit we don't have something as a good replacement forscanf
; we do havestd::format
that is very nice, and replacesstd::cout <<
shenanigans with a syntax reminescent of Python's format strings, and is much faster. Sadly, it hasn't hit standard libraries yet.)I'll actually say that I think your
sscanf
code is cleaner, and saner than the stringstream code: sscanf leaves things in an easier-to-understand state when parsing the line fails.There's libraries you can use to build a line parser;
Boost::spirit::qi
is probably the most well-known. You can do things likeIf this option does not fill you with joy, love for the world and faith in all-reaching beauty, you're exactly like me and find it a poor replacement for the compactness and ease of understanding the original
scanf
offers.There's the
std::regex
library these days (since C++11), and that might very well be what you're looking for! However, you'd need to write expressions for "here comes a floating point number" yourself, and that is not cool at all, either.¹ without wanting to hurt anyone, I think the iostreams library approach was the one single standard library feature that made C++ awkwarder to use than most contemporary languages for anything IO-related. And that has a lasting effect on code quality.
对于任何感兴趣的人来说,我如何使用
stringstream
样式而不是sscanf
样式解析 obj 文件旧代码:
新代码:(使用
std 的 @JerryCoffin 代码: :istream& 运算符>>(std::istream& is, char const* s);
)我在这里和那里改变了一些逻辑,但最棘手的部分是
fi/i/ii/i/ii/i/i
行,您必须检查它是否正确匹配。如果没有,您必须将 stringstream 重置为其原始状态和位置(至少删除 std::ios_base::failbit 并替换位置)。我想保留一行 = 一种情况的方面,因此使用 lambda 函数可能有点难以理解,但我也可以反转逻辑并逐边处理面(不确定我是否会使用它) )。
For anyone interested here is how I parsed my obj file with
stringstream
style instead ofsscanf
styleOld code:
New code: (with the @JerryCoffin code for
std::istream& operator>>(std::istream& is, char const* s);
)I changed a bit of logic here and there but the trickiest part was on the
f i/i/i i/i/i i/i/i
lines where you have to check if it matches right. If it doesn't you have to reset thestringstream
to it's original state and position (at least removestd::ios_base::failbit
and and replace position).I wanted to keep the aspect of one line = one case, so it might be a little harder to understand with the lambda function, but I could also reverse the logic and handle faces edge by edge (not sure if I'll use it though).