【问题标题】:istream::unget() in C++ doesn't work as I thoughtC++ 中的 istream::unget() 不像我想的那样工作
【发布时间】:2013-11-18 19:56:18
【问题描述】:

unget 没有按照我想象的方式工作……让我自己解释一下。正如我认为的那样, unget 获取流中提取的最后一个字符并将其放回流中(并准备再次提取)。在内部,它正在减少流缓冲区中的指针(创建哨兵和所有这些东西)。

但是,当我一个接一个地使用两个 unget() 时,它的行为变得非常奇怪。如果写hello<bye 之类的东西,我使用< 作为分隔符,如果我使用getline 和后来的两个ungets,它返回我你好,而不是o<bye"。这是我的代码:

#include <iostream>
#define MAX_CHARS 256

using namespace std;

int main(){

    char cadena[MAX_CHARS];

    cout << "Write something: ";
    cin.getline(cadena, MAX_CHARS, '<');

    cout << endl << "Your first word delimited by < is: " << cadena << endl;

    cin.unget(); //Delimiter (removed by getline) is put back in the stream
    cin.unget(); //!?
    cin >> cadena;

    cout << "Your phrase with 2 ungets done..." << cadena;
    return 0;
}

尝试使用 bye&lt;hello,然后 cadena 得到 bye 而不是 e&lt;hello 我认为 unget 每次调用时都与最后一个字符一起工作,f*** 发生了什么?

【问题讨论】:

  • "我认为 unget 每次调用时都与最后一个字符一起工作" — 不,只有一次。
  • 那它是如何工作的呢? (感谢黑隼把
  • @n.m.:流缓冲区接口中的任何内容都不会阻止尽可能多的放回字符。 ...而且没有任何东西可以保证您甚至可以放回一个角色!这完全取决于流缓冲区。

标签: c++ istream


【解决方案1】:

您观察到的问题一点也不奇怪。首先,请注意底层流缓冲区可能支持也可能不支持取消获取字符。通常,至少支持一个回退字符。这是否真的正确以及是否支持更多字符完全取决于流缓冲区。

在您的测试程序中发生的只是第二个unget() 失败,流进入失败状态(即,std::ios_base::failbit 已设置)并且再次尝试读取某些内容失败。失败的读取使原始缓冲区保持不变,并且由于它没有经过测试(应该如此),看起来好像同一个字符串被读取了两次。

std::cin 可能只支持一个字符放回的根本原因是它默认与stdin 同步。结果,std::cin 不做任何缓冲(因此它也相当慢)。通过不与stdin同步,您很有可能获得更好的结果:

std::ios_base::sync_with_stdio(false);

这将提高性能和放置更多角色成功的可能性。仍然不能保证您可以放回多个字符(甚至只是一个字符)。如果你真的需要放回字符,你应该考虑使用一个过滤流缓冲区,它支持你需要的尽可能多的字符放回。通常,标记化输入不需要任何放回字符,这是只有平庸支持的基本原因:由于放回支持很差,因此最好使用适当的标记化来减少改进放回的需要。有点循环论证。不过,由于您始终可以创建自己的流缓冲区,因此实际上并没有什么害处。

【讨论】:

  • 所以它完全取决于我正在使用的 cin 实现? c++ 中的流不是 unget ANSI 函数吗?我越来越糊涂了。什么是标准输入?又为什么要和cin同步呢?感谢您的回答。
  • @Carrascado: std::istream::unget() 当然是一个标准函数,它被定义为调用this-&gt;rdbuf()-&gt;sungetc(),但如果你继续挖掘,你会发现不能保证它会在未知的情况下成功流缓冲区。您可以创建具有受控放回语义的流缓冲区。 stdin 是访问的 C 标准输入流,例如,使用 fgetc(stdin)。由于 C++ 建立在 C 之上,因此人们认为 std::cin 不能仅仅劫持标准输入,即它会在后台有效地使用 stdin,除非被告知不要使用 std:ios_base::sync_with_stdio(false)
  • 因为 unget 被定义为在他的 rdbuf(他的流缓冲区)中调用 sungetc(),这将取决于对此流缓冲区的实现,不是吗?再次感谢您的回答。
  • 是的。可以创建一个过滤流缓冲区,它可以保证可以放回的字符数。
  • @DietmarKühl 确定 boost 过滤流中的 unget() 可以多次使用(大约 4 、 5 次),没有故障位问题或在多次调用 unget 过程中遇到的任何其他问题在 std::cin 上?
【解决方案2】:

此行为的实际原因与流的故障位有关,如上一个答案中所述。我可以提供一个变通的代码,可以帮助你实现你想要的结果。

#include <iostream>
#include <boost/iostreams/filtering_stream.hpp>
// compile using g++ -std=c++11 -lboost_iostreams

#define MAX_CHARS 256
using namespace std;

int main(){

    boost::iostreams::filtering_istream cinn(std::cin,0,1);
    char cadena[MAX_CHARS];

    cout << "Write something: ";
    cinn.getline(cadena, MAX_CHARS, '<');

    cout << endl << "Your first word delimited by < is: " << cadena << endl;

    cinn.unget(); //Delimiter (removed by getline) is put back in the stream
    cinn.unget(); //!?
    cinn >> cadena;

    cout << "Your phrase with 2 ungets done..." << cadena;
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-05
    • 1970-01-01
    相关资源
    最近更新 更多