提问人:devMe 提问时间:10/26/2023 最后编辑:chqrliedevMe 更新时间:11/10/2023 访问量:195
如何在 c 中将十六进制字符串转换为十进制
How to convert a hex string to decimal in c
问:
所以我一直在学习 Kernighan 和 Ritchie 的 C 编程语言。在第二章中,练习 2.3 将十六进制数字字符串转换为等效整数值问题的函数。我看到了很多实现这个目标的方法:我没有复制解决方案,而是尝试实现我自己的解决方案版本。我仍然无法找出最短和最简单的方法来执行此操作并尝试实现可选的“0x”或“Ox”场景。下面是我的代码htoi
如何实现可选的“0x”?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char *reverse(char s[]) {
int len = strlen(s);
char *r = (char * )malloc(len + 1);;
int k = 0;
for (int i = len; i > 0; i--) {
r[k] = s[i - 1];
k++;
}
r[k] = '\0';
return r;
}
int check_valid(char *s) {
int valid = 0;
int not_valid = 0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
if ((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >= 'A' && s[i] <= 'F')) {
valid = 1;
} else
not_valid = 1;
}
if (!not_valid) {
return 1;
} else
return 0;
}
int rawint(char c ) {
if(isalpha(c)) {
return toupper(c) - 'A' + 10;
} else
return c - '0';
}
int htoi(char *s) {
char len = strlen(s);
int power = 1;
int dec = 0;
for (int i = 0; i < len; i++) {
dec += rawint(s[i]) * power;
power *= 16;
}
return dec;
}
int main() {
char s[] = "7abc";;
int check = check_valid(s);
char *r = reverse(s);
printf("reversed %s\n",r);
if (check) {
int dec = htoi(r);
printf("%d\n", dec);
} else {
printf("not OK");
}
}
答:
实现可选的“0x”或“Ox”方案。
当然,OP的意思是“实现可选的'0x'或'0X'场景”。(数字,不是字母)。'0'
'O'
如何实现可选的“0x”?
若要检查有效性,请添加一个测试,如果前导字符与 0x、0X 序列匹配。如果是这样,请跳过 2 。char
int check_valid(char *s){
// Add
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2;
}
错误
检查是否有效,不认为有效。valid
not_valid
""
// if( !not_valid ){
if(valid && !not_valid ){
return 1;
提示:与其向下传递字符串 2,不如迭代直到找到空字符。
//int len = strlen(s);
//for(int i = 0; i < len;i++){
for(int i = 0; s[i]; i++){
下面是您的代码,经过简化和修改,可以根据需要接受可选的“0x”前缀:
由于 htoi() 内部查找表,省去了 rawint()。
通过对 strchr() 返回的指针进行自己的内部检查来省去 check()
更新:sreverse() 已免除对 malloc() 和 strlen() 的调用
htoi() 接受带或不带可选“0x”前缀的字符串
这是可运行的代码,可以看到它是否有效。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/* sreverse()
-- no longer has to allocate memory -- uses single char temp value
-- removed call to strlen() with tiny while() loop
*/
char *sreverse(char *s)
{
int i=0, k=-1;
char ch;
while(s[++k]); //get strlen
for(i = 0, k-=1; k>i; i++, k--)
{
ch = s[k];
s[k] = s[i];
s[i] = ch;
}
return s;
}
/*
htoi()
Updated:
- Removed dependency on rawint()
- Removed call to strlen() with for() loop mod
- Uses Hex lookup table 'lut' and strchr()
- Performs its own internal check for valid Hex string on the fly
- Quits when valid '0x' prefix or end of string is encountered
- Sets parameter success flag for success/fail
*/
int htoi(char *s, int *success)
{
int i, dec=0, power = 1;
char *p, *sr = sreverse(s), lut[]="0123456789ABCDEF";
*success = 1; /* Assume success until proven otherwise */
for (i = 0; sr[i]; i++)
{
p = strchr(lut, toupper(sr[i]));
if(p)
{
dec += (int)(p - lut) * power;
power *= 16;
}
else
{
/* If we only failed look up because of "0x" prefix, that's a success */
*success = (!strcmp( &sr[i], "x0"));
break;
}
}
return dec;
}
#define SAMPLES 6
int main()
{
/* Samples are with/without "0x" prefix, upper and lowercase, etc */
char s[SAMPLES][15] = { "7FF", "0xf", "ff", "0xFFFFFFFF", "0x7FG", "Hello" };
int success, dec, runcount = 0;
do{
dec = htoi(s[runcount], &success);
printf("Result %-7s: %-4d from string: %s\n", success?"SUCCESS":"FAIL", dec, sreverse(s[runcount++]));
}while(runcount < SAMPLES);
return 0;
}
输出:
Result SUCCESS: 2047 from string: 7FF
Result SUCCESS: 15 from string: 0xf
Result SUCCESS: 255 from string: ff
Result SUCCESS: -1 from string: 0xFFFFFFFF
Result FAIL : 0 from string: 0x7FG
Result FAIL : 0 from string: Hello
试图提出自己的解决方案并没有错,但如果你读过本书的第 2 章,你就会看到 K&R 如何实现函数来执行从十进制表示到值的转换。atoi
int
书中的代码不接受初始空白字符,也不处理可选的 or 符号,但它确实以非常简洁有效的方式执行转换,一次从字符串中消耗一个数字。+
-
您的解决方案 OTOH 为字符串的反向副本分配内存(未释放),并以相反的顺序解析字符串,这会使计算复杂化,并且当您坚持检查 中的正确数字时,前缀(或在本例中为 or 后缀)被拒绝。主要问题是没有正确的语义:它应该直接转换。htoi
check_valid
x0
X0
htoi
0x7abc
31420
您可以通过以下方式修改书中的代码以处理十六进制表示形式和可选前缀:atoi
unsigned htoi(const char s[]) {
int i = 0;
unsigned n = 0;
/* skip an optional 0x or 0X prefix */
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
i = 2;
for (;;) {
/* parse the string one digit at a time, stop on the first non digit */
unsigned digit;
char c = s[i++];
if (c >= '0' && c <= '9')
digit = c - '0';
else
if (c >= 'a' && c <= 'f')
digit = c - 'a' + 10;
else
if (c >= 'A' && c <= 'F')
digit = c - 'A' + 10;
else
break;
n = 16 * n + digit;
}
return n;
}
请注意以下备注:
- type 比此转换更合适,以避免对大值(如
unsigned
int
0xFFFFFFFF
s
定义为 ,或等效地允许在不发出警告的情况下使用字符串文本和其他常量字符串进行调用。const char s[]
const char *s
htoi
htoi
还应该接受初始空格和可选符号,但这留给另一个练习- 与本书一样,停在第一个不是正确数字的字符上,它不会检查和拒绝格式错误的字符串。
atoi
htoi
- 该代码假定 和 是目标字符集中的连续字符序列。
abcdef
ABCDEF
i
应该使用 type 进行定义,以适应平台上的极长字符串,其中 .size_t
SIZE_MAX > INT_MAX
评论
当你学习编码时,通常很想添加更多的代码来解决问题。随着经验的积累,这种趋势会逐渐消失。我强调了这一点,并指出 OP 的代码有 4 个函数来为正在执行的测试提供服务。将代码分解为“辅助”函数非常好。但是,在这种情况下,可能会发生过多的卸载。main()
一个尚未注意到的缺陷是内存泄漏,因为指针返回的指针不是“d”。打字应该在打字之后打字,就像打字在打字之后一样。在一个好的程序中,堆分配和释放必须平衡。reverse()
free()
free()
malloc()
')'
'('
的功能是值得商榷的。很高兴您知道数据验证!应该受到保护吗?或者,它应该用它所给予的任何东西尽力而为吗?一个悬而未决的问题......(建议你应该去走一小段路。下面的代码演示如何使用标准库函数来测试字母是否是字母数字字符的特定子集之一。check_valid()
htoi()
!not_valid
rawint()
作为内联代码可能更好,而不是程序对从 ASCII 转换为其二进制值的每个字符执行函数调用。
最后,执行字符串的另外两次遍历(一次用于测量,一次用于转换字符)。到现在为止,角色已经变得狗耳朵了。htoi()
下面是另一个版本供您考虑(带有测试值套件)。学习是非常有益的。此代码很小且很密集,但有些复杂。每条线都发生了很多事情。这是为了展示另一个应该避免的极端。你希望你的读者理解代码,必须剖析每个符号与其他符号的关系......只是在下雨天玩得开心。
#include <stdio.h>
#include <string.h>
unsigned htoi(const char s[]) {
char c[2] = {0};
const char *lut = "0123456789ABCDEFabcdef";
unsigned d, n = 0, i = 0;
s += (s[0]=='0' && (s[1]=='x' || s[1]=='X')) ? 2 : 0;
while( i < 8 && (c[0] = s[i++]) != 0 && lut[d = strcspn(lut, c)] )
n = 16 * n + (( d<16 ) ? d : d-6);
return n;
}
int main( void ) {
char *s[] = {
"Hello", "7FF", "0xff", "0XFF", "ff",
"7FFFFFFF", "0xFFFFFFFF",
"0x12345678", "0x123456789",
"C0FFEE", "ABBA FOREVER",
NULL,
};
int i = 0;
do {
printf( "%13s - %10u - %10u\n", s[i], strtoul(s[i], NULL, 16), htoi( s[i] ) );
} while( s[++i]);
return 0;
}
输出:
Hello - 0 - 0
7FF - 2047 - 2047
0xff - 255 - 255
0XFF - 255 - 255
ff - 255 - 255
7FFFFFFF - 2147483647 - 2147483647
0xFFFFFFFF - 4294967295 - 4294967295
0x12345678 - 305419896 - 305419896
0x123456789 - 4294967295 - 305419896 // Which behaviour is correct?
C0FFEE - 12648430 - 12648430
ABBA FOREVER - 43962 - 43962
关于返回值的差异,请参阅手册页以获取...这个玩具代码可以改进,是吗?strtoul()
评论
strtol
函数,而不是编写自己的函数。它将忽略任何前导或被告知基数 16 时。sprintf()
0x
0X
0x
1234