用C++實現ASCII碼字元陣列轉16進位制字元陣列的功能
阿新 • • 發佈:2019-02-01
在網路通訊中經常用到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;
}
有興趣的同學可以寫個單元測試驗證一下轉化結果。或者用效率更高的查表法來實現,節省更多計算步驟和時間。