更安全但易于使用且灵活的 C++ 替代 sscanf()

2021-12-13 00:00:00 string parsing c++ scanf

当我需要从一堆字符串中扫描值时,我经常发现自己完全回到了 C 的 sscanf() ,因为它的简单性和易用性.例如,我可以非常简洁地从字符串中提取几个双精度值:

When I need to scan in values from a bunch of strings, I often find myself falling back to C's sscanf() strictly because of its simplicity and ease of use. For example, I can very succinctly pull a couple double values out of a string with:

string str;
double val1, val2;
if (sscanf(str.c_str(), "(%lf,%lf)", &val1, &val2) == 2)
{
    // got them!
}

这显然不是 C++.我不一定认为这是可憎的,但我一直在寻找一种更好的方法来完成一项共同的任务.我知道读取字符串的C++ 方式"是 istringstream,但是在上面的格式字符串中处理括号和逗号所需的额外输入只会让我想使用它太麻烦了.

This obviously isn't very C++. I don't necessarily consider that an abomination, but I'm always looking for a better way to do a common task. I understand that the "C++ way" to read strings is istringstream, but the extra typing required to handle the parenthesis and comma in the format string above just make it too cumbersome to make me want to use it.

是否有一种好方法可以以类似于上述的方式使内置设施符合我的意愿,或者是否有一个好的 C++ 库以更类型安全的方式执行上述操作?看起来Boost.Format确实很好地解决了输出问题,但我还没有找到任何类似简洁的输入.

Is there a good way to either bend built-in facilities to my will in a way similar to the above, or is there a good C++ library that does the above in a more type-safe way? It looks like Boost.Format has really solved the output problem in a good way, but I haven't found anything similarly succinct for input.

推荐答案

我编写了一些可以读取字符串和字符文字的代码.像普通的流读取一样,如果它得到无效数据,它会设置流的坏位.这应该适用于所有类型的流,包括宽流.将此位粘贴到新标题中:

I wrote a bit of code that can read in string and character literals. Like normal stream reads, if it gets invalid data it sets the badbit of the stream. This should work for all types of streams, including wide streams. Stick this bit in a new header:

#include <iostream>
#include <string>
#include <array>
#include <cstring>

template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) {
        std::array<e, N-1> buffer; //get buffer
        in >> buffer[0]; //skips whitespace
        if (N>2)
                in.read(&buffer[1], N-2); //read the rest
        if (strncmp(&buffer[0], sliteral, N-1)) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
        e buffer;  //get buffer
        in >> buffer; //read data
        if (buffer != cliteral) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
//redirect mutable char arrays to their normal function
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) {
        return std::operator>>(in, carray);
}

它会让输入字符变得非常容易:

And it will make input characters very easy:

std::istringstream input;
double val1, val2;
if (input >>'('>>val1>>','>>val2>>')') //less chars than scanf I think
{
    // got them!
}

概念证明.现在您可以 cin 字符串和字符文字,如果输入不是完全匹配,它的行为就像任何其他未能正确输入的类型.请注意,这仅匹配不是第一个字符的字符串文字中的空格.只有四个功能,都是脑残的简单.

PROOF OF CONCEPT. Now you can cin string and character literals, and if the input is not an exact match, it acts just like any other type that failed to input correctly. Note that this only matches whitespace in string literals that aren't the first character. It's only four functions, all of which are brain-dead simple.

用流解析是个坏主意.使用正则表达式.

Parsing with streams is a bad idea. Use a regex.

相关文章