Как преобразовать символ ASCII HEX в его значение (0-15)?

Я пишу анализатор строк, и мне пришла в голову мысль, что могут быть действительно интересные способы преобразования шестнадцатеричного символа ASCII [0-9A-Fa-f] в его числовое значение.

Каковы самые быстрые, самые короткие, самые элегантные или самые непонятные способы преобразования [0-9A-Fa-f] в его значение между 0 и 15?

Предположим, если хотите, что символ является допустимым шестнадцатеричным символом.

У меня нет шансов, поэтому я попробую самый скучный.

( c <= '9' ) ? ( c - '0' ) : ( (c | '\x60') - 'a' + 10 )

person philcolbourn    schedule 23.03.2010    source источник
comment
какой-то конкретный язык?   -  person scunliffe    schedule 23.03.2010
comment
Я играю с C, но не стесняйтесь.   -  person philcolbourn    schedule 23.03.2010


Ответы (6)


(c&15)+(c>>6)*9

В ответ на «Как это работает» он отбрасывает достаточное количество битов, чтобы числа отображались в [0:9], а буквы — в [1:6], а затем добавляло 9 для букв. c>>6 заменяет if (c >= 64) ....

person Marcelo Cantos    schedule 23.03.2010
comment
Первая часть, c&15, даст вам 0-9 для цифр (они от 0x30 до 0x39, а 0x0F очищает верхние 4 цифры => 0x00-0x09). Для букв AF вы получите 1–6 (A — 0x41, F — 0x46 и 0x0F и т. д.). Теперь для букв вам нужно 10–15, а не 1–6, поэтому вам нужно добавить 9, а только для писем. Ну, цифры 0011xxxx, буквы (A-F) 0100xxxx, сдвиньте это вправо 6 раз, и вы получите 00 для цифр, 01 для букв. Умножьте это на 9, и в результате получится 0 для цифр и 9 для букв. теперь добавьте это к первой части, и все готово. - person David V.; 23.03.2010
comment
+1, красиво. не работает со строчными буквами, но его можно изменить. - person David V.; 23.03.2010
comment
@David V: я думаю, что это работает для верхнего и нижнего регистра. Я протестировал его, и он действительно работает. - person philcolbourn; 23.03.2010
comment
Без сравнений и прыжков это, наверное, самое быстрое и маленькое. 14 байт с gcc -O3. - person philcolbourn; 23.03.2010
comment
gcc умножает на 9 на (c>>6<<3)+(c>>6) или c/64*8 + c/64 - person philcolbourn; 23.03.2010
comment
Я исправлен, символы нижнего регистра - 0110xxxx, что по-прежнему дает 1 при сдвиге вправо 6 раз. Я написал слишком быстро. - person David V.; 23.03.2010
comment
Очень хорошо! Это действительно работает как для верхнего, так и для нижнего регистра - проверено на C. - person EMP; 24.03.2010
comment
Хороший! Но есть ли хороший способ убедиться, что вводимый символ находится в правильном диапазоне? Что-то короче, чем ('0'‹=c && c‹= '9') || ('A' ‹= c && ... ) || ... - person calandoa; 26.04.2011
comment
@calandoa: Я думаю, что это сломает банк, поскольку точные диапазоны символов не достигают хороших границ степени двойки. Тем не менее, вы можете удалить ветвление из теста, используя побитовые вместо логических операторов — if (('0'<=c)&(c<='9)|('A'<=c)&(c<='F')|('a'<=c)&(c<='f')) ... — хотя я понятия не имею, что это вам даст. - person Marcelo Cantos; 26.04.2011

Один простой способ — найти его в строке:

int n = "0123456789ABCDEF".IndexOf(Char.ToUpper(c));

Другой способ - преобразовать его в цифру, а затем проверить, является ли он символом:

int n = Char.ToUpper(c) - '0';
if (n > 9) n -= 7;
person Guffa    schedule 23.03.2010

В C вы можете что-то вроде:

if(isdigit(c)) 
  num = c -'0';
else if(c>='a' && c<='f')
  num = 10 + c - 'a';
else if(c>='A' && c<='F')
  num = 10 + c - 'A';
person codaddict    schedule 23.03.2010

В JavaScript:

num = parseInt(hex, 16);
person scunliffe    schedule 23.03.2010

Вот скучная версия C (а мой C очень ржавый, так что, вероятно, он тоже неправильный).

char parseIntChar(const char p) {
  char c[2];
  c[0]=p;
  c[1]=0;
  return strtol(c,0,16);
}
person Joachim Sauer    schedule 23.03.2010

person    schedule
comment
Этот ответ можно было бы улучшить с небольшим объяснением того, как работает код. - person Paul Turner; 11.12.2012