解決NSData轉NSString返回nil的問題

在使用initWithData等方法將NSData轉換成NSString時,如果NSData的內容含有非encoding編碼的字符,將會返回nil。

----------SDK文檔如下-------------

- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;

Return Value

An NSString object initialized by converting the bytes in data into Unicode characters using encoding. The returned object may be different from the original receiver. Returns nil if the initialization fails for some reason (for example if data does not represent valid data for encoding).

-----------------------------


這個結果在很多時候可能並不是我們所希望的,比如在獲取網頁源碼進行分析等方面,如果頁面採用了utf-8編碼,只是含有個別非utf-8字符,我們更希望轉換NSString成功,拋棄(或替換)那些非法字符。


按照utf8格式標準

Unicode/UCS-4
bit數
UTF-8
byte數
範圍(16進制)
0000 ~
007F
0~7
0XXX XXXX
1
0x - 7x
0080 ~
07FF
8~11
110X XXXX
10XX XXXX
2
Cx 8x - Dx Bx
0800 ~
FFFF
12~16
1110XXXX
10XX XXXX
10XX XXXX
3
Ex 8x 8x - Ex Bx Bx
1 0000 ~
1F FFFF
17~21
1111 0XXX
10XX XXXX
10XX XXXX
10XX XXXX
4
F8 8x 8x 8x 8x - FB Bx Bx Bx Bx 
20 0000 ~
3FF FFFF
22~26
1111 10XX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
5
FC 8x 8x 8x 8x 8x - FD Bx Bx Bx Bx Bx
400 0000 ~
7FFF FFFF
27~31
1111 110X
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
6


如果一個字節小於0x80,那麼他就是一個字符;

如果大於C0小於E0,表示2個字節組成的utf8字符(第一個是110開頭的,第二個是10開頭的);

如果大於E0小於F0,表示3個字節組成的utf8字符(第一個是1110開頭的,第二個是10開頭的,第三個是10開頭的);

以此類推,如果不符合utf-8規則,則表示一個非法字符,只要替換這樣的字符即可。 


實現方法如下(此實現可用但不夠嚴謹,如用於工程中建議進行優化):

//替換非utf8字符
//注意:如果是三字節utf-8,第二字節錯誤,則先替換第一字節內容(認爲此字節誤碼爲三字節utf8的頭),然後判斷剩下的兩個字節是否非法;
- (NSData *)replaceNoUtf8:(NSData *)data
{
    char aa[] = {'A','A','A','A','A','A'};                      //utf8最多6個字符,當前方法未使用
    NSMutableData *md = [NSMutableData dataWithData:data];
    int loc = 0;
    while(loc < [md length])
    {
        char buffer;
        [md getBytes:&buffer range:NSMakeRange(loc, 1)];
        if((buffer & 0x80) == 0)
        {
            loc++;
            continue;
        }
        else if((buffer & 0xE0) == 0xC0)
        {
            loc++;
            [md getBytes:&buffer range:NSMakeRange(loc, 1)];
            if((buffer & 0xC0) == 0x80)
            {
                loc++;
                continue;
            }
            loc--;
            //非法字符,將這個字符(一個byte)替換爲A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
        else if((buffer & 0xF0) == 0xE0)
        {
            loc++;
            [md getBytes:&buffer range:NSMakeRange(loc, 1)];
            if((buffer & 0xC0) == 0x80)
            {
                loc++;
                [md getBytes:&buffer range:NSMakeRange(loc, 1)];
                if((buffer & 0xC0) == 0x80)
                {
                    loc++;
                    continue;
                }
                loc--;
            }
            loc--;
            //非法字符,將這個字符(一個byte)替換爲A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
        else
        {
            //非法字符,將這個字符(一個byte)替換爲A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
    }
    
    return md;
}


轉換後的NSData就可以正確轉換爲NSString了。


*如果是非utf-8編碼,請自行對對應照編碼協議轉換。


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