web-dev-qa-db-ja.com

ostreamを使用してC ++で16進数として符号なし文字を印刷するにはどうすればよいですか?

C++で符号なし8ビット変数を使用したい。算術に関する限り、unsigned charまたはuint8_tのいずれかがトリックを行います(AFAIK uint8_tunsigned charの単なるエイリアスであるため、デバッガーが提示します)。

問題は、C++でostreamを使用して変数を出力すると、charとして処理されることです。私が持っている場合:

unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;

出力は次のとおりです。

a is ^@; b is 377

の代わりに

a is 0; b is ff

uint8_tを使用してみましたが、前述したように、それはunsigned charにtypedefされているので、同じことを行います。変数を正しく印刷するにはどうすればよいですか?

編集:コード全体の多くの場所でこれを行います。これを行う方法はありますかwithout印刷するたびにintにキャストしますか?

65
Nathan Fellman

次の手法を使用することをお勧めします。

struct HexCharStruct
{
  unsigned char c;
  HexCharStruct(unsigned char _c) : c(_c) { }
};

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs)
{
  return (o << std::hex << (int)hs.c);
}

inline HexCharStruct hex(unsigned char _c)
{
  return HexCharStruct(_c);
}

int main()
{
  char a = 131;
  std::cout << hex(a) << std::endl;
}

書くのは短く、元のソリューションと同じ効率があり、「元の」文字出力を使用することを選択できます。そして、それはタイプセーフです(「悪」マクロを使用しません:-))

50
MartinStettner

つかいます:

cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;

そして、先行ゼロでパディングしたい場合:

#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ; 

Cスタイルのキャストを使用しているので、C++の最終的な悪さを十分に理解し、マクロを使用してみませんか。

#define HEX( x )
   setw(2) << setfill('0') << hex << (int)( x )

あなたはその後言うことができます

cout << "a is " << HEX( a );

編集:とは言っても、MartinStettnerのソリューションははるかに優れています!

45
anon

詳細については http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ および- http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/ 。上記の記事の著者が意図していないことが明らかになったため、私はこれを投稿しています。

文字を16進数として印刷する最も簡単で正しいテクニックは次のとおりです。

unsigned char a = 0;
unsigned char b = 0xff;
auto flags = cout.flags(); //I only include resetting the ioflags because so
                           //many answers on this page call functions where
                           //flags are changed and leave no way to  
                           //return them to the state they were in before 
                           //the function call
cout << "a is " << hex << +a <<"; b is " << +b << endl;
cout.flags(flags);

読者がこれがどのように機能するかをダイジェスト版は、単項+演算子が正しい符号付きのintにno op型変換を強制することです。したがって、unsigned charはunsigned intに変換され、signed charはintに変換され、charはプラットフォームでcharが署名されているか署名されていないかに応じてunsigned intまたはintに変換されます署名済みまたは署名なしとして指定されていません)。

この手法の唯一の欠点は、慣れていない人に何が起こっているのかがはっきりしない場合があることです。しかし、間違っているがすぐに明確なことをするよりも、正しいテクニックを使用し、他の人にそれを教える方が良いと思います。

30
ltc

まあ、これは私のために働く:

std::cout << std::hex << (0xFF & a) << std::endl;

単に(int)推奨されるように、最上位ビットが1の場合、aの左に1を追加する場合があります。 coutを強制的に16進数として印刷します。

これがお役に立てば幸いです。

7
VinGarcia

ええと、昨日ホイールを再発明したようです...しかし、ちょっと、少なくとも今回は一般的なホイールです:) charsは2桁の16進数で印刷され、shortsは4桁の16進数等々。

template<typename T>
struct hex_t
{
    T x;
};

template<typename T>
hex_t<T> hex(T x)
{
    hex_t<T> h = {x};
    return h;
}

template<typename T>
std::ostream& operator<<(std::ostream& os, hex_t<T> h)
{
    char buffer[2 * sizeof(T)];
    for (auto i = sizeof buffer; i--; )
    {
        buffer[i] = "0123456789ABCDEF"[h.x & 15];
        h.x >>= 4;
    }
    os.write(buffer, sizeof buffer);
    return os;
}
5
fredoverflow

TrungTNとanonの答えは大丈夫だと思いますが、hex()関数を実装するMartinStettnerの方法は本当に単純ではなく、hex <<(int)mycharがすでに回避策であることを考えると暗すぎます。

「<<」演算子を簡単にするための私のソリューションは次のとおりです。

#include <sstream>
#include <iomanip>

string uchar2hex(unsigned char inchar)
{
  ostringstream oss (ostringstream::out);
  oss << setw(2) << setfill('0') << hex << (int)(inchar);
  return oss.str();
}

int main()
{
  unsigned char a = 131;
  std::cout << uchar2hex(a) << std::endl;
}

ストリーム演算子を実装するだけの価値はありません:-)

4
tdihp

私はMartinStettnerのようにしますが、桁数に追加のパラメーターを追加します。

inline HexStruct hex(long n, int w=2)
{
  return HexStruct(n, w);
}
// Rest of implementation is left as an exercise for the reader

したがって、デフォルトでは2桁ですが、必要に応じて4、8などを設定できます。

例えば。

int main()
{
  short a = 3142;
  std:cout << hex(a,4) << std::endl;
}

やり過ぎのように見えるかもしれませんが、Bjarneが言ったように、「ライブラリは使いやすく、書くのは簡単ではないはずです」。

4
Jimmy J

私はお勧めします:

std::cout << setbase(16) << 32;

から取得: http://www.cprogramming.com/tutorial/iomanip.html

2
nikolajsheller

私はwin32/linux(32/64ビット)で以下を使用します:

#include <iostream>
#include <iomanip>

template <typename T>
std::string HexToString(T uval)
{
    std::stringstream ss;
    ss << "0x" << std::setw(sizeof(uval) * 2) << std::setfill('0') << std::hex << +uval;
    return ss.str();
}
2
drus

次のコードを試すことができます:

unsigned char a = 0;
unsigned char b = 0xff;
cout << hex << "a is " << int(a) << "; b is " << int(b) << endl;
cout << hex
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;
cout << hex << uppercase
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;

出力:

a is 0; b is ff

a is 00; b is ff

a is 00; b is FF

2
TrungTN

これは古い質問ですが、テンプレートクラス内で任意の整数から16進文字列への変換を実装したいという非常によく似た問題の解決策を模索する上で、Googleのトップの結果でもあります。私の最終目標は、実際にGtk::Entryさまざまな整数幅を16進数で編集できるサブクラステンプレートですが、それはポイントの横にあります。

これは単項operator+トリックstd::make_unsigned from <type_traits>符号拡張ネガティブの問題を防ぐためにint8_t または signed charこの回答 で発生する値

とにかく、これは他の一般的なソリューションよりも簡潔だと思います。 any符号付きまたは符号なし整数型で機能し、非整数型で関数をインスタンス化しようとするとコンパイル時エラーがスローされます。

template < 
  typename T,
  typename = typename std::enable_if<std::is_integral<T>::value, T>::type
>
std::string toHexString(const T v)
{ 
  std::ostringstream oss;
  oss << std::hex << +((typename std::make_unsigned<T>::type)v);
  return oss.str();
}

使用例:

int main(int argc, char**argv)
{
  int16_t val;
  // Prints 'ff' instead of "ffffffff". Unlike the other answer using the '+'
  // operator to extend sizeof(char) int types to int/unsigned int
  std::cout << toHexString(int8_t(-1)) << std::endl;

  // Works with any integer type
  std::cout << toHexString(int16_t(0xCAFE)) << std::endl;

  // You can use setw and setfill with strings too -OR- 
  // the toHexString could easily have parameters added to do that.
  std::cout << std::setw(8) << std::setfill('0') << 
    toHexString(int(100)) << std::endl;
  return 0;
}

Update:または、ostringstreamが使用されているという考えが気に入らない場合は、テンプレートと単項演算子のトリックを組み合わせることができます。次の受け入れられた答えの構造ベースのソリューションで。ここでは、整数型のチェックを削除してテンプレートを変更したことに注意してください。 make_unsignedの使用は、コンパイル時の型の安全性を保証するのに十分かもしれません。

template <typename T>
struct HexValue 
{
  T value;
  HexValue(T _v) : value(_v) { }
};

template <typename T>
inline std::ostream& operator<<(std::ostream& o, const HexValue<T>& hs)
{
  return o << std::hex << +((typename std::make_unsigned<T>::type) hs.value);
}

template <typename T>
const HexValue<T> toHex(const T val)
{
  return HexValue<T>(val);
}

// Usage:
std::cout << toHex(int8_t(-1)) << std::endl;
1
Brian McFarland

@FredOverflowに基づいて再発明したバージョンを投稿したいと思います。次の変更を加えました。

修正:

  • _operator<<_のRhsは、const参照型でなければなりません。 @FredOverflowのコードでは、_h.x >>= 4_が出力hを変更します。これは驚くべきことに標準ライブラリと互換性がなく、タイプTはコピー構築可能に再作成されます。
  • _CHAR_BITS_のみが4の倍数であると想定します。@ FredOverflowのコードはcharが8ビットであると想定しています。これは、DSPの実装によっては必ずしも当てはまりません。特に、charは16ビット、24ビット、32ビットなどです。

改善する:

  • 整数型に使用できる他のすべての標準ライブラリマニピュレーターをサポートします。 _std::uppercase_。形式出力は__print_byte_で使用されるため、標準ライブラリマニピュレーターは引き続き使用可能です。
  • _hex_sep_を追加して、個別のバイトを印刷します(C/C++では、「バイト」はサイズがcharのストレージユニットであることに注意してください)。テンプレートパラメータSepを追加し、hexおよび__Hex<T, false>_にそれぞれ__Hex<T, true>_および_hex_sep_をインスタンス化します。
  • バイナリコードの膨張を避けます。関数__print_byte_は、_operator<<_から関数パラメーターsizeで抽出され、異なるSizeのインスタンス化を回避します。

バイナリコードの肥大化の詳細:

改善3で述べたように、hexおよび_hex_sep_がどれほど広範囲に使用されても、(ほぼ)複製された関数の2つのコピーのみがバイナリコードで終了します:__print_byte<true>_および__print_byte<false>_ 。そして、あなたは、この重複もまったく同じアプローチを使用して排除できることに気付くかもしれません:関数パラメーターsepを追加します。はい。ただし、そうする場合、ランタイムif(sep)が必要です。プログラムで広く使用される可能性のある共通ライブラリユーティリティが必要なため、ランタイムオーバーヘッドではなく複製で妥協しました。コンパイル時にif:C++ 11 _std::conditional_を使用することでこれを達成しました。関数呼び出しのオーバーヘッドはinlineによって最適化されます。

hex_print.h:

_namespace Hex
{
typedef unsigned char Byte;

template <typename T, bool Sep> struct _Hex
{
    _Hex(const T& t) : val(t)
    {}
    const T& val;
};

template <typename T, bool Sep>
std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h);
}

template <typename T>  Hex::_Hex<T, false> hex(const T& x)
{ return Hex::_Hex<T, false>(x); }

template <typename T>  Hex::_Hex<T, true> hex_sep(const T& x)
{ return Hex::_Hex<T, true>(x); }

#include "misc.tcc"
_

hex_print.tcc:

_namespace Hex
{

struct Put_space {
    static inline void run(std::ostream& os) { os << ' '; }
};
struct No_op {
    static inline void run(std::ostream& os) {}
};

#if (CHAR_BIT & 3) // can use C++11 static_assert, but no real advantage here
#error "hex print utility need CHAR_BIT to be a multiple of 4"
#endif
static const size_t width = CHAR_BIT >> 2;

template <bool Sep>
std::ostream& _print_byte(std::ostream& os, const void* ptr, const size_t size)
{
    using namespace std;

    auto pbyte = reinterpret_cast<const Byte*>(ptr);

    os << hex << setfill('0');
    for (int i = size; --i >= 0; )
    {
        os << setw(width) << static_cast<short>(pbyte[i]);
        conditional<Sep, Put_space, No_op>::type::run(os);
    }
    return os << setfill(' ') << dec;
}

template <typename T, bool Sep>
inline std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h)
{
    return _print_byte<Sep>(os, &h.val, sizeof(T));
}

}
_

テスト:

_struct { int x; } output = {0xdeadbeef};
cout << hex_sep(output) << std::uppercase << hex(output) << endl;
_

出力:

_de ad be ef DEADBEEF_

1
wpzdm