كيف يمكنني طباعة حرف غير موقع كـ hex في c++ باستخدام ostream؟
-
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 برقمين سداسيين، short
s مع 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;
يمكنك تجربة الكود التالي:
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.يفترض رمز @FredOverflowchar
هو 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;