【问题标题】:changing the delimiter for cin (c++)更改 cin (c++) 的分隔符
【发布时间】:2011-11-10 07:21:27
【问题描述】:

我已重定向“cin”以从文件流中读取 cin.rdbug(inF.rdbug()) 当我使用提取操作符时,它会一直读取到一个空格字符。

是否可以使用其他分隔符?我在 cplusplus.com 中浏览了 api,但没有找到任何东西。

【问题讨论】:

  • 你不使用operator<<std::cin,你的意思是>>吗?
  • 你可以尝试include white-space cin 缓冲区中的字符。
  • @0x69 :那行不通。这只是意味着给定输入“A B”,提取第一个单词会得到“A”而不是“A”。

标签: c++ stream whitespace delimiter cin


【解决方案1】:

可以更改cin 或任何其他std::istream 的字间分隔符,使用std::ios_base::imbue 添加自定义ctype facet

如果你正在读取/etc/passwd 样式的文件,下面的程序会分别读取每个:-delimited 单词。

#include <locale>
#include <iostream>


struct colon_is_space : std::ctype<char> {
  colon_is_space() : std::ctype<char>(get_table()) {}
  static mask const* get_table()
  {
    static mask rc[table_size];
    rc[':'] = std::ctype_base::space;
    rc['\n'] = std::ctype_base::space;
    return &rc[0];
  }
};

int main() {
  using std::string;
  using std::cin;
  using std::locale;

  cin.imbue(locale(cin.getloc(), new colon_is_space));

  string word;
  while(cin >> word) {
    std::cout << word << "\n";
  }
}

【讨论】:

  • 以不受控制的方式使用new是邪恶的,不用说你没有delete你的结构(并且没有办法删除未命名的指针)。尽可能尝试shared_ptr
  • 这通常是很好的建议,不适用于这种特定情况。在这种情况下,std::facet 是一个引用计数指针,std::locale::locale 需要一个原始指针,而不是共享指针,并且std::locale::~locale 被定义为delete 方面指针。如果您对locale 的接口有任何疑问,请与标准委员会联系,而不是我。请参阅en.cppreference.com/w/cpp/locale/locale/locale 处的示例程序
  • 尽管我会建议定义一个包装函数 get_locale 来用 cmets 包装 new 的那些不寻常使用。所以代码审查者会意识到接口有问题,而不是代码编写者。这就是我所说的使用new的“受控”方式。
  • 如果不创建新函数,表示所有权转移的更好方式可能是unique_ptr&lt;colon_is_space&gt;(new colon is_space).release()。虽然它和你的代码基本上是一样的,但更冗长,它表明你正在转移指针所有权。
【解决方案2】:

对于字符串,您可以使用 std::getline 重载来使用不同的分隔符进行读取。

对于数字提取,分隔符并不是真正的“空白”开头,而是数字中的任何无效字符。

【讨论】:

  • @JonathanMee:我没有说空格不是分隔符,我说分隔符集不仅仅是空格。试试istringstream("123_456") &gt;&gt; foo; 或试试istringstream("123|456") &gt;&gt; foo;
  • @JonathanMee:是的,虽然它比这更复杂,但在数字解析期间允许使用一些标点符号。很明显,是否将其归类为空格可能会影响状态标志,但空格并不是导致数字提取停止的唯一因素。
  • @Wolf 流通常是标准中性能最差的东西之一。但通常您将使用带输入/输出的流,因此相对于输入/输出操作的成本而言,缓慢的性能可以忽略不计。出于性能原因,尽管应该优先使用数组而不是流。
  • @JonathanMee:“相对于输入/输出操作的成本而言,缓慢的性能可以忽略不计”在我的经验中从未如此。事实上,在许多应用程序中,与其他处理、等待用户点击开始按钮或网络请求的成本相比,文件 I/O 和解析都可以忽略不计。但在使用 iostream 构建的 I/O 繁重的应用程序中,主导的是 iostream 代码,而不是 I/O 操作。
  • 嗯...我想这是我有历史的项目类型。感谢您的澄清。有一个平衡的观点是很好的。对于@Wolf 的问题,我想一个更好的答案是:“getline 并不比整个流慢,但是如果您关心性能,您应该寻找非流选项。”
【解决方案3】:

这是对Robᵩ 答案的改进,因为这是正确的答案(我很失望它没有被接受。)

您需要做的是更改ctype 查看的数组以确定分隔符是什么。

在最简单的情况下,您可以创建自己的:

const ctype<char>::mask foo[ctype<char>::table_size] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ctype_base::space};

在我的机器上,'\n' 是 10。我已将该数组元素设置为分隔符值:ctype_base::space。用foo 初始化的ctype 只会在'\n' 上定界,而不是' ''\t'

现在这是一个问题,因为传入ctype 的数组不仅定义了分隔符,还定义了流式传输所需的字母、数字、符号和其他一些垃圾。 (Ben Voigt 的回答涉及到这一点。)所以我们真正想做的是修改mask,而不是从头开始创建一个。

可以这样实现:

const auto temp = ctype<char>::classic_table();
vector<ctype<char>::mask> bar(temp, temp + ctype<char>::table_size);

bar[' '] ^= ctype_base::space;
bar['\t'] &= ~(ctype_base::space | ctype_base::cntrl);
bar[':'] |= ctype_base::space;

使用bar 初始化的ctype 将分隔'\n'':',但不会分隔' ''\t'

您开始设置cin 或任何其他istream,以使用您的自定义ctype,如下所示:

cin.imbue(locale(cin.getloc(), new ctype<char>(data(bar))));

您也可以在ctypes 之间切换,行为会在中途改变:

cin.imbue(locale(cin.getloc(), new ctype<char>(foo)));

如果您需要返回默认行为,只需执行以下操作:

cin.imbue(locale(cin.getloc(), new ctype<char>));

Live example

【讨论】:

  • 这会将bar['\t'] 设置为零,这可能不是有意的。要清除一点,请使用&amp;~(按位与与按位非)。 ! 是逻辑非,不会产生预期的效果。
  • @BenVoigt 谢谢,我想去掉 spacecntrl 位,但我不小心得到了所有东西。
【解决方案4】:

这是对 Jon 的回答以及来自 cppreference.com 的示例的改进。所以这遵循与两者相同的前提,但将它们与参数化分隔符结合起来。

struct delimiter_ctype : std::ctype<char> {
    static const mask* make_table(std::string delims)
    {
        // make a copy of the "C" locale table
        static std::vector<mask> v(classic_table(), classic_table() + table_size);
        for(mask m : v){
            m &= ~space;
        }
        for(char d : delims){
            v[d] |= space;
        }
        return &v[0];
    }
    delimiter_ctype(std::string delims, ::size_t refs = 0) : ctype(make_table(delims), false, refs) {}
};

干杯!

【讨论】:

    猜你喜欢
    • 2010-11-03
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多