【iOS解決方案】網路請求返回GB2312格式的xml資料轉成UTF-8後為空(適用於論壇bbs)
阿新 • • 發佈:2019-01-29
一些高校的bbs由於歷史久遠,沒有適應新的資料結構,請求返回的資料還是xml格式的,而現在常用的返回是json,這是個很頭疼的地方,碰到的問題網上很難搜到資料,走了很多彎路。
現在具體講一下在解析返回xml過程中碰到的問題,希望能有些借鑑:
1.xml編碼問題:
對於編碼,我也僅僅只是瞭解。英文網頁返回的xml用UTF-8編碼滿足要求,而如果包含中文的話,用的就是GB2312編碼。問題是,iOS自帶的sdk中NSXMLParser(對於iOS自帶的解析方法,可以百度下,這裡不再詳述)解析UTF-8沒有問題,但是對於GB2312編碼,會直接觸發parser:parseErrorOccurred:導致解析失敗。所以轉碼是第一個問題,網上有很多GB2312轉UTF-8的方法。
2.NSData在轉碼過程中出資料錯誤的情況:
針對BBS論壇這類特殊情況,當標題超過某一長度時會自動進行截斷,所以這個比較坑,NSData轉碼不會報錯,但是轉換完就為nil,完全無從下手,不知道什麼問題。開始有的xml請求到了轉碼之後可以自己解析,但是有的就返回為nil,一直以為是資料請求失敗,但是返回的NSData是有資料的。直到無意中搜到cocoachina上的一個帖子,關於NSData轉碼問題(針對高校BBS回覆)。這個帖子的回覆給了我方向,修復NSData中正好被截斷的位置,具體函式參考網上的一個處理方法(PS:他的封裝nsdata類我不太會用,所以自己修改了下,放到自己的類裡面去了= =)
+ (NSString *)GB18030String:(NSData *)sender { NSStringEncoding enc =CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSString *str = [[NSString alloc]initWithData:sender encoding:enc]; if (!str) { str = [[NSString alloc]initWithData:[self dataByHealingGB18030Stream:sender]encoding:enc]; } return str; } + (NSData *)dataByHealingGB18030Stream:(NSData *)sender { NSUInteger length = [sender length]; if (length == 0) { return sender; } static NSString * replacementCharacter = @"?"; NSStringEncoding enc =CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSData *replacementCharacterData = [replacementCharacterdataUsingEncoding:enc]; NSMutableData *resultData = [NSMutableData dataWithCapacity:sender.length]; const Byte *bytes = [sender bytes]; static const NSUInteger bufferMaxSize = 1024; Byte buffer[bufferMaxSize]; NSUInteger bufferIndex = 0; NSUInteger byteIndex = 0; BOOL invalidByte = NO; #define FlushBuffer() if (bufferIndex > 0) { \ [resultData appendBytes:buffer length:bufferIndex]; \ bufferIndex = 0; \ } #define CheckBuffer() if ((bufferIndex+5) >= bufferMaxSize) { \ [resultData appendBytes:buffer length:bufferIndex]; \ bufferIndex = 0; \ } while (byteIndex < length) { Byte byte = bytes[byteIndex]; //檢查第一位 if (byte >= 0 && byte <= (Byte)0x7f) { //單位元組文字 CheckBuffer(); buffer[bufferIndex++] = byte; } else if (byte >= (Byte)0x81 && byte <= (Byte)0xfe){ //可能是雙位元組,可能是四位元組 if (byteIndex + 1 >= length) { //這是最後一個位元組了,但是這個位元組表明後面應該還有1或3個位元組,那麼這個位元組一定是錯誤位元組 FlushBuffer(); return resultData; } Byte byte2 = bytes[++byteIndex]; if (byte2 >= (Byte)0x40 && byte <= (Byte)0xfe && byte != (Byte)0x7f) { //是雙位元組,並且可能合法 Byte tuple[] = {byte, byte2}; CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, tuple, 2,kCFStringEncodingGB_18030_2000, false); if (cfstr) { CFRelease(cfstr); CheckBuffer(); buffer[bufferIndex++] = byte; buffer[bufferIndex++] = byte2; } else { //這個雙位元組字元不合法,但byte2可能是下一字元的第一位元組 byteIndex -= 1; invalidByte = YES; } } else if (byte2 >= (Byte)0x30 && byte2 <= (Byte)0x39) { //可能是四位元組 if (byteIndex + 2 >= length) { FlushBuffer(); return resultData; } Byte byte3 = bytes[++byteIndex]; if (byte3 >= (Byte)0x81 && byte3 <= (Byte)0xfe) { // 第三位合法,判斷第四位 Byte byte4 = bytes[++byteIndex]; if (byte4 >= (Byte)0x30 && byte4 <= (Byte)0x39) { //第四位可能合法 Byte tuple[] = {byte, byte2, byte3, byte4}; CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, tuple,4, kCFStringEncodingGB_18030_2000, false); if (cfstr) { CFRelease(cfstr); CheckBuffer(); buffer[bufferIndex++] = byte; buffer[bufferIndex++] = byte2; buffer[bufferIndex++] = byte3; buffer[bufferIndex++] = byte4; } else { //這個四位元組字元不合法,但是byte2可能是下一個合法字元的第一位元組,回退3位 //並且將byte1,byte2用?替代 byteIndex -= 3; invalidByte = YES; } } else { //第四位元組不合法 byteIndex -= 3; invalidByte = YES; } } else { // 第三位元組不合法 byteIndex -= 2; invalidByte = YES; } } else { // 第二位元組不是合法的第二位,但可能是下一個合法的第一位,所以回退一個byte invalidByte = YES; byteIndex -= 1; } if (invalidByte) { invalidByte = NO; FlushBuffer(); [resultData appendData:replacementCharacterData]; } } byteIndex++; } FlushBuffer(); return resultData; }
3.關於xml中的特殊字元處理:
如果以為上面兩步處理完了解析xml就很順利了,那就錯了。在解析的過程中發現xml解析到中途就會失敗,返回的意思大概就是碰到PCDATA invalidchar 27什麼的,domain code = 9.這個只能怪我自己基礎不好,當時沒多想,網上也搜不到錯誤碼。後來無意中發現返回的xml nsstring裡面包含\x1b。這個我恰好搜到是ASCII碼裡面的第27個字元,和前面報錯對應起來了。就是![微笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/smile.gif)