C ++ แปลงสตริงฐานสิบหกเป็นจำนวนเต็มที่ลงนาม


137

ฉันต้องการแปลงสตริงฐานสิบหกเป็นจำนวนเต็ม 32 บิตเซ็นชื่อใน C ++

ตัวอย่างเช่นฉันมีสตริงเลขฐานสิบหก "fffefffe" การแทนค่าฐานสองของสิ่งนี้คือ 11111111111111101111111111111110 การแสดงเลขจำนวนเต็มที่มีลายเซ็นคือ: -65538

ฉันจะแปลงนี้ใน C ++ ได้อย่างไร สิ่งนี้จำเป็นต้องใช้กับตัวเลขที่ไม่เป็นลบ ตัวอย่างเช่นสตริงเลขฐานสิบหก "0000000A" ซึ่งเป็น 00000000000000000000000000001010 เป็นเลขฐานสองและ 10 ในฐานสิบ


2
บันทึก. คุณจะได้รับ -65538 สำหรับระบบที่ sizeof (int) == 4
Martin York

3
@ Martin York, intเขาไม่ได้พูดถึง "จำนวนเต็มที่ลงนาม 32 บิต" อาจเป็น int32_t หรือ __int32 เป็นต้น
Kirill V. Lyadvinsky

คำตอบ:


232

ใช้ std::stringstream

unsigned int x;   
std::stringstream ss;
ss << std::hex << "fffefffe";
ss >> x;

ตัวอย่างต่อไปนี้ก่อให้เกิด-65538ผลลัพธ์:

#include <sstream>
#include <iostream>

int main() {
    unsigned int x;   
    std::stringstream ss;
    ss << std::hex << "fffefffe";
    ss >> x;
    // output it as a signed type
    std::cout << static_cast<int>(x) << std::endl;
}

ในมาตรฐาน C ++ 11 ใหม่มีฟังก์ชั่นยูทิลิตี้ใหม่บางอย่างที่คุณสามารถใช้ประโยชน์ได้! โดยเฉพาะอย่างยิ่งมีตระกูลของฟังก์ชัน "string to number" ( http://en.cppreference.com/w/cpp/string/basic_string/stolและhttp://en.cppreference.com/w/cpp/string/ basic_string / stoul ) สิ่งเหล่านี้เป็นกระดาษห่อบาง ๆ รอบ ๆ ฟังก์ชันการแปลงสตริงเป็นตัวเลขของ C แต่รู้วิธีจัดการกับไฟล์std::string

ดังนั้นคำตอบที่ง่ายที่สุดสำหรับโค้ดรุ่นใหม่อาจมีลักษณะดังนี้:

std::string s = "0xfffefffe";
unsigned int x = std::stoul(s, nullptr, 16);

หมายเหตุ:ด้านล่างนี้เป็นคำตอบเดิมของฉันซึ่งตามที่การแก้ไขระบุว่าไม่ใช่คำตอบที่สมบูรณ์ สำหรับวิธีแก้ปัญหาที่ใช้งานได้ให้ติดรหัสไว้เหนือบรรทัด :-)

ดูเหมือนว่าเนื่องจากlexical_cast<>ถูกกำหนดให้มีความหมายของการแปลงสตรีม น่าเศร้าที่สตรีมไม่เข้าใจสัญลักษณ์ "0x" ดังนั้นทั้งboost::lexical_castมือของฉันและมือของฉันก็กลิ้งไปมาก็ไม่สามารถจัดการกับสตริง hex ได้ดี วิธีแก้ปัญหาข้างต้นซึ่งตั้งค่าสตรีมอินพุตเป็นฐานสิบหกด้วยตนเองจะจัดการได้ดี

Boost มีบางสิ่งที่ต้องทำเช่นกันซึ่งมีความสามารถในการตรวจสอบข้อผิดพลาดที่ดีเช่นกัน คุณสามารถใช้งานได้ดังนี้:

try {
    unsigned int x = lexical_cast<int>("0x0badc0de");
} catch(bad_lexical_cast &) {
    // whatever you want to do...
}

หากคุณไม่รู้สึกอยากใช้บูสต์นี่คือคำศัพท์เวอร์ชันเบาซึ่งไม่มีการตรวจสอบข้อผิดพลาด:

template<typename T2, typename T1>
inline T2 lexical_cast(const T1 &in) {
    T2 out;
    std::stringstream ss;
    ss << in;
    ss >> out;
    return out;
}

ซึ่งคุณสามารถใช้ได้ดังนี้:

// though this needs the 0x prefix so it knows it is hex
unsigned int x = lexical_cast<unsigned int>("0xdeadbeef"); 

2
เมื่อฉันใช้วิธีนั้นฉันจะได้ค่าจำนวนเต็ม 152144602
Clayton

@ jmanning2k ใช่มันแปลกที่ทั้ง boost และ lexical_cast barf ของฉันบนสตริง hex (แม้จะมีคำนำหน้า 0x) ถ้าฉันไม่ใส่ std :: hex ในสตริง
Evan Teran

1
@SteveWilkinson: อ่านย่อหน้าที่ขึ้นต้นด้วย " EDIT " อธิบายวิธีการใช้งานstd::hex
Evan Teran

1
สำหรับstringstreamหนึ่งควรตรวจสอบss.good() && ss.eof()ให้แน่ใจว่าไม่มีข้อผิดพลาดเกิดขึ้น
atoMerz

1
"เหม็น" นี้บันทึกตรรกะของฉัน
vincenzopalazzo

60

สำหรับวิธีการที่ใช้ได้กับทั้ง C และ C ++ คุณอาจต้องการพิจารณาใช้ strtol ฟังก์ชันไลบรารีมาตรฐาน ()

#include <cstdlib>
#include <iostream>
using namespace std;

int main() {
    string s = "abcd";
    char * p;
    long n = strtol( s.c_str(), & p, 16 );
    if ( * p != 0 ) { //my bad edit was here
        cout << "not a number" << endl;
    }
    else {
        cout << n << endl;
    }
}

2
คุณควรใช้ไม่ได้strtoul strtolจะมี+ overflowเมื่อใช้ strtol โดยstrtoulจะไม่มีการล้นและค่าที่ส่งคืนจะถูกแปลงlongเป็นผลลัพธ์ที่ถูกต้อง (-65538) ดังนั้นคำตอบของคุณเกือบถูกต้อง :)
Kirill V. Lyadvinsky

8
+1. เนื่องจาก strtol (หรือ strtoul) เร็วกว่าการใช้ stringstream
Kirill V. Lyadvinsky

27

Andy Buchanan เท่าที่ยึด C ++ ไปฉันชอบของคุณ แต่ฉันมี mods ไม่กี่ตัว:

template <typename ElemT>
struct HexTo {
    ElemT value;
    operator ElemT() const {return value;}
    friend std::istream& operator>>(std::istream& in, HexTo& out) {
        in >> std::hex >> out.value;
        return in;
    }
};

ใช้แล้วชอบ

uint32_t value = boost::lexical_cast<HexTo<uint32_t> >("0x2a");

ด้วยวิธีนี้คุณไม่จำเป็นต้องมีหนึ่งนัยต่อประเภท int


1
ฉันจะทำตามขั้นตอนนั้นด้วย แต่ฉันพบว่าฉันต้องการ จำกัด การขยายตัวของวงเล็บมุม สำหรับกรณีนี้ฉันรู้สึกว่าการละเมิดกฎ "อย่าทำรหัสซ้ำ" เป็นสิ่งที่ถูกต้อง :-)
Andy J Buchanan

โชคร้ายที่มันจำเป็น แต่ทำได้ดีมาก เพิ่มในส่วนขยาย / แก้ไข STL / Boost ส่วนตัวของฉัน ขอบคุณ!
Tim Sylvester

น่าเสียดายที่ใช้งานได้กับ Conversion ที่ไม่ได้ลงชื่อเท่านั้น คุณจึงไม่สามารถแปลง 0xFFFFFFFF เป็น -1
fmuecke

@fmuecke: นั่นเป็นเพราะ 0xFFFFFFFF เป็นจำนวนเต็มล้นที่มีลายเซ็นซึ่งเป็นพฤติกรรมที่ไม่ได้กำหนด
Billy ONeal

15

ตัวอย่างการทำงานด้วยstrtoulจะเป็น:

#include <cstdlib>
#include <iostream>
using namespace std;

int main() { 
    string s = "fffefffe";
    char * p;
    long n = strtoul( s.c_str(), & p, 16 ); 
    if ( * p != 0 ) {  
        cout << "not a number" << endl;
    }    else {  
        cout << n << endl;
    }
}

strtolแปลงstringเป็นlong. บนคอมพิวเตอร์ของฉันให้numeric_limits<long>::max() 0x7fffffffเห็นได้ชัดว่า0xfffefffeมีค่ามากกว่า0x7fffffffมีค่ามากกว่าดังนั้นstrtolผลตอบแทนMAX_LONGแทนที่จะเป็นค่าที่ต้องการ strtoulแปลงstringไปunsigned longว่าทำไมไม่มีล้นในกรณีนี้

ตกลง, strtolกำลังพิจารณาสตริงอินพุตไม่ใช่จำนวนเต็มลงนาม 32 บิตก่อนการแปลง ตัวอย่างตลกด้วยstrtol:

#include <cstdlib>
#include <iostream>
using namespace std;

int main() { 
    string s = "-0x10002";
    char * p;
    long n = strtol( s.c_str(), & p, 16 ); 
    if ( * p != 0 ) {  
        cout << "not a number" << endl;
    }    else {  
        cout << n << endl;
    }
}

โค้ดด้านบนพิมพ์-65538ในคอนโซล


9

นี่เป็นวิธีที่ง่ายและใช้ได้ผลที่ฉันพบจากที่อื่น:

string hexString = "7FF";
int hexNumber;
sscanf(hexString.c_str(), "%x", &hexNumber);

โปรดทราบว่าคุณอาจต้องการใช้จำนวนเต็มยาว / จำนวนเต็มยาวที่ไม่ได้ลงชื่อเพื่อรับค่า หมายเหตุอีกประการหนึ่งฟังก์ชั่น c_str () จะแปลง std :: string เป็น const char *

ดังนั้นหากคุณมี const char * พร้อมให้ใช้ชื่อตัวแปรนั้นโดยตรงดังที่แสดงด้านล่าง [ฉันกำลังแสดงการใช้ตัวแปรยาวที่ไม่ได้ลงชื่อสำหรับจำนวนฐานสิบหกที่ใหญ่กว่า อย่าสับสนกับกรณีที่มี const char * แทน string]:

const char *hexString = "7FFEA5"; //Just to show the conversion of a bigger hex number
unsigned long hexNumber; //In case your hex number is going to be sufficiently big.
sscanf(hexString, "%x", &hexNumber);

สิ่งนี้ใช้งานได้ดีอย่างสมบูรณ์ (หากคุณใช้ประเภทข้อมูลที่เหมาะสมตามความต้องการของคุณ)


6

วันนี้ฉันมีปัญหาเดียวกันนี่คือวิธีที่ฉันแก้ไขเพื่อให้ฉันสามารถเก็บ lexical_cast <> ได้

typedef unsigned int    uint32;
typedef signed int      int32;

class uint32_from_hex   // For use with boost::lexical_cast
{
    uint32 value;
public:
    operator uint32() const { return value; }
    friend std::istream& operator>>( std::istream& in, uint32_from_hex& outValue )
    {
        in >> std::hex >> outValue.value;
    }
};

class int32_from_hex   // For use with boost::lexical_cast
{
    uint32 value;
public:
    operator int32() const { return static_cast<int32>( value ); }
    friend std::istream& operator>>( std::istream& in, int32_from_hex& outValue )
    {
        in >> std::hex >> outvalue.value;
    }
};

uint32 material0 = lexical_cast<uint32_from_hex>( "0x4ad" );
uint32 material1 = lexical_cast<uint32_from_hex>( "4ad" );
uint32 material2 = lexical_cast<uint32>( "1197" );

int32 materialX = lexical_cast<int32_from_hex>( "0xfffefffe" );
int32 materialY = lexical_cast<int32_from_hex>( "fffefffe" );
// etc...

(พบหน้านี้เมื่อฉันกำลังมองหาวิธีที่ห่วยน้อยกว่า :-)

ไชโยอ.


1
โค้ดมีข้อผิดพลาดในการคอมไพล์เล็กน้อย - ไม่ได้กำหนดค่า outvalue (ควรเป็น outValue)
Gabi Davar

3

สิ่งนี้ใช้ได้ผลสำหรับฉัน:

string string_test = "80123456";
unsigned long x;
signed long val;

std::stringstream ss;
ss << std::hex << string_test;
ss >> x;
// ss >> val;  // if I try this val = 0
val = (signed long)x;  // However, if I cast the unsigned result I get val = 0x80123456 

1

เพียงใช้ stoi / stol / stoll เช่น:

std::cout << std::stol("fffefffe", nullptr, 16) << std::endl;

เอาต์พุต: 4294901758


0

ลองทำตามนี้ การแก้ปัญหานี้มีความเสี่ยงเล็กน้อย ไม่มีการตรวจสอบใด ๆ สตริงต้องมีค่าฐานสิบหกเท่านั้นและความยาวสตริงต้องตรงกับขนาดของชนิดการส่งคืน แต่ไม่จำเป็นต้องมีส่วนหัวเพิ่มเติม

char hextob(char ch)
{
    if (ch >= '0' && ch <= '9') return ch - '0';
    if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
    if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
    return 0;
}
template<typename T>
T hextot(char* hex)
{
    T value = 0;
    for (size_t i = 0; i < sizeof(T)*2; ++i)
        value |= hextob(hex[i]) << (8*sizeof(T)-4*(i+1));
    return value;
};

การใช้งาน:

int main()
{
    char str[4] = {'f','f','f','f'};
    std::cout << hextot<int16_t>(str)  << "\n";
}

หมายเหตุ: ความยาวของสตริงต้องหารด้วย 2 ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.