C ++ chuyển đổi chuỗi hex thành số nguyên đã ký


135

Tôi muốn chuyển đổi một chuỗi hex thành một số nguyên có chữ ký 32 bit trong C ++.

Vì vậy, ví dụ, tôi có chuỗi hex "fffefffe". Biểu diễn nhị phân của cái này là 1111111111111101111111111111110. Biểu diễn số nguyên đã ký của cái này là: -65538.

Làm cách nào để thực hiện chuyển đổi này trong C ++? Điều này cũng cần phải làm việc cho các số không âm. Ví dụ: chuỗi hex "0000000A", là 00000000000000000000000000001010 ở dạng nhị phân và 10 ở dạng thập phân.


2
Ghi chú. Bạn sẽ chỉ nhận được -65538 cho các hệ thống có sizeof (int) == 4
Martin York

3
@Martin York, Anh không nhắc đến int. "Số nguyên có chữ ký 32 bit" có thể là int32_t hoặc __int32, v.v.
Kirill V. Lyadvinsky

Câu trả lời:


230

sử dụng std::stringstream

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

ví dụ sau đây tạo ra -65538kết quả của nó:

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

Trong tiêu chuẩn C ++ 11 mới, có một vài chức năng tiện ích mới mà bạn có thể sử dụng! cụ thể, có một nhóm các hàm "chuỗi thành số" ( http://en.cppreference.com/w/cpp/opes/basic_opes/stolhttp://en.cppreference.com/w/cpp/opes/ basic_ chuỗi / stoul ). Về cơ bản, đây là các hàm bao mỏng xung quanh chuỗi C thành các hàm chuyển đổi số, nhưng biết cách xử lýstd::string

Vì vậy, câu trả lời đơn giản nhất cho mã mới hơn có thể sẽ giống như thế này:

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

LƯU Ý: Dưới đây là câu trả lời ban đầu của tôi, như bản chỉnh sửa nói không phải là câu trả lời hoàn chỉnh. Đối với một giải pháp chức năng, dán mã phía trên dòng :-).

Dường như lexical_cast<>được xác định là có ngữ nghĩa chuyển đổi luồng. Đáng buồn thay, các luồng không hiểu ký hiệu "0x". Vì vậy, cả boost::lexical_casttôi và tay tôi đều không xử lý tốt các chuỗi hex. Giải pháp trên mà tự đặt luồng đầu vào thành hex sẽ xử lý tốt.

Boost cũng có một số thứ để làm điều này, trong đó có một số khả năng kiểm tra lỗi tốt. Bạn có thể sử dụng nó như thế này:

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

Nếu bạn không muốn sử dụng boost, thì đây là phiên bản nhẹ của dàn diễn viên từ vựng không có lỗi kiểm tra:

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

mà bạn có thể sử dụng như thế này:

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

1
Khi tôi sử dụng phương pháp đó, tôi kết thúc với giá trị nguyên là 152144602
Clayton

@ jmanning2k, vâng, thật kỳ lạ khi cả boost và lexical_cast barf của tôi trên các chuỗi hex (ngay cả với tiền tố 0x) nếu tôi không đặt std :: hex trong chuỗi.
Evan Teran

1
@SteveWilkinson: Đọc đoạn văn bắt đầu bằng " EDIT ". Nó giải thích cách bạn cần sử dụngstd::hex
Evan Teran

1
Đối với stringstreammột người nên kiểm tra ss.good() && ss.eof()để đảm bảo không có lỗi xảy ra.
atoMerz

1
"stoul" này tiết kiệm logic của tôi
vincenzopalazzo

60

Đối với một phương thức hoạt động với cả C và C ++, bạn có thể muốn xem xét sử dụng hàm thư viện chuẩn 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
Bạn nên sử dụng strtoulkhông strtol. Sẽ có + overflowkhi sử dụng strtol. Với strtoulsẽ không có tràn và giá trị trả về sẽ được chuyển đổi longđể tạo ra kết quả chính xác (-65538). Vì vậy, câu trả lời của bạn gần như đúng :)
Kirill V. Lyadvinsky

8
+1. Bởi vì strtol (hoặc strtoul) nhanh hơn so với sử dụng chuỗi.
Kirill V. Lyadvinsky

27

Andy Hội trưởng, theo như C ++, tôi thích bạn, nhưng tôi có một vài mod:

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

Được sử dụng như

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

Bằng cách đó, bạn không cần một impl cho mỗi kiểu int.


1
Tôi cũng đã thực hiện bước đó, nhưng tôi thấy rằng tôi muốn hạn chế sự phổ biến của dấu ngoặc góc. Trong trường hợp này, tôi cảm thấy việc phá vỡ quy tắc "không trùng lặp mã" là hợp lý. :-)
Andy J Hội trưởng

Thật không may là nó cần thiết, nhưng được thực hiện độc đáo. Đã thêm vào tiêu đề / phần mở rộng STL / Boost cá nhân của tôi. Cảm ơn!
Tim Sylvester

Thật không may, điều này chỉ hoạt động cho chuyển đổi không dấu. Vì vậy, bạn không thể chuyển đổi 0xFFFFFFFF thành -1.
fmuecke

@fmuecke: Đó là vì 0xFFFFFFFF là một tràn số nguyên đã ký, là hành vi không xác định.
Billy ONeal

15

Ví dụ làm việc với strtoulsẽ là:

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

strtolchuyển đổi stringthành long. Trên máy tính của tôi numeric_limits<long>::max()cho 0x7fffffff. Rõ ràng 0xfffefffelà lớn hơn 0x7fffffff. Vì vậy, strtoltrả về MAX_LONGthay vì giá trị mong muốn. strtoulchuyển đổi stringthành unsigned longlý do tại sao không tràn trong trường hợp này.

Ok, strtolđang xem xét chuỗi đầu vào không phải là số nguyên có chữ ký 32 bit trước khi chuyển đổi. Mẫu hài hước với 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;
    }
}

Các mã trên in -65538trong giao diện điều khiển.


9

Đây là một phương pháp đơn giản và hiệu quả mà tôi tìm thấy ở nơi khác:

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

Xin lưu ý rằng bạn có thể thích sử dụng số nguyên dài / số nguyên dài không dấu, để nhận giá trị. Một lưu ý khác, hàm c_str () chỉ chuyển đổi chuỗi std :: thành const char *.

Vì vậy, nếu bạn đã có const char * sẵn sàng, chỉ cần tiếp tục sử dụng tên biến đó trực tiếp, như được hiển thị bên dưới [Tôi cũng đang hiển thị cách sử dụng biến dài không dấu cho số hex lớn hơn. Đừng nhầm lẫn với trường hợp có const char * thay vì chuỗi]:

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

Điều này hoạt động hoàn toàn tốt (miễn là bạn sử dụng các loại dữ liệu phù hợp theo nhu cầu của bạn).


6

Tôi đã có cùng một vấn đề ngày hôm nay, đây là cách tôi giải quyết nó để tôi có thể giữ 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...

(Tìm thấy trang này khi tôi đang tìm kiếm một cách ít may mắn hơn :-)

Chúc mừng, A.


1
Mã có lỗi biên dịch tầm thường - giá trị không được xác định (nên là outValue).
Gabi Davar

3

Điều này làm việc cho tôi:

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 

0

Thử cái này. Giải pháp này có một chút rủi ro. Không có kiểm tra. Chuỗi chỉ phải có giá trị hex và độ dài chuỗi phải phù hợp với kích thước loại trả về. Nhưng không cần thêm tiêu đề.

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

Sử dụng:

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

Lưu ý: độ dài của chuỗi phải chia hết cho 2

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.