c流负数转换

我遇到了一个问题,C试图读取一个填充了十六进制形式的有符号整数的文本文件并将它们解析为向量.我使用C流进行变量重定向(stream>> var),似乎没有正确解析负数 – 变量获取值0,并设置流失败标志.

如果我尝试使用strtol()函数转换字符串,结果是预期的.同样,如果我尝试首先将流重定向到无符号整数,然后将变量转换为有符号整数,则结果再次正确,并且不会报告流错误.

我在Debian 9.1(x64)上使用gcc 6.3.0,在Xeon E5-2643 v3系统上运行.

有没有人遇到过这个问题?我希望转换的工作方式与strtol函数相同,并且不报告任何流错误.我错过了一些流设置/忘记调用某些功能或在这里设置一些标志?

任何建议将不胜感激.

下面是一个示例C程序,演示了这个问题.

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>


int main()
{
  const char* minus_one = "0xffffffff";

  std::stringstream ss;
  ss << minus_one;

  std::cout << "input string    : " << ss.str() << "\n"; // outputs "0xffffffff"

  // C-style conversion
  int32_t cint;
  cint = strtol(ss.str().c_str(), NULL, 0);
  std::cout << "strtol conv     : " << cint <<  " (" << std::hex << cint << ")\n"; // outputs "-1 (ffffffff)"
  std::cout << std::dec;

  // C++-style conversion
  int32_t cppint;
  ss >> std::hex >> cppint;
  std::cout << std::dec << "ssextr conv     : " << cppint <<  " (" << std::hex << cppint << ")\n"; // outputs "0 (0)" <- ERROR
  std::cout << std::dec;
  if (ss.fail()) std::cout << "Error converting number.\n";

  // C++-style conversion with cast
  uint32_t cppuint;
  int32_t cppint2;
  ss.clear();
  ss.str(minus_one);
  ss >> std::hex >> cppuint;
  cppint2 = (int32_t)cppuint;
  std::cout << std::dec << "ssextr cast conv: " << cppint2 <<  " (" << std::hex << cppint2 << ")\n"; // outputs "-1 (0xffffffff)"
  std::cout << std::dec;
  if (ss.fail()) std::cout << "Error converting number.\n";

  exit(EXIT_SUCCESS);
}

最佳答案

int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);

这将值0xffffffff读取为long,然后将其转换为int32_t.如果long大于32位,则strtol工作并返回0xffffffff,即4294967295,将其转换为int32_t会产生-1.但这与从字符串中读取负数不同(如果长为32位,则它不能按预期工作,而是返回LONG_MAX并将其转换为int32_t,即0x7fffffff).

int32_t cppint;
ss >> std::hex >> cppint;

这会尝试将值0xffffffff读入int32_t但值0xffffffff不适合该类型,因此读取该值失败(就像当long为32位时strtol失败).

与strtol版本相近的更接近的是:

int32_t cppint;
long l;
if (ss >> std::hex >> l)
  cppint = l;
else
  // handle error ...

期望能够将值0xffffffff读取为带符号的32位整数是不合理的. strtol和istreams不读取位模式,它们读取数字,并且数字0xffffffff不适合带符号的32位整数.

点赞