从流中读取十六进制文本格式 0x。

7

我正在寻找一种简单的方法,使用流从文本文件中读取十六进制值。我在 Stack Overflow 上搜索了“C++ hex read stream 0x”,大多数回答都是关于将十六进制写为文本或在没有“0x”前缀的情况下读取十六进制值。这个问题是关于将带有“0x”前缀的十六进制数作为一个数字一次性读取。

我的方法:

unsigned char byte;
std::istringstream sample("0xce");
sample >> std::hex >> byte;

第一个字符对应的byte值为'0'(0x30)。

'strtol`函数处理转换,但需要读取数据,并将其转换为C风格字符串。

我正在重载一个类中的operator>>,以读取逗号分隔值(CSV)文本文件。这是数据文件的示例:

1,-4.93994892,0xa5,8,115.313e+3,
2,-4.93986238,0xc0,8,114.711e+3,
3,-4.93977554,0xc2,8,114.677e+3,

我的提取方法:
class Csv_Entry
{
public:
    friend std::istream& operator >>(std::istream& inp, Csv_Entry& ce);
    unsigned int    m_index;
    double      m_time;
    unsigned char   m_byte;
    unsigned int    m_data_length;
    double      m_bit_rate;
};

std::istream&
operator >> (std::istream& inp, Csv_Entry& ce)
{
    char    separator;
    inp >> ce.m_index;
    inp >> separator;
    inp >> ce.m_time;
    inp >> separator;
    inp >> std::hex >> ce.m_byte;
    inp >> separator;
    inp >> ce.m_data_length;
    inp >> separator;
    inp >> ce.m_bit_rate;
    inp.ignore(10000, '\n');
    return inp;
}

我需要使用 std::setw 吗?

编辑1:
我正在使用 Windows 7 上的 Visual Studio 2010 Premium,64位平台。


2
你可以节省自己解析CSV文件的麻烦,使用现成的解析器生成器,它足够灵活,可以处理所有情况(包括Unicode):http://tinyurl.com/3zs2o6k - Gene Bushuyev
@Gene Bushuyev:感谢你的建议。我一直认为解析器在处理简单的CSV文件时是杀鸡焉用牛刀。如果涉及到语言或复杂的协议,我会使用解析器。这个程序只是一个小型分析工具,我认为使用解析器会让程序变得过于复杂。 - Thomas Matthews
3个回答

7

一种解决方法是使用 unsigned int 读取值,然后转换为 unsigned char

unsigned int value;
inp >> hex >> value;
unsigned char byte;
byte = value & 0xFF;

我猜测是 unsigned char 类型引起了问题。

有没有C++语言专家能引用一段描述这种行为的章节?


2
这是一个文本数据流,字符被读取为符号而非值;因此 '0' 被读取为值为 0x30 的字符。 - Gene Bushuyev
@Gene Bushuyev:无符号字符仍然被读作符号(字符)吗?我来自嵌入式系统领域,我们使用unsigned char作为数字类型,而不是字符。 - Thomas Matthews
@Thomas -- 这个流是文本类型,operator>> 已经为所有字符类型(有符号/无符号,charT)进行了重载。 - Gene Bushuyev
请参见27.7.2.2.3/12,其中提到了所有这些重载。 - Gene Bushuyev
2
根据C++03 §27.6.1.2.2 [lib.istream.formatted.arithmetic],对于算术类型,提取器使用区域设置的num_get<>对象来解析输入流。根据§22.2.2.1/2 [lib.facet.num.get.virtuals],字符根据适当的格式说明符(表55-56)解释,对于unsigned int来说是%X。然而,charunsigned char并不被视为算术类型;它们的行为由§27.6.1.2.3描述。 - Adam Rosenfield
显示剩余2条评论

0
问题在于您的 Csv_Entry 的 m_byte 成员的数据类型。当通过输入流提取输入数据时,它将 0 解释为有效值,然后将 x 解释为分隔符,从而扰乱了流提取中其余的值。如果您将 Csv_Entry::m_byte 成员更改为 unsigned int,则问题将消失,并且它将使用 std::hex 正确解释十六进制值。
顺便说一句,由于您的所有成员都是公共的,因此您可以将 Csv_Entry 设计为一个结构体,但这里有一些使用您的条目数据的示例工作代码:http://ideone.com/H7NG1 您会注意到,在输出方面,我只需要包括 std::hex 和 std::showbase 就可以正确打印十六进制值。

0

Thomas Matthews 是正确的。你需要将 unsigned int 转换为 unsigned char

如果你熟悉 C 函数 scanf/printf,你会发现它们的行为类似。但我认为它们更适用于这种情况。

//%X specifies that we trying read integer in format 0x123FFF
//%c specifies that we trying read character
//0xABC is input string

unsigned char hex;
sscanf("0xABC", "%X", &hex); // error because not enough memory allocated
                             // by address &hex to store integer

unsigned char hex;
sscanf("0xABC", "%c", &hex); // reads only one character '0'

我的观点是,你可以读取十六进制整数或字符,但你试图“在字符中读取十六进制整数”。这是stdlib开发人员的特定情况)


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