16進制字符串的壓縮存儲
對於秘鑰之類的一些字符串,我們往往都是通過16進制數對應的字符串進行顯示的。然而一個字符串一般情況下要占用一個字節,而一個字節一般能夠表示兩個16進制數,這就造成了空間的浪費。如0xEA,我們就可以用一個字節進行存儲,而用'E','A'存儲則需要花費兩個字節。這里提供了一個方法ZipStr
能夠將16進制字符串壓縮為16進制數。調用方法如下:
int main()
{
int i = 0;
unsigned char a[10];
char b[20] = "AbCdEf1234AbCdEf5678";
memset(a,0,sizeof(a));
ZipStr(a,b,20);
// ZipStr(a,&b[3],11);
for(i = 0;i < 10;i++)
printf("%x",a[i]); //如果想要輸出大寫的16進制數使用%X
printf("\r\n");
}
該調用方法中,第一個參數為16進制數存儲位置,第二個參數指向需要壓縮存儲的字符串,第三個數是是16進制數字符串數。當然我們也可以在字符串b的任意位置開始壓縮,並且壓縮任意個數的字符,就像注釋掉的哪行一樣。執行結果如下:
對應函數源代碼如下:
//將一個字符變成16進制數
unsigned char Str2Hex(char ch)
{
if((ch >= '0')&(ch <= '9'))
return ch - '0';
else if((ch >= 'a')&(ch <= 'f'))
return ch - 'a' + 10;
else if((ch >= 'A')&(ch <= 'F'))
return ch - 'A' + 10;
return 0;
}
//-----------------------------------------------------------------------------
// 函數功能: 將16進制字符串進行壓縮存儲
//-----------------------------------------------------------------------------
// 函數說明:無
//-----------------------------------------------------------------------------
// 輸入參數: dest -> 存儲壓縮數據的位置,src -> 需要被壓縮的字符串位置
// srcNum -> 被壓縮的字符串個數
// 輸出參數: 無
//-----------------------------------------------------------------------------
void ZipStr(unsigned char* dest,char* src,int srcNum)
{
int i = 0;
unsigned char temp = 0;
for(i = 0;i < srcNum;i++)
{
temp = Str2Hex(src[i]);
if(i%2)
dest[i/2] |= temp;
else
dest[i/2] = temp<<4;
}
}
這里為了看的更加清晰,將一個16進制字符串轉數單獨用一個函數Str2Hex
寫出來了。事實上這個函數並沒有在多個地方進行調用,因此完全可以將該函數寫到ZipStr
當中,這樣可以省去很大一部分函數在調用時的開銷(該函數被循環調用很多次)。當然如果我們寫的是C++代碼,直接將這個函數聲明為inline
即可。
16進制數的解壓
和壓縮相反,當我們需要將我們的16進制數打印到窗口,或者通過某個字符串傳遞協議發送時。我們還需要將16進制數轉化為字符串,這里同樣提供了一個UnzipStr
用來進行上面函數的逆運算。調用方法如下:
int main()
{
int i = 0;
unsigned char a[10];
char b[20] = "AbCdEf1234AbCdEf5678";
char c[21];
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
ZipStr(a,b,20);
// ZipStr(a,&b[3],11);
UnzipStr(c,a,20);
// UnzipStr(c,a+1,11);
printf("c:%s\r\n",c);
printf("a:");
for(i = 0;i < 10;i++)
printf("%x",a[i]); //如果想要輸出大寫的16進制數使用%X
printf("\r\n");
}
上面這段代碼是在前一個代碼上增加的,其中c就是a解壓后的字符串。這里為了方便打印字符串c在其后面又多加了一位0表示字符串結束。同樣,就像注釋掉的部分那樣,我們可以從a任意字節開始提取任意多個字符串。代碼執行結果如下:
對應函數源碼如下:
//在一個字節中提取一個16進制數
//idx = 1表示提取高位,idx = 0表示提取低位
char Hex2Str(unsigned char dat,int idx)
{
char temp = 0;
if(idx)
temp = dat>>4;
else
temp = dat&0xf;
if(temp <= 9)
return temp + '0';
else
return temp - 10 + 'A'; //全部轉化為大寫
return 0;
}
//-----------------------------------------------------------------------------
// 函數功能: 將16進制數解壓為字符串
//-----------------------------------------------------------------------------
// 函數說明:無
//-----------------------------------------------------------------------------
// 輸入參數: dest -> 存儲解壓后字符串的位置,src -> 需要被解壓的16進制數位置
// destNum -> 被解壓的字符串個數
// 輸出參數: 無
//-----------------------------------------------------------------------------
void UnzipStr(char* dest,unsigned char* src,int destNum)
{
int i = 0;
for(i = 0;i < destNum;i++)
{
if(i%2)
dest[i] = Hex2Str(src[i/2],0);
else
dest[i] = Hex2Str(src[i/2],1);
}
}
分析過程和壓縮過程幾乎一樣這里不再進行過多介紹了。需要注意的是這里只能將字符串轉成大寫的16進制數,當然轉成小寫或者通過一個參數進行控制也十分簡單。
文中的源代碼及對應的makefile和shell文件:源代碼