iOS開發中的HexString

作者:代培
地址:http://blog.csdn.net/dp948080952/article/details/53255906
轉載請註明出處

寫在前面

按照正常的時間來說,今天應該在寫本月的第三篇博客,但這個月各種忙,主要是幾場考試加上學校公司裏的一些事情,有時候本打算寫一篇博客卻被朋友叫去聚餐,又或是受不住一些影視劇的誘惑,到了今天總算有時間把這個月第二篇博客寫出來了。
前段時間在項目中遇到了HexString,既然遇到了,那麼肯定要把它搞明白,所以今天就來說說在iOS開發中的HexString。

正文

HexString是什麼?

無論什麼事情,首先要搞明白它是什麼,那麼HexString到底是什麼呢?首先看一下這個詞的前半部分:hex,hex的意思是十六進制,那這個詞的意思就很容易明白了,HexString的意思就是十六進制的字符串。
雖然這樣說看起來很簡單,但似乎還是不知道HexString到底是什麼東西,那我們就來舉個栗子,大家肯定就懂了。
假設下面是內存中的四個字節,由左往右地址變大

00010011 10101111 10010101 11000111

那麼這段內存用HexString表示就是@”13af95c7”
其實很簡單,就是將內存中的每個字節轉換爲兩個十六進制的數,0001對應1,0011對應3以此類推,所以經過這樣的轉換一個字節的信息實際上要用兩個字節來儲存。

HexString的作用

根據我上面的例子,不難看出HexString的作用就是用來表示一段比特信息,實際上就是一段0101代碼,用HexString可以用來表示一個程序,一段數據,一個字符串,甚至說可以表示一切,因爲在計算機中任何東西都是以二進制代碼存在的。
而且在計算機的內存地址的表示中一般都會用十六進制,因爲兩位表示一個字節清楚明瞭,尤其在彙編編程中,十六進制更是隨處可見,而HexString就是將十六進制的數轉換爲字符串,用以表更長的內存信息。

爲什麼在傳輸時要用HexString

有人會說一段內存實際上就可以看成一段字符串,直接將內存轉換爲字符串不就可以了嘛,這樣不僅佔的空間小於HexString,而且還省去了轉換的操作。
曾經我也是這樣想,也這樣去做了,卻發現這樣是行不通的,因爲當你將一段內存轉換爲字符串時,你可能會遇到下面這些問題:

  • 轉換出來字符串打印出來什麼都沒有
  • 無法轉換成字符串(字符串對象爲null)
  • 轉換的字符串轉換回去會不一樣

當然第一個問題並不妨礙我用字符串來傳遞數據,雖然打印出來看不見,但是轉換成內存後並沒有變化,而第二種情況就不行了,根本就無法轉換爲字符串,第三種情況就更奇怪了,內存到字符串再到內存,結果不一樣了。
經過我的一些實驗發現了一個規律,那就是每個字節的最高位如果是1轉換成字符串就會出問題,對於不同的解碼方式出現的問題不同,如果用ASCII解碼,內存到字符串看不出什麼問題,但是轉換回去以後就會變成0,如果用utf-8解碼,那樣生成的字符串就是null。
所以直接用字符串來傳遞數據就會遇到問題,那能不能直接傳遞比特信息,實際上傳遞的數據就是比特信息(高低電平),但是對於一段數據來說很難是單獨存在的,它需要一個上下文,就拿http的post請求來說,不論是使用json或是html表單,他裏面的value值都是無法單獨存在的,因爲你無法知道這個value是代表什麼,所以要將其組織成字符串,這樣人才能讀懂,才能對這段數據進行處理。
而HexString的字面值不是我們需要的信息,他轉換成的比特信息纔是我們需要的,但HexString可以和其他數據更好的拼接。
但是爲什麼要用十六進制而不用其他進制呢,我想十六進制的特殊之處應該在於十六進制的每一位正好對應二進制中的四位,沒兩個十六進制字符對應一個字節,8進制對應三位,連一個字節用8進制都不好表示,10進制跟位數直接根本不好對應,而如果直接用二進制的化,所佔空間是HexString的4倍,所以綜上可以看出十六進制在這個地方是十分友好的。

大端系統&小端系統

大小端是針對CPU而言的,對於大端來說,高位儲存在高地址,低位儲存在小地址

下面這段地址從左往右地址增大

0x100 0x101
00000010 00000001
2 1


對大端系統而言,左邊是低位,右邊是高位,那麼結果就是0x12
對小端系統而言,左邊是高位,右邊是低位,那麼結果就是0x21
所以實際上小端是符合人們的書寫習慣的。

而有人說HexString是爲了讓大小端系統兼容而使用的,而我覺得並不是這樣,維基百科中有這樣的解釋

一個多位的整數將按照其存儲地址的最低或最高字節排列。如果最低有效位在最高有效位的前面,則稱小端序;反之則稱大端序。在網絡應用中,字節序是一個必須被考慮的因素,因爲不同機器類型可能採用不同標準的字節序,所以均按照網絡標準轉化。

這個兼容不是我們自己做的,而是大家都轉換爲一個標準的字節序,這樣就不存在兼容問題了。
而即使轉換換爲HexString,實際上我們只是將一種比特信息映射成另一種比特信息,地址的單位仍然是字節,仍然還是一段字節序,這段字節序從大端到小端還是要反序儲存才正確,這個反序的過程是通過網絡標準轉化而達到的。

如何生成HexString

又到了貼代碼的時間,Talk is cheap,show you the code:

+ (NSString *)hexStringWithData:(NSData *)data {
    const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
    if (!dataBuffer) {
        return [NSString string];
    }

    NSUInteger          dataLength  = [data length];
    NSMutableString     *hexString  = [NSMutableString stringWithCapacity:(dataLength * 2)];

    for (int i = 0; i < dataLength; ++i) {
        [hexString appendFormat:@"%02x", (unsigned char)dataBuffer[i]];
    }
    return [NSString stringWithString:hexString];
}

這是data轉換爲HexString的方法,至於如何將指針轉換爲NSData,自行百度,這裏的核心代碼只有一句[hexString appendFormat:@"%02x", (unsigned char)dataBuffer[i]];
這句話中比較重要的是兩個點:

  • %02x,%x是按十六進制輸出,%02x是將一個字節轉換爲兩個十六進制輸出
  • 強制類型轉換(unsigned char),如果不進行這個轉換你最後的HexString裏可能就會多出6個f,就像這樣:ffffff,這裏牽涉到符號的問題而且有趣的是轉換成unsigned int都不行,只有char可以
+ (NSData *)dataWithHexString:(NSString *)hexString {
    const char *chars = [hexString UTF8String];
    int i = 0;
    NSUInteger len = hexString.length;

    NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
    char byteChars[3] = {'\0','\0','\0'};
    unsigned long wholeByte;

    while (i < len) {
        byteChars[0] = chars[i++];
        byteChars[1] = chars[i++];
        wholeByte = strtoul(byteChars, NULL, 16);
        [data appendBytes:&wholeByte length:1];
    }

    return data;
}

這是StackOverflow上的代碼,據說效率很高,不過確實,他每個循環能夠完成一個字節的轉換,然後用了C語言的strtoul函數,直接將字符串轉換爲無符號長整型。

總結

HexString就是將二進制換算爲十六進制再轉換爲字符串表示,在計算機內存中這段數據的表示已經和轉換前不同了,雖然有轉換的開銷,但是卻很有必要,因爲這樣會更通用。
這些都是自己的一些理解,如果有錯誤的地方歡迎大家指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章