C++中getline函数的多个分隔符问题

4
我希望以简单的方式逐字读取一段文字并避免任何非字母数字字符。在从带有空格和'\n'的文本"演变"之后,如果有','和'.'等字符,则需要解决这个问题。第一个情况可以通过使用以空格为分隔符的getline轻松解决。我想知道是否有一种方法可以使用具有多种分隔符的getline,或者甚至使用某种正则表达式(例如'.'|' '|','|'\n')。
据我所知,getline的工作方式是从输入流中读取字符,直到达到'\n'或delimiter字符为止。我的第一个猜测是很容易提供它多个分隔符,但事实证明并非如此。
编辑: 仅作澄清。我不寻求任何C风格的解决方案(例如,strtok对我来说非常丑陋)或算法类型的解决方案。设计一个简单的算法来解决这个问题并实现它相当容易。我正在寻找一种更优雅的解决方案,或者至少是为什么我们不能使用getline函数处理它,因为除非我完全误解了,否则应该能够以某种方式接受多个分隔符。

1
@BaummitAugen:找到一个C++的重复项还可以,但你关闭的那个并不是特别好的重复项(至少在我看来是这样)。一个答案根本没有解决这个问题(它只处理了字符串的分割,而不是读取所需的流)。另一个确实能用,但只是巧合(它确实指定了\n作为分隔符,但对于其他不想要它的人来说,它将无法工作)。 - Jerry Coffin
@JerryCoffin 这个问题似乎是一样的。如果其他问题需要更好的答案,仍然可以添加一个,它没有关闭。 - Baum mit Augen
1
@BaummitAugen: 我不同意--另一篇文章只谈到源是“一些文本”,可以是文本文件或字符串。他确实展示了如何从流中读取,但不清楚这是否真正需要,还是只是一个可能来源的示例。这个问题非常具体,询问如何从流中读取。 - Jerry Coffin
1
@BaummitAugen:如果我知道有一个重复的问题,我早就已经处理了。我还没有找到一个精确的重复(尽管许多都有些相似)。 - Jerry Coffin
1
@JerryCoffin 如果您对我的投票有异议,只需重新打开问题即可。我不会进行报复性的负投票行为,我保证。 ;) - Baum mit Augen
显示剩余2条评论
1个回答

6

好消息和坏消息都有。好消息是你可以做到这一点。

坏消息是要做这个有点绕,有些人甚至觉得它很丑陋难看。

要做到这一点,首先需要观察两个事实:

  1. 普通的字符串提取器使用空格来分隔“单词”。
  2. 什么构成空格是由流的区域设置定义的。

将这两者结合起来,答案就变得相当明显了(虽然迂回):为了定义多个分隔符,我们定义一个允许我们指定哪些字符应该被视为分隔符(即空格)的区域设置:

struct word_reader : std::ctype<char> {
    word_reader(std::string const &delims) : std::ctype<char>(get_table(delims)) {}
    static std::ctype_base::mask const* get_table(std::string const &delims) {
        static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());

        for (char ch : delims)
            rc[ch] = std::ctype_base::space;
        return &rc[0];
    }
};

然后,我们需要告诉流使用该区域设置(即具有该ctype facet的区域设置),传递我们想要用作分隔符的字符,然后从流中提取单词:

int main() {
    std::istringstream in("word1, word2. word3,word4");

    // create a ctype facet specifying delimiters, and tell stream to use it:
    in.imbue(std::locale(std::locale(), new word_reader(" ,.\n")));
    std::string word;

    // read words from the stream. Note we just use `>>`, not `std::getline`:
    while (in >> word)
        std::cout << word << "\n";
}

我希望您需要的是这个结果:提取每个单词,但不包含我们说的“空白标点符号”。
word1
word2
word3
word4

1
那确实是一个可靠的解决方案,但正如你所提到的那样,它很琐碎并且有一些“欺骗”的本质(通过将我们需要的分隔符替换为空格)。我想知道是否有更优雅的解决方案,比如说采用恰好N个操作的方法,其中N是文件长度,就像getline在我们的分隔符被限制为空格和\n时所执行的那样。 - Eliran Abdoo
@GoldenSpecOps:我们不是在替换任何东西。该流正在寻找单词的结尾。它获取一个字符。询问区域设置:“这是空格吗?”继续将字符添加到单词中,直到达到文件结尾,或者区域设置说:“是的,那是空格”。然后,它会向前跳过与区域设置保持相同的下一个字符为空格的时间。重复以上步骤。 - Jerry Coffin
与getline的唯一主要区别在于,如果您有类似a\n\n\nz的内容,getline将读取a,空行,空行,z,但是>>将只读取它作为az - Jerry Coffin
请问您能否进一步解释一下您的解决方案?
  • 我理解通过为stringstream赋予locale,可以得到一个具有自定义输出的流(在这种情况下,避免每个相关字符)。
  • 我也理解了std::locale构造函数的用法,如下所示
template< class Facet > locale( const locale& other, Facet* f );但我没有完全理解word_reader结构体,并且我很难找到有关模板Facet类的locale构造函数要求的相关文档。
- Eliran Abdoo
1
这是一个非常优雅的解决方案,如果不滥用 locale 类的话。但是,这会移除标准的语言环境设置吗? - Varad Mahashabde
2
@VaradMahashabde:它只影响该流的语言环境,并使用一个默认构造的语言环境,只替换了“ctype”面向。因此,它只影响该流如何对字符进行分类。 - Jerry Coffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接