解決NSData中包含非法UTF-8編碼

轉自:http://www.tanhao.me/code/150608.html/(老譚)
本文僅給自己作爲參考,如詳細內容還請看原作
我們開發中常會遇上將NSData轉換爲NSString,或通過NSJSONSerialization解析JSON的場景,一旦NSData中包含非法的UTF-8編碼,那麼結果將是返回nil,但這樣的結果並不符合我們預期,因爲可能這其中僅僅只是一個編碼錯誤,我們更希望將錯誤編碼丟棄或替換爲錯誤字符.
在Google上找了一圈,有人也實現了這樣的方法,但個人覺得寫得不夠嚴謹,容錯性也不太好,索性自己寫一個吧,嚴格按照RFC3629的標準.

UTF-8是一種變長的編碼,針對不同長度的字節有固定的格式,在RFC3629規範中最多隻能四個字節,且對範圍有區間有要求,更多相關介紹請跳轉維基百科UTF-8詞條(跳轉地址):

1字節 0xxxxxxx
2字節 110xxxxx 10xxxxxx
3字節 1110xxxx 10xxxxxx 10xxxxxx
4字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
按照這樣的規則寫了一個NSData的擴展方法,見代碼:

@implementation NSData (UTF8)

- (NSData *)UTF8Data
{
    //保存結果
    NSMutableData *resData = [[NSMutableData alloc] initWithCapacity:self.length];

    //無效編碼替代符號(常見 � □ ?)
    NSData *replacement = [@"�" dataUsingEncoding:NSUTF8StringEncoding];

    uint64_t index = 0;
    const uint8_t *bytes = self.bytes;

    while (index < self.length)
    {
        uint8_t len = 0;
        uint8_t header = bytes[index];

        //單字節
        if ((header&0x80) == 0)
        {
            len = 1;
        }
        //2字節(並且不能爲C0,C1)
        else if ((header&0xE0) == 0xC0)
        {
            if (header != 0xC0 && header != 0xC1)
            {
                len = 2;
            }
        }
        //3字節
        else if((header&0xF0) == 0xE0)
        {
            len = 3;
        }
        //4字節(並且不能爲F5,F6,F7)
        else if ((header&0xF8) == 0xF0)
        {
            if (header != 0xF5 && header != 0xF6 && header != 0xF7)
            {
                len = 4;
            }
        }

        //無法識別
        if (len == 0)
        {
            [resData appendData:replacement];
            index++;
            continue;
        }

        //檢測有效的數據長度(後面還有多少個10xxxxxx這樣的字節)
        uint8_t validLen = 1;
        while (validLen < len && index+validLen < self.length)
        {
            if ((bytes[index+validLen] & 0xC0) != 0x80)
                break;
            validLen++;
        }

        //有效字節等於編碼要求的字節數表示合法,否則不合法
        if (validLen == len)
        {
            [resData appendBytes:bytes+index length:len];
        }else
        {
            [resData appendData:replacement];
        }

        //移動下標
        index += validLen;
    }

    return resData;
}

@end

在Github上的鏈接地址:https://github.com/tanhaogg/THCategory

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