Chuyển đổi chuỗi hex (char []) thành int?


82

Tôi có một char [] có chứa một giá trị chẳng hạn như "0x1800785" nhưng hàm tôi muốn cung cấp giá trị yêu cầu một int, làm cách nào để chuyển đổi giá trị này thành một int? Tôi đã tìm kiếm xung quanh nhưng không thể tìm thấy câu trả lời. Cảm ơn.


1
có thể bản sao của chuỗi chuyển đổi thành ký int
bitmask

Câu trả lời:


207

Bạn đã thử strtol()chưa?

strtol - chuyển đổi chuỗi thành một số nguyên dài

Thí dụ:

const char *hexstring = "abcdef0";
int number = (int)strtol(hexstring, NULL, 16);

Trong trường hợp biểu diễn chuỗi của số bắt đầu bằng 0xtiền tố, người ta phải sử dụng 0 làm cơ sở:

const char *hexstring = "0xabcdef0";
int number = (int)strtol(hexstring, NULL, 0);

(Cũng có thể chỉ định một cơ sở rõ ràng chẳng hạn như 16, nhưng tôi không khuyên bạn nên giới thiệu dư thừa.)


4
Nếu chuỗi hex luôn được giới thiệu bởi một "0x"như đã cho trong câu hỏi, bạn chỉ nên sử dụng 0thay vì 16.
Jens Gustedt

16
@KelvinHu Đừng tin cplusplus.com , thật là tào lao. Nếu có, hãy truy cập cppreference.com .

1
@KelvinHu Rất nhiều thông tin không chính xác về mặt kỹ thuật hoặc diễn đạt lỏng lẻo trên trang web đó và nếu bạn thấy các lập trình viên C ++ xung quanh SO, tất cả họ sẽ không khuyến khích dựa vào đó vì lý do này.

1
@ H2CO3 Được rồi, tôi hiểu rồi, tôi mới làm quen với C ++, vì vậy tôi tìm kiếm cái này bằng google và nó đặt cplusplus.com ở vị trí chính. Thực sự cảm ơn thông tin của bạn.
Kelvin Hu

3
Như một lưu ý phụ strtolcung cấp cho bạn loại longrất tốt khi xử lý một hex rất lớn. Sử dụng strtollcho loại long longcho hexes thậm chí lớn hơn.
Steven Lu

20

Hoặc nếu bạn muốn triển khai riêng, tôi đã viết hàm nhanh này làm ví dụ:

/**
 * hex2int
 * take a hex string and convert it to a 32bit number (max 8 hex digits)
 */
uint32_t hex2int(char *hex) {
    uint32_t val = 0;
    while (*hex) {
        // get current character then increment
        uint8_t byte = *hex++; 
        // transform hex character to the 4bit equivalent number, using the ascii table indexes
        if (byte >= '0' && byte <= '9') byte = byte - '0';
        else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;    
        // shift 4 to make space for new digit, and add the 4 bits of the new digit 
        val = (val << 4) | (byte & 0xF);
    }
    return val;
}

1
Rất hữu ích cho các nền tảng có tài nguyên hạn chế như vi điều khiển.
Mubin Icyer

19

Một cái gì đó như thế này có thể hữu ích:

char str[] = "0x1800785";
int num;

sscanf(str, "%x", &num);
printf("0x%x %i\n", num, num); 

Đọc man sscanf


1
Điều này gây ra hành vi không xác định, %xphải nhận địa chỉ của một unsigned int. Và ngay cả khi bạn khắc phục điều đó, nếu số được đại diện bởi chuỗi lớn hơn UINT_MAXthì đó là hành vi không xác định một lần nữa
MM

10

Giả sử ý bạn là một chuỗi, vậy còn strtol thì sao?


Ban đầu tôi đã liên kết với strtod (-> double) thay vì strtol. Tôi đoán ai đó đã nhìn thấy nó khi tôi đang chỉnh sửa.
James McLaughlin

Chà, đó không phải là sai lầm nhiều ... trình biên dịch tự động chuyển giá trị trả về nếu được cung cấp cho một int. +1, btw.

Tôi không nghĩ rằng một đôi có thể lưu trữ tất cả các giá trị có thể một số nguyên 32-bit có thể (trên thực tế, không ai biết nếu điều này là đúng Tôi không phải 100% trên đại diện số điểm nổi?.)
James McLaughlin

4
@JamesMcLaughlin Nó có thể. IEEE kép 64 bit có độ chính xác tích phân đến khoảng 2 ^ 53.

Chi tiết, "IEEE kép 64 bit có độ chính xác tích phân đến khoảng 2 ^ 53." và một bit dấu. Vì vậy, nó có thể xử lý int54_tuint53_t.
Chux - Khôi phục Monica

5

Sử dụng strtolnếu bạn có sẵn libc như câu trả lời trên đề xuất. Tuy nhiên, nếu bạn thích nội dung tùy chỉnh hoặc sử dụng vi điều khiển không có libc, bạn có thể muốn một phiên bản được tối ưu hóa một chút mà không cần phân nhánh phức tạp.

#include <inttypes.h>


/**
 * xtou64
 * Take a hex string and convert it to a 64bit number (max 16 hex digits).
 * The string must only contain digits and valid hex characters.
 */
uint64_t xtou64(const char *str)
{
    uint64_t res = 0;
    char c;

    while ((c = *str++)) {
        char v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8);
        res = (res << 4) | (uint64_t) v;
    }

    return res;
} 

Phép thuật dịch chuyển bit tổng hợp thành: Chỉ cần sử dụng 4 bit cuối cùng, nhưng nếu nó là một chữ số không phải là chữ số, thì cũng thêm 9.


Nếu bạn có cơ hội, bạn có thể giải thích thêm một chút về cách dòng "v =" hoạt động không? Tôi quản lý để triển khai chức năng của bạn cho các mục đích của riêng tôi như sau:unsigned long long int hextou64(char *str) { unsigned long long int n = 0; char c, v; for (unsigned char i = 0; i < 16; i++) { c = *(str + i); v = (c & 0xF) + (c >> 6) | ((c >> 3) & 0x8); n = (n << 4) | (unsigned long long int)v; } return n; }
iamoumuamua

Để tôi xem liệu tôi có thể làm được điều này với nhau hay không ^^ Tôi đoán nó khá phức tạp .. Tiền đề: '0' - '9' tất cả đều bắt đầu bằng '0011 ????' trong hệ nhị phân. 'A' - 'F' đều bắt đầu bằng '0100 ????' trong hệ nhị phân. Chúng tôi có thể sử dụng thông tin này để tránh phân nhánh. (c & 0xF) → chỉ bốn bit cuối cùng chứa giá trị. Đây đã là giá trị chính xác cho '0' - '9'. Trong trường hợp 'A' - 'F', chúng ta phải thêm số thập phân 9 để có giá trị chính xác. (Ví dụ: Hex C kết thúc bằng 0011. Đó là 3. Nhưng chúng ta muốn 12. Do đó chúng ta phải thêm 9. (c >> 6) | ((c >> 3) & 0x8)→ đánh giá là 9 trong trường hợp là một chữ cái, hoặc 0 trong trường hợp là số thập phân. Đúng vậy, khá khó hiểu :)
LimeRed

3

Hãy thử khối mã dưới đây, nó phù hợp với tôi.

char *p = "0x820";
uint16_t intVal;
sscanf(p, "%x", &intVal);

printf("value x: %x - %d", intVal, intVal);

Đầu ra là:

value x: 820 - 2080

3

Vì vậy, sau một thời gian tìm kiếm và phát hiện ra rằng strtol chạy khá chậm, tôi đã code hàm của riêng mình. Nó chỉ hoạt động đối với chữ hoa trên các chữ cái, nhưng thêm chức năng viết thường không phải là vấn đề.

int hexToInt(PCHAR _hex, int offset = 0, int size = 6)
{
    int _result = 0;
    DWORD _resultPtr = reinterpret_cast<DWORD>(&_result);
    for(int i=0;i<size;i+=2)
    {
        int _multiplierFirstValue = 0, _addonSecondValue = 0;

        char _firstChar = _hex[offset + i];
        if(_firstChar >= 0x30 && _firstChar <= 0x39)
            _multiplierFirstValue = _firstChar - 0x30;
        else if(_firstChar >= 0x41 && _firstChar <= 0x46)
            _multiplierFirstValue = 10 + (_firstChar - 0x41);

        char _secndChar = _hex[offset + i + 1];
        if(_secndChar >= 0x30 && _secndChar <= 0x39)
            _addonSecondValue = _secndChar - 0x30;
        else if(_secndChar >= 0x41 && _secndChar <= 0x46)
            _addonSecondValue = 10 + (_secndChar - 0x41);

        *(BYTE *)(_resultPtr + (size / 2) - (i / 2) - 1) = (BYTE)(_multiplierFirstValue * 16 + _addonSecondValue);
    }
    return _result;
}

Sử dụng:

char *someHex = "#CCFF00FF";
int hexDevalue = hexToInt(someHex, 1, 8);

1 vì hex mà chúng ta muốn chuyển đổi bắt đầu ở độ lệch 1 và 8 vì đó là độ dài hex.

Speedtest (1.000.000 cuộc gọi):

strtol ~ 0.4400s
hexToInt ~ 0.1100s

2

Một giải pháp nhanh chóng và bẩn thỉu:

// makes a number from two ascii hexa characters
int ahex2int(char a, char b){

    a = (a <= '9') ? a - '0' : (a & 0x7) + 9;
    b = (b <= '9') ? b - '0' : (b & 0x7) + 9;

    return (a << 4) + b;
}

Bạn phải chắc chắn rằng đầu vào của bạn là chính xác, không bao gồm xác thực (có thể nói đó là C). Điều tốt là nó khá nhỏ gọn, nó hoạt động với cả 'A' thành 'F' và 'a' thành 'f'.

Cách tiếp cận dựa trên vị trí của các ký tự bảng chữ cái trong bảng ASCII, hãy xem ví dụ như Wikipedia ( https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png ). Truyện ngắn, các con số nằm dưới các ký tự, vì vậy các ký tự số (0 đến 9) dễ dàng được chuyển đổi bằng cách trừ mã cho số không. Các ký tự chữ cái (A đến F) được đọc bằng số 0 khác với ba bit cuối cùng (làm cho nó hoạt động hiệu quả với cả chữ hoa hoặc chữ thường), trừ đi một (vì sau khi che bit, bảng chữ cái bắt đầu ở vị trí một) và thêm mười ( vì A đến F đại diện cho giá trị thứ 10 đến thứ 15 trong mã thập lục phân). Cuối cùng, chúng ta cần kết hợp hai chữ số tạo thành nibble dưới và trên của số được mã hóa.

Ở đây chúng tôi đi với cùng một cách tiếp cận (với các biến thể nhỏ):

#include <stdio.h>

// takes a null-terminated string of hexa characters and tries to 
// convert it to numbers
long ahex2num(unsigned char *in){

    unsigned char *pin = in; // lets use pointer to loop through the string
    long out = 0;  // here we accumulate the result

    while(*pin != 0){
        out <<= 4; // we have one more input character, so 
                   // we shift the accumulated interim-result one order up
        out +=  (*pin < 'A') ? *pin & 0xF : (*pin & 0x7) + 9; // add the new nibble
        pin++; // go ahead
    }

    return out;
}

// main function will test our conversion fn
int main(void) {

    unsigned char str[] = "1800785";  // no 0x prefix, please
    long num;

    num = ahex2num(str);  // call the function

    printf("Input: %s\n",str);  // print input string
    printf("Output: %x\n",num);  // print the converted number back as hexa
    printf("Check: %ld = %ld \n",num,0x1800785);  // check the numeric values matches

    return 0;
}

1
Sau khi đăng câu trả lời tôi phát hiện ra tôi đã bỏ lỡ bài s @LimeRed', mà là rất dễ thương
Simon

1

Đây là một hàm để chuyển đổi trực tiếp mảng char chứa thập lục phân thành một số nguyên mà không cần thêm thư viện:

int hexadecimal2int(char *hdec) {
    int finalval = 0;
    while (*hdec) {
        
        int onebyte = *hdec++; 
        
        if (onebyte >= '0' && onebyte <= '9'){onebyte = onebyte - '0';}
        else if (onebyte >= 'a' && onebyte <='f') {onebyte = onebyte - 'a' + 10;}
        else if (onebyte >= 'A' && onebyte <='F') {onebyte = onebyte - 'A' + 10;}  
        
        finalval = (finalval << 4) | (onebyte & 0xF);
    }
    finalval = finalval - 524288;
    return finalval;
}

0

Tôi đã sử dụng thư viện để thực hiện chuyển đổi Hệ thập lục phân / Thập phân mà không cần sử dụng stdio.h. Rất đơn giản để sử dụng:

unsigned hexdec (const char *hex, const int s_hex);

Trước khi chuyển đổi đầu tiên, hãy nhập số hóa mảng được sử dụng để chuyển đổi với:

void init_hexdec ();

Đây là liên kết trên github: https://github.com/kevmuret/libhex/


0

tôi đã làm một điều tương tự, nghĩ rằng nó có thể giúp ích cho bạn, nó thực sự làm việc cho tôi

int main(){ int co[8],i;char ch[8];printf("please enter the string:");scanf("%s",ch);for(i=0;i<=7;i++){if((ch[i]>='A')&&(ch[i]<='F')){co[i]=(unsigned int)ch[i]-'A'+10;}else if((ch[i]>='0')&&(ch[i]<='9')){co[i]=(unsigned int)ch[i]-'0'+0;}}

ở đây tôi chỉ lấy một chuỗi 8 ký tự. nếu bạn muốn bạn có thể thêm logic tương tự cho 'a' thành 'f' để cung cấp các giá trị hex tương đương của chúng, tôi đã không làm điều đó vì tôi không cần nó.


-1

Sử dụng xtoi (stdlib.h). Chuỗi có "0x" là hai chỉ mục đầu tiên nên hãy cắt bớt val [0] và val [1] bằng cách gửi xtoi & val [2].

xtoi( &val[2] );


xtoikhông phải là một chức năng tiêu chuẩn
MM

-5

Tôi biết điều này thực sự cũ nhưng tôi nghĩ các giải pháp trông quá phức tạp. Hãy thử điều này trong VB:

Public Function HexToInt(sHEX as String) as long
Dim iLen as Integer
Dim i as Integer 
Dim SumValue as Long 
Dim iVal as long
Dim AscVal as long

    iLen = Len(sHEX)
    For i = 1 to Len(sHEX)
      AscVal = Asc(UCase(Mid$(sHEX, i,  1)))
      If AscVal >= 48 And AscVal  <= 57 Then
        iVal  = AscVal - 48
      ElseIf AscVal >= 65 And AscVal <= 70 Then
        iVal = AscVal - 55
      End If 
      SumValue = SumValue + iVal * 16 ^ (iLen- i)
    Next i 
    HexToInt  = SumValue
End Function 
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.