在istream对象上扫描

注意:我看过post什么是scanf格式输入的cin analougus? 在提出问题之前,post并没有解决我的问题。 该post寻求C ++ – 这样做的方式,但正如我已经提到的那样,使用C ++是不方便的 – 有时这样做的方式我有明确的例子。

我试图从istream对象中读取数据,有时使用C ++风格的方法(例如operator >>)是不方便的,例如数据是特殊forms的123:456所以你必须灌输’:’as space(非常hacky,而不是%d:scanf中的%d),或00123,你想读取字符串并转换十进制而不是八进制(而不是scanf中的%d),以及可能的许多其他情况。

我选择istream作为接口的原因是因为它可以派生,因此更灵活。 例如,我们可以创建内存流,或者动态生成的一些自定义流等。另一方面,C风格的FILE *非常有限,至少在符合标准的方式下,创建自定义流。

所以我的问题是,有没有办法在istream对象上进行类似scanf的数据提取? 我认为fscanf内部使用fgetc从FILE *逐字符号读取,而istream也提供这样的接口。 因此可以通过复制和粘贴fscanf的代码并用istream对象替换FILE *来实现,但这非常hacky。 有没有更聪明,更清洁的方式,还是有一些现有的工作?

谢谢。

在任何情况下,您都不应该使用scanf或其亲属做任何事情 ,原因有三:

  1. 许多格式字符串,包括例如%s所有简单用法,都与gets一样危险。
  2. 几乎不可能从格式错误的输入中恢复,因为scanf不会告诉你遇到意外事件时它在输入中输入的字符有多远。
  3. 数字溢出会触发未定义的行为:是的,这意味着如果输入中的数字字段位数过多,则允许scanf使整个程序崩溃

在C ++ 11之前,C ++规范定义了以scanf表示的数字格式的数字输入,这意味着最后的异议很可能也适用于它们! (在C ++ 11中,规范被改为使用strto*而是做一些可预测的事情,如果它检测到溢出。)

你应该做的是:用getline读取输入到std::string对象的整行,手动代码逻辑将它们分成字段(我不记得我的头顶是什么C ++ – 字符串相当于strsep是,但我确定它存在)然后使用strtol / strtod系列函数将数字字符串转换为机器号。

我不能强调这一点: 只有 100%可靠的方式将字符串转换为C或C ++中的数字,除非你有幸拥有一个在这方面已经符合C ++ 11标准的C ++运行时,与它同在function,你必须正确使用它们:

 errno = 0; result = strtoX(s, &ends, 10); // omit 10 for floats if (s == ends || *ends || errno) parse_error(); 

(上面链接的OpenBSD联机帮助页解释了为什么你必须做这个相当复杂的事情。)

(如果你很聪明,你可以使用ends和一些手动逻辑来跳过那个冒号,而不是strsep 。)

我不建议你混合C ++输入输出和C输入输出。 不,他们真的不兼容,但他们可能只是简单的互操作错误。

例如,Oracle文档建议不要混用它http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html

但没有人阻止你将数据读入缓冲区并使用sscanf等标准c函数进行解析。

 ... string curString; int a, b; ... std::getline(inputStream, curString); int sscanfResult == sscanf(curString.cstr(), "%d:%d", &a, &b); if (2 != sscanfResult) throw "error"; ... 

但是在某些情况下,当你的流只是一个很长的连续符号序列时(例如某些字符串变成了内存流),它将无济于事。

从头开始创建自己的fscanf或移植(?)原始的CRTfunction实际上并不是最糟糕的想法。 只要确保你已经彻底测试过(低级自定义字符操作总是C中的痛苦来源)。

我从来没有真正尝试过boost \ spirit ,这样的解析基础设施对你的项目来说真的太过分了。 但是增强库通常经过充分测试和设计。 你至少可以尝试使用它。

根据@ tmyklebu的评论,我实现了streamScanf,它通过fopencookie将istream包装为FILE *: https : //github.com/likan999/codejam/blob/master/Common/StreamScanf.cpp