轉自: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