在网络通信中经常用到16进制字符数组和ASCII码字符数组互相转换的功能,虽然功能简单,但初学者写出来的代码经常会有各种问题。从ASCII码字符数组转为16进制字符数组,一个判断比较完整的实现代码如下:
bool AsciiToHex( char * Dest, char * Src, int SrcLen )
{
if ( ( Dest == NULL ) || ( Src == NULL ) )
{
return false;
}
for ( int i = 0, j = 0; i < SrcLen; j++ )
{
unsigned char HiHalf = ( unsigned char ) Src[i++] ;
unsigned char LoHalf = ( unsigned char ) Src[i++] ;
if ( HiHalf >= 'a' && HiHalf <= 'z' )
{
HiHalf -= ( 'a' - 10 );
}
else if ( HiHalf >= 'A' && HiHalf <= 'Z' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf >= '0' && HiHalf <= '9' )
{
HiHalf -= '0';
}
else
{
return false;
}
if ( LoHalf >= 'a' && LoHalf <= 'z' )
{
LoHalf -= ( 'a' - 10 );
}
else if ( LoHalf >= 'A' && LoHalf <= 'Z' )
{
LoHalf -= ( 'A' - 10 );
}
else if ( LoHalf >= '0' && LoHalf <= '9' )
{
LoHalf -= '0';
}
else
{
return false;
}
Dest[j] = ( HiHalf << 4 ) | LoHalf;
}
return true;
}
以上代码中,比较了Dest和Src是否为NULL,还判断了字符数组Src里是否有非法字符(即0-9、A-Z、a-z以外的字符),进行计算的时候也使用了更安全的unsigned char类型,看起来很不错。但当SrcLen参数错误,为奇数时Dest会多写一个字节,可能会导致数组越界。 另外以上代码在效率上有严重的问题。一般我们在通信中是把10-15转为大写的A-F,以上代码先和小写字母判断,会导致一些无用功。另外被转换的Src字符为0-15,数字0-9有10个,字母A-F有6个,理论上出现数字的概率大。所以要先判断是否数字,再判断是否大写字母,最后判断是否小写字母,剩下的就是非法字符了。比较代码就应该如下:
if ( HiHalf >= '0' && HiHalf <= '9' )
{
HiHalf -= '0';
}
else if ( HiHalf >= 'A' && HiHalf <= 'F' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf >= 'a' && HiHalf <= 'f' )
{
HiHalf -= ( 'a' - 10 );
}
else
{
return false;
}
在修正后的比较代码中,仍然有效率问题。举例如下,当HiHalf等于'A'时,因为 '0' < '9' < 'A' < 'F' < 'a' < 'f',HiHalf分别和'0'、'9'、'A'、‘F’做了4次比较。如果我们将代码改为:
if ( HiHalf <= '9' && HiHalf >= '0' )
{
HiHalf -= '0';
}
else if ( HiHalf <= 'F' && HiHalf >= 'A' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf <= 'f' && HiHalf >= 'a' )
{
HiHalf -= ( 'a' - 10 );
}
else
{
return false;
}
那么HiHalf和'9'比较后,就不会再和'0'去比较了,比较次数从4次变成了3次。同理对小写字母来说,比较次数减少了2次。
另外为了调用方便,将Src、Dest的类型从char *改为void *。最终版本的代码如下:
bool AsciiToHex( void * DestBuf, void * SrcBuf, int SrcLen )
{
if ( ( DestBuf == NULL ) || ( SrcBuf == NULL ) )
{
return false;
}
unsigned char * Dest = ( unsigned char * ) DestBuf;
unsigned char * Src = ( unsigned char * ) SrcBuf;
int DestLen = SrcLen >> 1;
for ( int SrcPos = -1, DestPos = 0; DestPos < DestLen; )
{
unsigned char HiHalf = Src[++SrcPos];
unsigned char LoHalf = Src[++SrcPos];
if ( HiHalf <= '9' && HiHalf >= '0' )
{
HiHalf -= '0';
}
else if ( HiHalf <= 'F' && HiHalf >= 'A' )
{
HiHalf -= 55;
}
else if ( HiHalf <= 'f' && HiHalf >= 'a' )
{
HiHalf -= 87;
}
else
{
return false;
}
if ( LoHalf <= '9' && LoHalf >= '0' )
{
LoHalf -= '0';
}
else if ( LoHalf <= 'F' && LoHalf >= 'A' )
{
LoHalf -= 55;
}
else if ( LoHalf <= 'f' && LoHalf >= 'a' )
{
LoHalf -= 87;
}
else
{
return false;
}
Dest[DestPos++] = ( HiHalf << 4 ) | LoHalf;
}
return true;
}
有兴趣的同学可以写个单元测试验证一下转化结果。或者用效率更高的查表法来实现,节省更多计算步骤和时间。
bool AsciiToHex( char * Dest, char * Src, int SrcLen )
{
if ( ( Dest == NULL ) || ( Src == NULL ) )
{
return false;
}
for ( int i = 0, j = 0; i < SrcLen; j++ )
{
unsigned char HiHalf = ( unsigned char ) Src[i++] ;
unsigned char LoHalf = ( unsigned char ) Src[i++] ;
if ( HiHalf >= 'a' && HiHalf <= 'z' )
{
HiHalf -= ( 'a' - 10 );
}
else if ( HiHalf >= 'A' && HiHalf <= 'Z' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf >= '0' && HiHalf <= '9' )
{
HiHalf -= '0';
}
else
{
return false;
}
if ( LoHalf >= 'a' && LoHalf <= 'z' )
{
LoHalf -= ( 'a' - 10 );
}
else if ( LoHalf >= 'A' && LoHalf <= 'Z' )
{
LoHalf -= ( 'A' - 10 );
}
else if ( LoHalf >= '0' && LoHalf <= '9' )
{
LoHalf -= '0';
}
else
{
return false;
}
Dest[j] = ( HiHalf << 4 ) | LoHalf;
}
return true;
}
以上代码中,比较了Dest和Src是否为NULL,还判断了字符数组Src里是否有非法字符(即0-9、A-Z、a-z以外的字符),进行计算的时候也使用了更安全的unsigned char类型,看起来很不错。但当SrcLen参数错误,为奇数时Dest会多写一个字节,可能会导致数组越界。 另外以上代码在效率上有严重的问题。一般我们在通信中是把10-15转为大写的A-F,以上代码先和小写字母判断,会导致一些无用功。另外被转换的Src字符为0-15,数字0-9有10个,字母A-F有6个,理论上出现数字的概率大。所以要先判断是否数字,再判断是否大写字母,最后判断是否小写字母,剩下的就是非法字符了。比较代码就应该如下:
if ( HiHalf >= '0' && HiHalf <= '9' )
{
HiHalf -= '0';
}
else if ( HiHalf >= 'A' && HiHalf <= 'F' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf >= 'a' && HiHalf <= 'f' )
{
HiHalf -= ( 'a' - 10 );
}
else
{
return false;
}
在修正后的比较代码中,仍然有效率问题。举例如下,当HiHalf等于'A'时,因为 '0' < '9' < 'A' < 'F' < 'a' < 'f',HiHalf分别和'0'、'9'、'A'、‘F’做了4次比较。如果我们将代码改为:
if ( HiHalf <= '9' && HiHalf >= '0' )
{
HiHalf -= '0';
}
else if ( HiHalf <= 'F' && HiHalf >= 'A' )
{
HiHalf -= ( 'A' - 10 );
}
else if ( HiHalf <= 'f' && HiHalf >= 'a' )
{
HiHalf -= ( 'a' - 10 );
}
else
{
return false;
}
那么HiHalf和'9'比较后,就不会再和'0'去比较了,比较次数从4次变成了3次。同理对小写字母来说,比较次数减少了2次。
另外为了调用方便,将Src、Dest的类型从char *改为void *。最终版本的代码如下:
bool AsciiToHex( void * DestBuf, void * SrcBuf, int SrcLen )
{
if ( ( DestBuf == NULL ) || ( SrcBuf == NULL ) )
{
return false;
}
unsigned char * Dest = ( unsigned char * ) DestBuf;
unsigned char * Src = ( unsigned char * ) SrcBuf;
int DestLen = SrcLen >> 1;
for ( int SrcPos = -1, DestPos = 0; DestPos < DestLen; )
{
unsigned char HiHalf = Src[++SrcPos];
unsigned char LoHalf = Src[++SrcPos];
if ( HiHalf <= '9' && HiHalf >= '0' )
{
HiHalf -= '0';
}
else if ( HiHalf <= 'F' && HiHalf >= 'A' )
{
HiHalf -= 55;
}
else if ( HiHalf <= 'f' && HiHalf >= 'a' )
{
HiHalf -= 87;
}
else
{
return false;
}
if ( LoHalf <= '9' && LoHalf >= '0' )
{
LoHalf -= '0';
}
else if ( LoHalf <= 'F' && LoHalf >= 'A' )
{
LoHalf -= 55;
}
else if ( LoHalf <= 'f' && LoHalf >= 'a' )
{
LoHalf -= 87;
}
else
{
return false;
}
Dest[DestPos++] = ( HiHalf << 4 ) | LoHalf;
}
return true;
}
有兴趣的同学可以写个单元测试验证一下转化结果。或者用效率更高的查表法来实现,节省更多计算步骤和时间。