lilho_e 发表于 2014-2-26 20:34:35

求助:如何将字节数组转换为十六进制字符串再转为十进制整数

现在有一个字节数组,例如 char AA={a,f,1,e,6,c} 代表的就是十六进制的 AF 1E 6C

如何把这个数组转换为十六进制的字符串,即 0xAF1E6C?
再怎么把它转为十进制的整数,即 11476588 ?

谢谢!

jytong 发表于 2014-2-27 06:43:15

哈哈,你例子是一个伪命题,你那个定义AA方法是不存在的。
只有可能是下面两种情况:
第一种:char AA={0x0a,0x0f,0x01,0x0e,0x06,0x0c};第二种:char AA={'a','f','1','e','6','c'};第一种:
移位就行了
unsigned long CharArrayToNumber( unsigned char *p_pBuff, int p_iSize )
{
        if( NULL != p_pBuff && p_iSize > 0 && p_iSize <= 8 )
        {
                unsigned long ulRet = 0;
                p_iSize -= 1;
                for(int i=0;i<=p_iSize;++i)
                {
                        ulRet |= ((unsigned long)(p_pBuff))<<((p_iSize-i)<<2);
                }
                return ulRet;
        }
        else
        {
                // arduino 有没有 throw 我不知道,异常肯定就挂了,怎么调试我也不知道
                //throw "CharArrayToNumber: arg error";
                return 0;
        }
}
你的例子就写成:
char AA={0x0a,0x0f,0x01,0x0e,0x06,0x0c};
unsigned long ulVal = CharArrayToNumber(AA,6);
第二种:
就比较麻烦了,需要一个字符到数字的转换,估计你想要这个的可能性大
inline unsigned long CharToNumber( unsigned char p_ucVal )
{
        switch(p_ucVal)
        {
        case '0':
                return 0;

        case '1':
                return 1;

        case '2':
                return 2;

        case '3':
                return 3;

        case '4':
                return 4;

        case '5':
                return 5;

        case '6':
                return 6;

        case '7':
                return 7;

        case '8':
                return 8;

        case '9':
                return 9;

        case 'A':
                return 10;

        case 'B':
                return 11;

        case 'C':
                return 12;

        case 'D':
                return 13;

        case 'E':
                return 14;

        case 'F':
                return 15;

        case 'a':
                return 10;

        case 'b':
                return 11;

        case 'c':
                return 12;

        case 'd':
                return 13;

        case 'e':
                return 14;

        case 'f':
                return 15;

        default:
                // arduino 有没有 throw 我不知道,异常肯定就挂了,怎么调试我也不知道
                //throw "CharToNumber: arg error";
                return 0;
        }
}
这么写为了速度快,当然为了好看可以把 A 和 a 写一起,但写一起编译器恐怕不能优化成跳转,AVR的汇编我不了解,哪位了解可以看一下反汇编确定一下。
如果不确定,又追求效率,可以自己优化,给个思路:
inline unsigned long CharToNumber( unsigned char p_ucVal )
{        // 如果要用,具体 0 的数目对不对最好自己数清楚一下。。。
        static const unsigned char __TransferArray[] = {0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,12,13,14,15};
        if( p_ucVal >= '0' && p_ucVal <= 'f' )
        {        // 意思就是这一步在自己的代码里就保证了肯定是指针跳转
                return __TransferArray;
        }
        else
        {
                //throw "CharToNumber: arg error";
                return 0;
        }
}

unsigned long CharArrayToNumber( unsigned char *p_pBuff, int p_iSize )
{
        if( NULL != p_pBuff && p_iSize > 0 && p_iSize <= 8 )
        {
                unsigned long ulRet = 0;
                p_iSize -= 1;
                for(int i=0;i<=p_iSize;++i)
                {
                        ulRet |= CharToNumber(p_pBuff)<<((p_iSize-i)<<2);
                }
                return ulRet;
        }
        else
        {
                //throw "CharArrayToNumber: arg error";
                return 0;
        }
}
你的例子就写成:
char AA={'a','f','1','e','6','c'};
unsigned long ulVal = CharArrayToNumber(AA,6);
=========================================================
ulVal 既是 0xAF1E6C 也是 11476588
printf("ulVal=0x%X\n", ulVal);
printf("ulVal=0x%ld\n", ulVal);
// 输出:
ulVal=0xAF1E6C
ulVal=11476588
不过你这个需求真的很奇怪,一个 char 的范围本身就是 0x00-0xFF 了
你那个AA为什么不是 char AA={0x00,0xaf,0x1e,0x6c} ?

如果是大端系统就可以直接:
unsigned long ulVal = *((unsigned long *)AA);
小端系统0、3字节1、2字节交换后,就和上面一样直接强转。

大致就这么几种,根据你的情况用吧。总的来说 arduino 对性能的要求比较苛刻,
但效率主要取决于设计思路,具体代码的优化只能是毛毛雨。

arduino 是大端还是小端系统我还真没注意,测一下很简单:
void TestEndian()
{
        static union {
                char _c;
                unsigned long _l;
        } __endian_test = { 'l', '0' ,'0', 'b'};

        if( 'b' == ((char)__endian_test._l) )
                printf("是大端系统\n");
        else
                printf("是小端系统\n");
}

茕兔 发表于 2014-2-27 09:31:08

膜拜一楼大神

转角 发表于 2014-2-27 10:30:33

补充一楼大神的,先上一段代码char s[]="0xA";
int i;
i = strtol(s, NULL, 16);
printf("%d", i);使用strtol可以直接转化成十进制,百科中查下这个函数。你可以直接把AA作为参数放进来,楼主的AA定义是错的,得用1楼的第二种方式定义。
另外你要把AA变成0x开头的可以这样char tmpdata;
sprintf(tmpdata,"0x%s",AA);
//tmpdata中就是0xAF1E6C字符串了
// 继续用strtol转换行了
int i= strtol(tmpdata,NULL,16);sprintf    scanf   memset   memcpy 这四个函数还是比较有用的,前两个是对字符串操作,后两个是直接对内存数据操作,可以百科看一下

lilho_e 发表于 2014-2-27 11:00:51

本帖最后由 lilho_e 于 2014-2-27 11:20 编辑

非常感谢 jytong ,转角 两位兄弟的回复。
我先消化一下,回头再来请教!
谢谢你们二位!

我的字符数组确实是类似
char AA={'a','f','1','e','6','c'};这样的,里面只包含0-9 a-f,字母都是小写的。

实际上我得到的最原始的数据是 string 的字符串,就是类似 string data= "af1e6c........" 这样的,有100多个字符在里面,我自己再转换为char数组的。

这些数据是从传感器上传回的一系列的参数,一些参数占用1位(类似 c2 这样),一些参数占用2位 (类似 a3 c4),还有参数占用4位(类似 e3 c5 a2 22)

我想实现的功能就是用最快的速度,从一个 string 的字符串中,取出其中的某几位的数据 例如 "e3c5a222"来还原出十进制的数值(3821380130)。

顺带再请教一下,比如我要把这个数组清零,准备接收下组数据,语句是什么样的?

还有一个问题是:我现在是在开头就定义好数组的个数 ,比如 char data,如果定义成空的 char data[] ,程序运行第一次的时候,开始的几个参数总是存不进去。不知道是什么原因?

转角 发表于 2014-2-27 11:51:14

lilho_e 发表于 2014-2-27 11:00 static/image/common/back.gif
非常感谢 jytong ,转角 两位兄弟的回复。
我先消化一下,回头再来请教!
谢谢你们二位!


数组清零你可以用memset函数memset(aa,0,aa占用的内存大小);
//这句是把AA数组所用内存全部设置为0C语言中数组定义不能没有长度,必须指定长度。如果你想用个变长的字符串的话可以使用char* 这是指针,不建议新手使用,不及时释放会造成内存泄漏

转角 发表于 2014-2-27 12:02:05

另外你要截取字符串中间的某几位数据的话可以使用strncpy函数char data[]= "af1e6c........";//这里直接赋值时候是可以不写数组长度的,编译器会自动指定data长度的
//要从第12位开始截取4个字符,出租是从0开始的还记得吧
char dest;//要定义5个字符的长度才能保存4个字符 切记
strncpy(dest,data+12,4);现在dest中就是你想要的数据了。别忘记字符串是用'\0'标识结束的,所以要定义5个内存中保存的数据举例: a3c4\0这就是内存中保存的数据

lilho_e 发表于 2014-2-27 12:16:53

转角 发表于 2014-2-27 12:02 static/image/common/back.gif
另外你要截取字符串中间的某几位数据的话可以使用strncpy函数现在dest中就是你想要的数据了。别忘记字符串是 ...

兄弟,非常感谢你耐心的回复,真是太谢谢了!
我来试一下!

lilho_e 发表于 2014-2-27 16:03:48

再次感谢楼上两位兄弟的回复。
现在找到一个简单的函数解决了:
unsigned long ulVal =strtol(data, NULL, 16);

data 是一个 char的数组,里面的内容类似 {"af1e6c........"} 只包含 0-9 a-f

这个函数的作用:直接将这个数组里的十六进制字符串转换为对应的十进制数值。

转角 发表于 2014-2-27 16:51:54

lilho_e 发表于 2014-2-27 16:03 static/image/common/back.gif
再次感谢楼上两位兄弟的回复。
现在找到一个简单的函数解决了:
unsigned long ulVal =strtol(data, NULL ...

看来data数组中不需要加上0x开头是吧?

lilho_e 发表于 2014-2-27 18:00:31

转角 发表于 2014-2-27 16:51 static/image/common/back.gif
看来data数组中不需要加上0x开头是吧?

是的,直接字符串就可以。
还有个问题想请教你一下:

strncpy(dest,data+12,4);

我现在有个数组 类似int Index={14,2,25,3,35,2,60,4}这样

其中 index 代表要取值的位置,index 代表要取值的个数

用index来代替原来的 12 4 ,应该怎么写? 4好像可以直接用index 来代替,12直接代替了要出错。

谢谢!

转角 发表于 2014-2-28 09:49:39

strncpy(dest,data+12,4);
这个data+12是表示从数组起始地址开始偏移12个字节。
data是数组名,+12是便宜12个字节。如果只写data的话就是访问的这个数组的起始地址(就是数组的第一个元素的地址)。
char data[]="abcdefghigkl";   // data长度是13
data其实就是"abcdefghigkl"的首地址,只写data得到的是'a'所在的内存地址值是0x3432dbac    data 这就是取值,会得到‘a' .
data+3相当于0x3432dbac+3   这是'd'的内存地址了。同理data+12你就从你的数组头部往后数12个单位就是了。
回到strncpy(dest,data+3,4); 中,就是把‘d’所在的地址作为第二个参数传进去,截取4个单位的长度
执行完之后 dest的值就是“defg"

你那边12代替进去出错,你检查一下data的长度有index+index 那么长吗?还有dest可用空间大小有没有小于index +1

下面付上strncpy的源码char *strncpy(char *dest, const char *src, int n)
{
    char c;
    char *s = dest;
    if (n >= 4)
    {
      size_t n4 = n >> 2;
      for (;;)
      {
            c = *src++;
            *dest++ = c;
            if (c == '\0')
                break;
            c = *src++;
            *dest++ = c;
            if (c == '\0')
                break;
            c = *src++;
            *dest++ = c;
            if (c == '\0')
                break;
            c = *src++;
            *dest++ = c;
            if (c == '\0')
                break;
            if (--n4 == 0)
                goto last_chars;
      }
      n -= dest - s;
      goto zero_fill;
    }
last_chars:
    n &= 3;
    if (n == 0)
      return s;
    for (;;)
    {
      c = *src++;
      --n;
      *dest++ = c;
      if (c == '\0')
            break;
      if (n == 0)
            return s;
    }
zero_fill:
    while (n-- > 0)
      dest = '\0';
    return s;
}

lilho_e 发表于 2014-2-28 10:15:40

早上好,转角!感谢你如此详细的回复!希望以后能多多请教你!我的编程只是小白水平
你的回复让我对基本的概念又清晰了一些,我今天来学习一下指针的基本概念并试着运用一下。
很奇怪,我昨天用index数组来代替不行,今天早上可以了!可能是昨天哪里有错误没发现。

转角 发表于 2014-2-28 10:23:43

lilho_e 发表于 2014-2-28 10:15 static/image/common/back.gif
早上好,转角!感谢你如此详细的回复!希望以后能多多请教你!我的编程只是小白水平
你的回复让我对基本的 ...

我是程序员,刚开始接触单片机,好多硬件的东西不太懂需要摸索,互相学习吧

转角 发表于 2014-2-28 10:27:11

学习C语言基础,建议你使用vc2005 或者vc6也可以,这编译器可以调试程序,让你能看到各个变量在运行时候都是什么值,也能查看内存、堆栈非常好用。目前最高版可能是vs2014了你没必要用那么高的版本,占用资源太大还慢
页: [1] 2
查看完整版本: 求助:如何将字节数组转换为十六进制字符串再转为十进制整数