كيف يمكنني طباعة حرف غير موقع كـ hex في c++ باستخدام ostream؟

StackOverflow https://stackoverflow.com/questions/673240

  •  21-08-2019
  •  | 
  •  

سؤال

أريد العمل مع متغيرات 8 بت غير الموقعة في C++.أيضاً unsigned char أو uint8_t قم بالخدعة فيما يتعلق بالحساب (وهو أمر متوقع، منذ AFAIK uint8_t هو مجرد اسم مستعار ل unsigned char, ، أو هكذا يقدمه مصحح الأخطاء.

المشكلة هي أنه إذا قمت بطباعة المتغيرات باستخدام ostream في C++ فإنه يعاملها على أنها 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, ، لذلك يفعل نفس الشيء.كيف يمكنني طباعة المتغيرات الخاصة بي بشكل صحيح؟

يحرر: أفعل هذا في العديد من الأماكن خلال الكود الخاص بي.هل هناك أي طريقة يمكنني من خلالها القيام بذلك بدون الصب ل int في كل مرة أريد الطباعة؟

هل كانت مفيدة؟

المحلول

وأقترح استخدام التقنية التالية:

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;
}

إنه قصير في الكتابة، وله نفس كفاءة الحل الأصلي ويتيح لك اختيار استخدام إخراج الأحرف "الأصلي".وهو آمن من النوع (لا يستخدم وحدات الماكرو "الشريرة" :-))

نصائح أخرى

يستخدم:

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 هو أفضل بكثير!

يمكنك قراءة المزيد عن هذا في 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/.أنا أنشر هذا فقط لأنه أصبح من الواضح أن كاتب المقالات المذكورة أعلاه لا ينوي ذلك.

الأسلوب الأبسط والأكثر صحة لطباعة حرف سداسي عشري هو

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);

نسخة ملخص القراء لكيفية عمل ذلك هي أن عامل التشغيل الأحادي + يفرض تحويلًا من النوع no op إلى int بالتوقيع الصحيح.لذلك، يتم تحويل الحرف غير الموقع إلى int غير الموقع، ويتحول الحرف الموقع إلى int، ويتحول الحرف إلى int غير الموقع أو int اعتمادًا على ما إذا كان char موقعًا أم غير موقع على النظام الأساسي الخاص بك (يأتي بمثابة صدمة للكثيرين أن char مميز ولم يتم تحديدها على أنها موقعة أو غير موقعة).

الجانب السلبي الوحيد لهذه التقنية هو أنه قد لا يكون واضحًا ما يحدث لشخص لا يعرفه.ومع ذلك، أعتقد أنه من الأفضل استخدام الأسلوب الصحيح وتعليم الآخرين عنه بدلاً من القيام بشيء غير صحيح ولكنه أكثر وضوحًا على الفور.

حسنًا ، هذا يناسبني:

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

إذا قمت بإلقاء فقط (int) كما هو مقترح قد يضيف 1s إلى يسار a إذا كان الجزء الأكثر أهمية هو 1.لذا فإن إجراء هذه العملية الثنائية AND يضمن أن الإخراج سيكون به البتات اليسرى مملوءة بـ 0 ويحولها أيضًا إلى int غير الموقعة مما يجبر cout على طباعتها على شكل سداسي عشري.

آمل أن يساعد هذا.

حسنًا، يبدو أنني قمت بإعادة اختراع العجلة بالأمس...ولكن مهلا، على الأقل إنها عجلة عامة هذه المرة :) charتتم طباعة s برقمين سداسيين، shorts مع 4 أرقام سداسية وهكذا.

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;
}

سأفعل ذلك مثل MartinStettner ولكن أضف معلمة إضافية لعدد الأرقام:

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

إذن لديك رقمين افتراضيًا ولكن يمكنك تعيين أربعة أو ثمانية أو أي رقم آخر إذا كنت تريد ذلك.

على سبيل المثال.

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

قد يبدو الأمر مبالغة ولكن كما قال بيارن:"يجب أن تكون المكتبات سهلة الاستخدام، وليس سهلة الكتابة".

أعتقد أن إجابة TrungTN وanon جيدة، لكن طريقة MartinStettner في تنفيذ وظيفة hex() ليست بسيطة حقًا، ومظلمة جدًا، مع الأخذ في الاعتبار أن 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;
}

إنه لا يستحق تنفيذ مشغل الدفق :-)

أود أن أقترح:

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

مأخوذ من:http://www.cprogramming.com/tutorial/iomanip.html

يمكنك تجربة الكود التالي:

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

أستخدم ما يلي على 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();
}

أرغب في نشر نسخة إعادة الاختراع الخاصة بي بناءً على @FredOverflow.لقد قمت بالتعديلات التالية.

يصلح:

  • آر إس من operator<< ينبغي أن يكون من const نوع مرجع.في كود @FredOverflow، h.x >>= 4 يتغير الإخراج h, ، وهو أمر غير متوافق بشكل مدهش مع المكتبة القياسية، ونوع T مطلوب أن يكون قابلاً للإنشاء.
  • افترض فقط CHAR_BITS هو مضاعف 4.يفترض رمز @FredOverflow char هو 8 بت، وهو ليس صحيحًا دائمًا، في بعض التطبيقات على DSPs، على وجه الخصوص، ليس من غير المألوف أن char هي 16 بت، 24 بت، 32 بت، الخ.

يحسن:

  • دعم جميع معالجات المكتبة القياسية الأخرى المتاحة للأنواع المتكاملة، على سبيل المثال. std::uppercase.لأنه يتم استخدام إخراج التنسيق في _print_byte, ، لا تزال معالجات المكتبة القياسية متاحة.
  • يضيف hex_sep لطباعة بايتات منفصلة (لاحظ أنه في C/C++، "البايت" هو بحكم التعريف وحدة تخزين بحجم char).إضافة معلمة القالب Sep وإنشاء مثيل _Hex<T, false> و _Hex<T, true> في hex و hex_sep على التوالى.
  • تجنب انتفاخ الكود الثنائي.وظيفة _print_byte يتم استخراجه من operator<<, ، مع معلمة الوظيفة size, ، لتجنب إنشاء مثيل لمختلف Size.

المزيد عن تضخم الكود الثنائي:

كما هو مذكور في التحسين 3، بغض النظر عن مدى اتساعه hex و hex_sep يتم استخدام نسختين فقط من الوظيفة المكررة (تقريبًا) في الكود الثنائي: _print_byte<true> و _print_byte<false>.وقد تدرك أنه يمكن أيضًا التخلص من هذه الازدواجية باستخدام نفس الطريقة تمامًا:إضافة معلمة دالة sep.نعم، ولكن في حالة القيام بذلك، وقت التشغيل if(sep) وهناك حاجة.أريد أداة مكتبة عامة يمكن استخدامها على نطاق واسع في البرنامج، وبالتالي أخاطر بالتكرار بدلاً من تحمل عبء وقت التشغيل.لقد حققت ذلك باستخدام وقت الترجمة if:سي++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

أدرك أن هذا سؤال قديم، ولكنه أيضًا أحد أهم نتائج Google في البحث عن حل لمشكلة مشابهة جدًا لدي، وهي الرغبة في تنفيذ عدد صحيح عشوائي لتحويلات السلسلة السداسية داخل فئة القالب.كان هدفي النهائي في الواقع أ Gtk::Entry قالب فئة فرعية من شأنه أن يسمح بتحرير عروض أعداد صحيحة مختلفة في شكل سداسي عشري، ولكن هذا ليس هو الهدف.

وهذا يجمع بين الأحادي operator+ خدعة مع std::make_unsigned من <type_traits> للوقاية من مشكلة امتداد الإشارة السلبية int8_t أو signed char القيم التي تحدث في هذه الإجابة

على أي حال، أعتقد أن هذا أكثر إيجازًا من أي حل عام آخر.يجب أن تعمل من أجل أي الأنواع الصحيحة الموقعة أو غير الموقعة، ويلقي خطأ وقت الترجمة إذا حاولت إنشاء مثيل للوظيفة مع أي أنواع غير صحيحة.

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;
}

تحديث: بدلا من ذلك، إذا كنت لا تحب فكرة 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;

سيعمل هذا أيضًا:

std::ostream& operator<< (std::ostream& o, unsigned char c)
{
    return o<<(int)c;
}

int main()
{
    unsigned char a = 06;
    unsigned char b = 0xff;
    std::cout << "a is " << std::hex << a <<"; b is " << std::hex << b << std::endl;
    return 0;
}

لقد استخدمت بهذه الطريقة.

    char strInput[] = "yourchardata";
char chHex[2] = "";

int nLength = strlen(strInput);
char* chResut = new char[(nLength*2) + 1];
memset(chResut, 0, (nLength*2) + 1);



for (int i = 0; i < nLength; i++)
{
    sprintf(chHex, "%02X", strInput[i]& 0x00FF);    
    memcpy(&(chResut[i*2]), chHex, 2);
}

printf("\n%s",chResut);
delete chResut;
chResut = NULL;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top