¿Cómo imprimo un carácter sin firmar como hexadecimal en C++ usando ostream?
-
21-08-2019 - |
Pregunta
Quiero trabajar con variables de 8 bits sin firmar en C++.Cualquiera unsigned char
o uint8_t
hacer el truco en lo que respecta a la aritmética (lo cual es de esperar, ya que AFAIK uint8_t
es solo un alias para unsigned char
, o eso es lo que presenta el depurador.
El problema es que si imprimo las variables usando ostream en C++, las trata como char.Si tengo:
unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;
entonces la salida es:
a is ^@; b is 377
en lugar de
a is 0; b is ff
Intenté usar uint8_t
, pero como mencioné antes, eso está definido como unsigned char
, entonces hace lo mismo.¿Cómo puedo imprimir mis variables correctamente?
Editar: Hago esto en muchos lugares a lo largo de mi código.¿Hay alguna manera de que pueda hacer esto? sin casting a int
cada vez que quiero imprimir?
Solución
Se recomienda usar la siguiente técnica:
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;
}
Es corto para escribir, tiene la misma eficacia que la solución original y que le permite elegir utilizar la salida "original" carácter. Y es de tipo seguro (no usar macros "malos": -))
Otros consejos
Uso:
cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;
Y si quieres rellenando con ceros a la izquierda, a continuación:
#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ;
Como estamos usando C-estilo arroja, por qué no ir hasta el final con la maldad terminal de C ++ y utilizar una macro!
#define HEX( x )
setw(2) << setfill('0') << hex << (int)( x )
A continuación, se puede decir
cout << "a is " << HEX( a );
Editar Una vez dicho esto, la solución de MartinStettner es mucho más agradable
!Puede leer más sobre esto en http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ y http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/ . Sólo estoy fijando esto, ya que ha quedado claro que el autor de los artículos anteriores no se propone.
La técnica más sencilla y correcta de hacerlo imprimir un char que es hexagonal
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);
Los lectores digieren versión de cómo funciona esto es que el operador unario + obliga a una conversión de ningún tipo op a un int con el signo numérico correcto. Por lo tanto, un unsigned char convierte a unsigned int, char firmado convierte a int, y un char convierte a cualquiera unsigned int o INT dependiendo de si Char está firmada o no en su plataforma (viene como una sorpresa para muchos que char es especial y no se especifica ya sea con o sin signo).
El único aspecto negativo de esta técnica es que puede que no sea evidente lo que está sucediendo a un alguien que no está familiarizado con ella. Sin embargo, creo que es mejor utilizar la técnica que es correcto y enseñar a otros acerca de él en lugar de hacer algo que es incorrecto pero más claro de inmediato.
Bueno, esto funciona para mí:
std::cout << std::hex << (0xFF & a) << std::endl;
Si sólo echas (int)
como sugirió que podría añadir 1s a la izquierda de a
si su bit más significativo es 1. Por lo tanto hacer esta operación binaria Y garantiza la salida tendrá los bits de izquierda ocupados por 0s y también lo convierte a unsigned int forzando cout imprimirlo como hex.
Espero que esto ayude.
Hm, parece que reinventado la rueda de ayer ... Pero bueno, al menos es una rueda genérico de este tiempo :) char
s se imprimen con dos dígitos hexadecimales, short
s con 4 dígitos hexadecimales y así sucesivamente.
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;
}
lo haría como MartinStettner además de añadir un parámetro adicional para el número de dígitos:
inline HexStruct hex(long n, int w=2)
{
return HexStruct(n, w);
}
// Rest of implementation is left as an exercise for the reader
Así que hay dos dígitos por defecto, pero puede configurar cuatro, ocho, o lo que sea, si usted quiere.
por ejemplo.
int main()
{
short a = 3142;
std:cout << hex(a,4) << std::endl;
}
Puede parecer una exageración, pero como Bjarne dijo:. "Las bibliotecas deben ser fáciles de usar, no es fácil de escribir"
Creo TrungTN y de anon respuesta es aceptable, pero forma de implementar el hexágono () no es realmente simple, y demasiado oscura, teniendo en cuenta hexagonal << mychar (int) es ya una solución. De MartinStettner
aquí está mi solución para hacer "<<" operador más fácil:
#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;
}
Es que no es digno de la implementación de un operador de corriente: -)
Puede probar el siguiente código:
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;
Salida:
a is 0; b is ff
a is 00; b is ff
a is 00; b is FF
utilizo el siguiente en Win32 / Linux (32/64 bits):
#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();
}
Me gustaría publicar mi versión reinventada basada en la de @FredOverflow.Hice las siguientes modificaciones.
arreglar:
- Rhs de
operator<<
debería ser deconst
tipo de referencia.En el código de @FredOverflow,h.x >>= 4
cambia la salidah
, que sorprendentemente no es compatible con la biblioteca estándar y escribeT
Se requiere que se pueda copiar y construir. - Suponer sólo
CHAR_BITS
es múltiplo de 4.El código de @FredOverflow suponechar
es de 8 bits, lo que no siempre es cierto; en algunas implementaciones en DSP, en particular, no es raro quechar
es de 16 bits, 24 bits, 32 bits, etc.
mejorar:
- Admite todos los demás manipuladores de biblioteca estándar disponibles para tipos integrales, p.
std::uppercase
.Debido a que la salida en formato se utiliza en_print_byte
, los manipuladores de biblioteca estándar todavía están disponibles. - Agregar
hex_sep
para imprimir bytes separados (tenga en cuenta que en C/C++ un 'byte' es por definición una unidad de almacenamiento con el tamaño dechar
).Agregar un parámetro de plantillaSep
y crear una instancia_Hex<T, false>
y_Hex<T, true>
enhex
yhex_sep
respectivamente. - Evite la hinchazón del código binario.Función
_print_byte
se extrae deoperator<<
, con un parámetro de funciónsize
, para evitar la creación de instancias para diferentesSize
.
Más sobre la inflación del código binario:
Como se mencionó en la mejora 3, no importa cuán extensamente hex
y hex_sep
se utiliza, sólo dos copias de la función (casi) duplicada saldrán en código binario: _print_byte<true>
y _print_byte<false>
.Y quizás te hayas dado cuenta de que esta duplicación también se puede eliminar utilizando exactamente el mismo enfoque:agregar un parámetro de función sep
.Sí, pero si lo hace, un tiempo de ejecución if(sep)
es necesario.Quiero una utilidad de biblioteca común que pueda usarse ampliamente en el programa, por lo que comprometí la duplicación en lugar de la sobrecarga del tiempo de ejecución.Logré esto usando tiempo de compilación. if
:C++11 std::conditional
, es de esperar que la sobrecarga de la llamada a la función se pueda optimizar mediante 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));
}
}
prueba:
struct { int x; } output = {0xdeadbeef};
cout << hex_sep(output) << std::uppercase << hex(output) << endl;
producción:
de ad be ef DEADBEEF
Me di cuenta que es una vieja pregunta, pero también es un resultado superior Google en la búsqueda de una solución a un problema muy similar que tengo, que es el deseo de implementar entero arbitrario para la conversión de series hexagonal dentro de una clase de plantilla. Mi objetivo final era en realidad una plantilla Gtk::Entry
subclase que permitiría la edición de varios anchos enteros en hexadecimal, pero eso no viene al caso.
Esto combina el truco unario operator+
con std::make_unsigned
de <type_traits>
para evitar el problema de la int8_t
o signed char
valores negativos de signo extiende que se produce en esta respuesta
De todos modos, creo que esto es más breve que cualquier otra solución genérica. Se debe trabajar para cualquier Signed o tipos de enteros sin signo, y lanza un error de compilación si se intenta crear una instancia de la función con cualquier tipo no enteros.
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();
}
Algunos Uso de ejemplo:
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;
}
Actualización: Como alternativa, si no te gusta la idea de la ostringstream
siendo utilizado, puede combinar la plantilla y el truco operador unitario con una solución basada en la estructura de la respuesta aceptada para el siguiente . Tenga en cuenta que aquí, he modificado la plantilla mediante la eliminación de la comprobación de tipos enteros. El uso de make_unsigned
podría ser suficiente para las garantías de seguridad de tipo en tiempo de compilación.
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;
Esto también funciona:
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;
}
He utilizado de esta manera.
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;