Ciąg w postaci liczby całkowitej na szesnastkową w C ++


128

Jak przekonwertować liczbę całkowitą na ciąg szesnastkowy w C ++ ?

Mogę znaleźć kilka sposobów, aby to zrobić, ale wydają się one głównie ukierunkowane na C. Wydaje się, że nie ma natywnego sposobu na zrobienie tego w C ++. Jest to jednak dość prosty problem; Mam, intktóry chciałbym przekonwertować na ciąg szesnastkowy do późniejszego wydrukowania.

Odpowiedzi:


226

Użyj <iomanip>'s std::hex. Jeśli drukujesz, po prostu wyślij go na std::coutadres, jeśli nie, użyjstd::stringstream

std::stringstream stream;
stream << std::hex << your_int;
std::string result( stream.str() );

Można poprzedzić pierwsze <<z << "0x"lub cokolwiek chcesz, jeśli chcesz.

Inne interesujące manipulacje to std::oct(ósemkowe) i std::dec(z powrotem do dziesiętnych).

Jednym z problemów, które możesz napotkać, jest fakt, że daje to dokładną liczbę cyfr potrzebną do jego przedstawienia. Możesz użyć setfilli setwto, aby obejść problem:

stream << std::setfill ('0') << std::setw(sizeof(your_type)*2) 
       << std::hex << your_int;

Na koniec zasugerowałbym taką funkcję:

template< typename T >
std::string int_to_hex( T i )
{
  std::stringstream stream;
  stream << "0x" 
         << std::setfill ('0') << std::setw(sizeof(T)*2) 
         << std::hex << i;
  return stream.str();
}

7
@MSalters - wręcz przeciwnie. Sprawdź swoją sugestię co do inttypu;)
Kornel Kisielewicz

2
@LexFridman, aby wyemitować dokładnie tyle cyfr szesnastkowych, ile potrzeba. Po co emitować 8 cyfr, jeśli typ to uint8_t?
Kornel Kisielewicz

16
OSTRZEŻENIE: to nie zadziała dla pojedynczego bajtu, ponieważ znak jest zawsze traktowany jako znak
ov7a

5
Potrzebujesz również #include <sstream>
David Gausmann,

2
Jeśli jestem formatowania wielu ints Wygląda na to, że std::setwmusi być wyjście do strumienia dla każdego int, natomiast std::hex, std::setfill, std::uppercase, ... wystarczy być wysyłane do strumienia wyjściowego raz. Wydaje się to niespójne?
wcochran

39

Aby było lżejsze i szybsze sugeruję użycie bezpośredniego wypełnienia sznurka.

template <typename I> std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) {
    static const char* digits = "0123456789ABCDEF";
    std::string rc(hex_len,'0');
    for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
        rc[i] = digits[(w>>j) & 0x0f];
    return rc;
}

Czy to zadziała dla każdego typu? Mam na myśli również double, float, uint8_t?
SR

@SR Działa dla typów całkowitych, a nie dla doublei float(i nie dla wskaźników)
Wolf,

... bardzo pragmatyczna (ale ważna) mieszanka C i C ++, nie jestem pewien co do szybkości ... jak na mój gust , jest trochę gęsta.
Wolf

Dziękuję, świetna odpowiedź. Wprowadzenie biblioteki strumieniowej „tylko” po to, wydaje się marnotrawstwem.
DeweloperChris

1
To drukuje 0000FFFFdla 0xFFFF. Wolę mieć 0xFFFFjako wyjście.
DrumM

24

Służy std::stringstreamdo konwersji liczb całkowitych na łańcuchy i specjalnych manipulatorów do ustawiania podstawy. Na przykład tak:

std::stringstream sstream;
sstream << std::hex << my_integer;
std::string result = sstream.str();

14

Po prostu wydrukuj to jako liczbę szesnastkową:

int i = /* ... */;
std::cout << std::hex << i;

8
Użyłbym std::cout<<std::hex<<i<<std::dec;, w przeciwnym razie wszystkie liczby całkowite, które są przesyłane później, będą w formacie szesnastkowym. Nie musisz tego robić w przypadku innych odpowiedzi, które używają, stringstreamponieważ strumień jest odrzucany po użyciu, ale coutżyje wiecznie.
Mark Lakata

8

Możesz spróbować następujących rzeczy. To działa...

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;

template <class T>
string to_string(T t, ios_base & (*f)(ios_base&))
{
  ostringstream oss;
  oss << f << t;
  return oss.str();
}

int main ()
{
  cout<<to_string<long>(123456, hex)<<endl;
  system("PAUSE");
  return 0;
}

1
dobra odpowiedź, ale uważaj, że to_stringjest to część przestrzeni nazw stdw C ++ 11
Alex,

@Alex tak, w końcu jest rok 2014 ... broń Boże, wkrótce będziemy musieli zacząć zajmować się C ++ 14.
Alex

7

To pytanie jest stare, ale dziwię się, dlaczego nikt nie wspomniał boost::format:

cout << (boost::format("%x") % 1234).str();  // output is: 4d2

4
int num = 30;
std::cout << std::hex << num << endl; // This should give you hexa- decimal of 30

4

Dzięki poniższemu komentarzowi Lincolna zmieniłem tę odpowiedź.

Poniższa odpowiedź poprawnie obsługuje 8-bitowe liczby int w czasie kompilacji. Jednak wymaga C ++ 17. Jeśli nie masz C ++ 17, będziesz musiał zrobić coś innego (np. Podać przeciążenia tej funkcji, jedną dla uint8_t i jedną dla int8_t, lub użyć czegoś innego niż „if constexpr”, może enable_if).

template< typename T >
std::string int_to_hex( T i )
{
    // Ensure this function is called with a template parameter that makes sense. Note: static_assert is only available in C++11 and higher.
    static_assert(std::is_integral<T>::value, "Template argument 'T' must be a fundamental integer type (e.g. int, short, etc..).");

    std::stringstream stream;
    stream << "0x" << std::setfill ('0') << std::setw(sizeof(T)*2) << std::hex;

    // If T is an 8-bit integer type (e.g. uint8_t or int8_t) it will be 
    // treated as an ASCII code, giving the wrong result. So we use C++17's
    // "if constexpr" to have the compiler decides at compile-time if it's 
    // converting an 8-bit int or not.
    if constexpr (std::is_same_v<std::uint8_t, T>)
    {        
        // Unsigned 8-bit unsigned int type. Cast to int (thanks Lincoln) to 
        // avoid ASCII code interpretation of the int. The number of hex digits 
        // in the  returned string will still be two, which is correct for 8 bits, 
        // because of the 'sizeof(T)' above.
        stream << static_cast<int>(i);
    }        
    else if (std::is_same_v<std::int8_t, T>)
    {
        // For 8-bit signed int, same as above, except we must first cast to unsigned 
        // int, because values above 127d (0x7f) in the int will cause further issues.
        // if we cast directly to int.
        stream << static_cast<int>(static_cast<uint8_t>(i));
    }
    else
    {
        // No cast needed for ints wider than 8 bits.
        stream << i;
    }

    return stream.str();
}

Oryginalna odpowiedź, która nie obsługuje poprawnie 8-bitowych intów, tak jak myślałem:

Odpowiedź Kornela Kisielewicza jest świetna. Jednak niewielki dodatek pomaga wychwycić przypadki, w których wywołujesz tę funkcję z argumentami szablonu, które nie mają sensu (np. Float) lub które spowodowałyby nieporządne błędy kompilatora (np. Typ zdefiniowany przez użytkownika).

template< typename T >
std::string int_to_hex( T i )
{
  // Ensure this function is called with a template parameter that makes sense. Note: static_assert is only available in C++11 and higher.
  static_assert(std::is_integral<T>::value, "Template argument 'T' must be a fundamental integer type (e.g. int, short, etc..).");

  std::stringstream stream;
  stream << "0x" 
         << std::setfill ('0') << std::setw(sizeof(T)*2) 
         << std::hex << i;

         // Optional: replace above line with this to handle 8-bit integers.
         // << std::hex << std::to_string(i);

  return stream.str();
}

Zmodyfikowałem to, aby dodać wywołanie do std :: to_string, ponieważ 8-bitowe typy całkowite (np. std::uint8_tPrzekazane wartości) std::stringstreamsą traktowane jako znaki, co nie daje pożądanego wyniku. Przekazywanie takich liczb całkowitych w celu std::to_stringich poprawnej obsługi i nie szkodzi przy korzystaniu z innych, większych typów liczb całkowitych. Oczywiście w takich przypadkach może wystąpić niewielki spadek wydajności, ponieważ wywołanie std :: to_string jest niepotrzebne.

Uwaga: dodałbym to w komentarzu do oryginalnej odpowiedzi, ale nie mam przedstawiciela, który mógłby to skomentować.


w moich testach std :: to_string (i) nie wypisuje liczb całkowitych std :: uint8_t jako szesnastkowych. Myślę, że może to wymagać oddzielnych warunków dla obu typów uint8_t i int8_t, ponieważ muszą one być rzutowane na większe liczby całkowite.
Lincoln,

1
@Lincoln Masz rację. Nie wiem, co robiłem w tamtym czasie (kilka miesięcy temu), co sprawiło, że_string radził sobie z 8-bitowymi intami. Wróciłem nawet do wersji kompilatora, której, jak sądzę, używałem wtedy, aby sprawdzić dwukrotnie, ale to_string nie działało tak, jak powiedziałem. Więc kto wie? W każdym razie dziękuję za złapanie tego - zredagowałem odpowiedź na coś, co powinno działać poprawnie.
Mentalność strat

1
To nadal zadziała nieoczekiwanie dla char(co różni się od obu uint8_ti int8_twiększości implementacji (gdzie są odpowiednio unsigned chari signed char)).
Rusłan

@ruslan Tak, wszystkie typy bool i wide char pasują do std :: is_integral i nie zawiodą asercji. Ale ponieważ znak jest, zgodnie ze standardowym, gwarantowanym unikalnym typem, podobnie jak szerokie typy znaków, możesz obsłużyć je wszystkie, jeśli chcesz (wyjątki to znak bez / ze znakiem, który pasuje do typu całkowego bez / ze znakiem o dowolnej szerokości a bajt znajduje się na bieżącej maszynie - zwykle int8 - i nie można go filtrować, jeśli chcesz również dopasować wartości int o tej samej szerokości). Możesz odrzucić char, wide chars, bools, dodając więcej terminów do static_assert: ... && !std::is_same_v<char, T> && !std::is_same_v<bool, T>etc ...
Loss Mentality

2

Dla tych z Was, którzy zorientowali się, że wiele / większość z ios::fmtflagsnich nie działa std::stringstreamjeszcze z pomysłem szablonu, który Kornel opublikował dawno temu, poniższe działa i jest stosunkowo czyste:

#include <iomanip>
#include <sstream>


template< typename T >
std::string hexify(T i)
{
    std::stringbuf buf;
    std::ostream os(&buf);


    os << "0x" << std::setfill('0') << std::setw(sizeof(T) * 2)
       << std::hex << i;

    return buf.str().c_str();
}


int someNumber = 314159265;
std::string hexified = hexify< int >(someNumber);

9
Czy nie powinieneś po prostu zwrócić buf.str ()?
ruipacheco

2

Ja robię:

int hex = 10;      
std::string hexstring = stringFormat("%X", hex);  

Spójrz na odpowiedź SO z iFreilicht i wymagany plik nagłówkowy szablonu stąd GIST !


2

Wystarczy spojrzeć na moje rozwiązanie [1], które dosłownie skopiowałem z mojego projektu, więc dołączony jest niemiecki dokument API. Moim celem było połączenie elastyczności i bezpieczeństwa z moimi rzeczywistymi potrzebami: [2]

  • nie0x dodano prefiksu : dzwoniący może zdecydować
  • automatyczne odliczanie szerokości : mniej pisania
  • jawna kontrola szerokości : poszerzanie w celu formatowania, (bezstratne) zmniejszanie w celu zaoszczędzenia miejsca
  • zdolny do radzenia sobie long long
  • ograniczone do typów integralnych : unikaj niespodzianek poprzez ciche konwersje
  • łatwość zrozumienia
  • bez zakodowanego limitu
#include <string>
#include <sstream>
#include <iomanip>

/// Vertextet einen Ganzzahlwert val im Hexadezimalformat.
/// Auf die Minimal-Breite width wird mit führenden Nullen aufgefüllt;
/// falls nicht angegeben, wird diese Breite aus dem Typ des Arguments
/// abgeleitet. Funktion geeignet von char bis long long.
/// Zeiger, Fließkommazahlen u.ä. werden nicht unterstützt, ihre
/// Übergabe führt zu einem (beabsichtigten!) Compilerfehler.
/// Grundlagen aus: http://stackoverflow.com/a/5100745/2932052
template <typename T>
inline std::string int_to_hex(T val, size_t width=sizeof(T)*2)
{
    std::stringstream ss;
    ss << std::setfill('0') << std::setw(width) << std::hex << (val|0);
    return ss.str();
}

[1] na podstawie odpowiedzi Kornela Kisielewicza
[2] Przetłumaczone na język CppTest , tak brzmi:

TEST_ASSERT(int_to_hex(char(0x12)) == "12");
TEST_ASSERT(int_to_hex(short(0x1234)) == "1234");
TEST_ASSERT(int_to_hex(long(0x12345678)) == "12345678");
TEST_ASSERT(int_to_hex((long long)(0x123456789abcdef0)) == "123456789abcdef0");
TEST_ASSERT(int_to_hex(0x123, 1) == "123");
TEST_ASSERT(int_to_hex(0x123, 8) == "00000123");
// with deduction test as suggested by Lightness Races in Orbit:
TEST_ASSERT(int_to_hex(short(0x12)) == "0012"); 

1
Mógłby pochwalić się odliczeniem szerokości, np.TEST_ASSERT(int_to_hex(short(0x12)) == "0012");
Wyścigi lekkości na orbicie

2

Kod w celach informacyjnych:

#include <iomanip>
#include <sstream>
...
string intToHexString(int intValue) {

    string hexStr;

    /// integer value to hex-string
    std::stringstream sstream;
    sstream << "0x"
            << std::setfill ('0') << std::setw(2)
    << std::hex << (int)intValue;

    hexStr= sstream.str();
    sstream.clear();    //clears out the stream-string

    return hexStr;
}

4
To tak naprawdę nie dodaje do istniejących odpowiedzi i nie ma sensu jawnie cleartego sstream(zostanie zniszczone, gdy funkcja i tak powróci w następnym wierszu). Można uniknąć nazwany hexStrcałkowicie i po prostu return sstream.str();bez clearing i uzyskać ten sam efekt, co zmniejsza cztery linie kodu do jednego.
ShadowRanger

1
gdy celem forum jest zrozumienie rzeczy i użytkowania. bycie gadatliwym jest o wiele lepsze, aby zapewnić wyraźny obraz, niż zapisywanie linii. Pytanie nie dotyczyło zoptymalizowanego kodu, a odpowiedź stara się podać modułowy sposób radzenia sobie z takimi konwersjami. @ShadowRanger
parasrish

1
Jaki jest cel sstream.clear();? sstreamObiekt jest automatycznie zniszczony pod koniec zakresu, tak return sstream.str();by to zrobić.
Wilk

sstream.clearpo prostu wyczyści zawartość, zanim strumień zakończy się na końcu zasięgu (aby usunąć wszystkie flagi niepowodzenia i eof z clear). Rzeczywiście, gdy zakres umiera, wraz z okresem życia zmiennej strumieniowej, i dlatego sstream.strmoże być używany do zwracania wartości. [Odniesienie: cplusplus.com/reference/ios/ios/clear/]
parasrish

2

Moje rozwiązanie. Dozwolone są tylko typy całkowite.

Aktualizacja. W drugim parametrze można ustawić opcjonalny prefiks 0x.

definicja. h

#include  <iomanip>
#include <sstream>

template <class T, class T2 = typename std::enable_if<std::is_integral<T>::value>::type>
static std::string ToHex(const T & data, bool addPrefix = true);



template<class T, class>
inline std::string Convert::ToHex(const T & data, bool addPrefix)
{
    std::stringstream sstream;
    sstream << std::hex;
    std::string ret;
    if (typeid(T) == typeid(char) || typeid(T) == typeid(unsigned char) || sizeof(T)==1)
    {
        sstream << static_cast<int>(data);
        ret = sstream.str();
        if (ret.length() > 2)
        {
            ret = ret.substr(ret.length() - 2, 2);
        }
    }
    else
    {
        sstream << data;
        ret = sstream.str();
    }
    return (addPrefix ? u8"0x" : u8"") + ret;
}

main.cpp

#include <definition.h>
int main()
{
    std::cout << ToHex<unsigned char>(254) << std::endl;
    std::cout << ToHex<char>(-2) << std::endl;
    std::cout << ToHex<int>(-2) << std::endl;
    std::cout << ToHex<long long>(-2) << std::endl;

    std::cout<< std::endl;
    std::cout << ToHex<unsigned char>(254, false) << std::endl;
    std::cout << ToHex<char>(-2, false) << std::endl;
    std::cout << ToHex<int>(-2, false) << std::endl;
    std::cout << ToHex<long long>(-2, false) << std::endl;
    return 0;
}

Wyniki:
0xfe
0xfe
0xFFFFFFFE
0xfffffffffffffffe

fe
fe fffffffe
fffffffffffffffe


1

Chciałbym dodać odpowiedź, aby cieszyć się pięknem języka C ++. Jego zdolność przystosowania do pracy na wysokich i niskich poziomach. Miłego programowania.

public:template <class T,class U> U* Int2Hex(T lnumber, U* buffer)
{
    const char* ref = "0123456789ABCDEF";
    T hNibbles = (lnumber >> 4);

    unsigned char* b_lNibbles = (unsigned char*)&lnumber;
    unsigned char* b_hNibbles = (unsigned char*)&hNibbles;

    U* pointer = buffer + (sizeof(lnumber) << 1);

    *pointer = 0;
    do {
        *--pointer = ref[(*b_lNibbles++) & 0xF];
        *--pointer = ref[(*b_hNibbles++) & 0xF];
    } while (pointer > buffer);

    return buffer;
}

Przykłady:

char buffer[100] = { 0 };
Int2Hex(305419896ULL, buffer);//returns "0000000012345678"
Int2Hex(305419896UL, buffer);//returns "12345678"
Int2Hex((short)65533, buffer);//returns "FFFD"
Int2Hex((char)18, buffer);//returns "12"

wchar_t buffer[100] = { 0 };
Int2Hex(305419896ULL, buffer);//returns L"0000000012345678"
Int2Hex(305419896UL, buffer);//returns L"12345678"
Int2Hex((short)65533, buffer);//returns L"FFFD"
Int2Hex((char)18, buffer);//returns L"12"

Wykorzystuje to pewne techniki, których nie widać w innych odpowiedziach. Czy możesz edytować odpowiedź, tak aby zawierała wyjaśnienie, w jaki sposób i dlaczego to działa?
Jason Aller

0
#include <iostream> 
#include <sstream>  

int main()
{
unsigned int i = 4967295; // random number
std::string str1, str2;
unsigned int u1, u2;

std::stringstream ss;

Korzystanie z void pointer:

// INT to HEX
ss << (void*)i;       // <- FULL hex address using void pointer
ss >> str1;          //     giving address value of one given in decimals.
ss.clear();         // <- Clear bits
// HEX to INT
ss << std::hex << str1;   // <- Capitals doesn't matter so no need to do extra here
ss >> u1;
ss.clear();

Dodawanie 0x:

// INT to HEX with 0x
ss << "0x" << (void*)i;   // <- Same as above but adding 0x to beginning
ss >> str2;
ss.clear();
// HEX to INT with 0x
ss << std::hex << str2;   // <- 0x is also understood so need to do extra here
ss >> u2;
ss.clear();

Wyjścia:

std::cout << str1 << std::endl; // 004BCB7F
std::cout << u1 << std::endl;   // 4967295
std::cout << std::endl;
std::cout << str2 << std::endl; // 0x004BCB7F
std::cout << u2 << std::endl;   // 4967295


return 0;
}

-1
int var = 20;
cout <<                          &var << endl;
cout <<                     (int)&var << endl;
cout << std::hex << "0x" << (int)&var << endl << std::dec; // output in hex, reset back to dec

0x69fec4 (adres)
6946500 (adres do dec)
0x69fec4 (adres do dec, wyjście szesnastkowe)


instynktownie poszedł z tym ...
int address = (int) & var;

widziałem to gdzie indziej ...
unsigned long address = reinterpret_cast (& var);

komentarz powiedział mi, że to jest poprawne ...
int address = (int) & var;

mówiąc o dobrze zakrytej lekkości, gdzie jesteś? dostaje za dużo polubień!


Ta kwestia została już dobrze omówiona; co twoja odpowiedź dodaje do już opublikowanych? A co mają z tym wspólnego adresy?
Wyścigi lekkości na orbicie

@LightnessRacesinOrbit nie zostało zamknięte, prawda? czy powiedziałeś to ostatnim 3 facetom, którzy skomentowali? to po prostu staje się bardziej istotne dla tego, czego szukałem. może pomóc komuś innemu. i co mają z tym wspólnego adresy? kto czyta adresy dziesiętnie? to jest prawdziwy przykład.
Puddle

Opublikowanie tej samej odpowiedzi, która została już opublikowana prawie dziewięć lat temu, nie jest uważane za przydatne, a to wprowadzenie wskaźników do równania wydaje się być niespodziewane - PO nie pyta o wskaźniki. Co więcej, nie, nie byłoby, unsigned longale std::intptr_t.
Wyścigi lekkości na orbicie

intptr_t = int ... uintptr_t = unsigned int ... czy adresy pamięci są teraz podpisane? a ile pamięci da ci int?
Puddle

Nie rozumiesz. intptr_tMoże przechowywać dowolny wskaźnik na platformie budowlanej; nie jest to [koniecznie] prawdą unsigned int. I znowu nic z tego nie ma znaczenia dla pytania. Żadnych dalszych odpowiedzi ode mnie
Lightness Races in Orbit
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.