我需要像這樣轉換十六進制編碼的字串:
char hstr[9] = "61626364"; // characters abcd\0
進入
"abcd" // characters as hex: 0x61 0x62 0x63 0x64
// hex "digits" a-f are always lowercase
此刻我寫了這個函式:
#include <stdlib.h>
void htostr(char* hexstr, char* str) {
int len = strlen(hexstr);
for (int i = 0; i < len/2; i ) // edit: fixed bounds
{
char input[3] = { hexstr[2 * i], hexstr[2 * i 1], 0 };
*(str i) = (char)strtol(input, NULL, 16);
}
}
我正在使用strtol函式來完成這項作業。
我覺得我浪費了 3 個位元組的記憶體用于input陣列和一些處理器時間來復制兩個位元組并以 0 結尾,因為strtol函式沒有像“長度”這樣的引數。
該代碼應該在一個非常繁忙的微控制器上運行,字串很長(盡快釋放所使用的記憶體是個好主意hexstr)。
問題是:有沒有更有效的方法來做到這一點,而無需從頭開始撰寫我自己的轉換器?
“從頭開始”是指不使用函式標準庫的低級轉換。
uj5u.com熱心網友回復:
當您被允許臨時更改輸入字串時:
void htostr_1(char* hexstr, char* str) {
int len = strlen(hexstr);
for (int i = 0; 2 * i 2 <= len; i )
{
char tmp = hexstr[2 * i 2];
hexstr[2 * i 2] = 0;
str[i] = (char)strtol(hexstr 2 * i, NULL, 16);
hexstr[2 * i 2] = tmp;
}
}
在終止字串之前保存下一個位元組以在以下位置撤消它strtol:https ://godbolt.org/z/zdMdKrY7n
附帶說明:回圈的結束條件for錯誤,您訪問越界:https ://godbolt.org/z/ra87cWocY
如果您還想保存int len不必要的strlen呼叫:
void htostr_2(char* hexstr, char* str) {
while (*hexstr)
{
char tmp = hexstr[2];
hexstr[2] = 0;
*str = (char)strtol(hexstr, NULL, 16);
hexstr[2] = tmp;
hexstr = 2;
}
}
uj5u.com熱心網友回復:
如果你真的想修剪它:
void htostr(char* hexstr, char* str) {
int i = 0;
while (hexstr[2*i]) {
{
str[i] = 0;
for (int j=0; j<2; j ) {
str[i] <<= 4;
char c = hexstr[2*i j];
if (c >= '0' && c <= '9') {
str[i] |= c - '0';
} else if (c >= 'A' && c <= 'F') {
str[i] |= c - 'A' 10;
} else if (c >= 'a' && c <= 'f') {
str[i] |= c - 'a' 10;
}
}
i ;
}
}
uj5u.com熱心網友回復:
strtol您可以創建一個將字符0..9和A..F轉換為int( 0x0to 0xF)的函式,而不是復制兩個字符并使用。
#include <ctype.h>
int toval(char ch) {
if (isdigit((unsigned char)ch)) return ch - '0';
return toupper((unsigned char)ch) - 'A' 0x10;
}
然后回圈遍歷字串并將結果相加將非常簡單:
void htostr(char *wr, const char *rd) {
for (; rd[0] != '\0' && rd[1] != '\0'; rd = 2, wr) {
// multiply the first with 0x10 and add the value of the second
*wr = toval(rd[0]) * 0x10 toval(rd[1]);
}
*wr = '\0'; // null terminate
}
示例用法:
#include <stdio.h>
int main() {
char hstr[] = "61626364";
char res[1 sizeof hstr / 2];
htostr(res, hstr);
printf(">%s<\n", res);
}
uj5u.com熱心網友回復:
有很多方法可以做到這一點,并且有效地取決于典型的字串長度、使用頻率、允許的記憶體占用等。
下面是一個相當快地完成這項作業的。
回圈遍歷成對的十六進制數字并通過查表計算字符代碼。
#include <ctype.h>
static const unsigned char val[] = { //
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, //
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9, //
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14, ['F'] = 15, //
['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15, //
};
void htostr_alt(const char* hexstr, char* str) {
// Best to use is...() functions with unsigned char data
const unsigned char *uhexstr = (const unsigned char *) hexstr;
while (isxdigit(uhexstr[0]) && isxdigit(uhexstr[1])) {
*str = (char) (val[uhexstr[0]]*16u uhexstr[uhexstr[1]]);
uhexstr = 2;
}
*str = '\0';
// Consider returning something useful, like where did input stop.
// return (char *) uhexstr;
}
為了避免在分配字符時實作定義的行為:
void htostr_alt2(const char* hexstr, char* str) {
const unsigned char *uhexstr = (const unsigned char *) hexstr;
unsigned char *ustr = (const unsigned char *) str;
while (isxdigit(uhexstr[0]) && isxdigit(uhexstr[1])) {
*ustr = (unsigned char) (val[uhexstr[0]]*16u uhexstr[uhexstr[1]]);
uhexstr = 2;
}
*ustr = '\0';
}
即使字串長度超過INT_MAX,代碼也可以作業,接受const輸入字串,在任何非十六進制數字對上停止并且只有 1 通過源字串。
如果你不喜歡這個功能isxdigit(),可以很容易地撰寫代碼unsigned char my_isxdigit[256]。
uj5u.com熱心網友回復:
假設您事先知道字串格式并且它永遠不會超過 8 位,那么請保持簡單。這既高效又可讀:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
int main()
{
char hstr[9] = "61626364";
uint32_t n = strtoul(hstr, 0, 16);
char str[5] =
{
(n >> 24) & 0xFFu,
(n >> 16) & 0xFFu,
(n >> 8) & 0xFFu,
(n >> 0) & 0xFFu,
'\0'
};
puts(str);
}
至于手動推出十六進制字串到整數的轉換(我真的不明白你為什么會在這種情況下),最有效但稍微消耗閃存的代碼是這樣的:
const uint8_t LUT[128] =
{
['0'] = 0, ['1'] = 1, /* and so on... */
['A'] = 10, ['B'] = 11, /* and so on... */
};
...
uint8_t val = LUT[str[i]];
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/448824.html
