一直以來,我都被一個問題小小困擾疑枯,就是當我在lldb中想要查看一個NSDictionary
對象時袍榆,其中的中文會顯示成\Uxxxx
。
比如我創(chuàng)建了一個NSDictionary
對象:
NSDictionary *dic = @{@"我" : @"哈哈"};
當我在lldb中想要查看它時糊昙,我使用了po
命令,但是打印出來卻是這樣:
(lldb) po dic
{
"\U6211" = "\U54c8\U54c8";
}
雖然單獨打印鍵和值都能顯示出正確的中文谢谦,也不影響程序的最終執(zhí)行結(jié)果释牺,但是在調(diào)試的時候萝衩,沒法方便直觀的看到dic
里的數(shù)據(jù),還是有點苦惱的船侧。
之前也沒怎么在意欠气,不過秉承著(三分鐘熱度的)新年新氣象的決心,打算解決一下這個問題镜撩。
解決方案
先說最后找到的一個解決方案:利用chisel中的pjson
命令预柒,就可以查看到NSDictionary
對象中的中文了(=?ω?)?。
(lldb) pjson dic
{
"我" : "哈哈"
}
除此之外袁梗,之前還考慮了幾種解決辦法:
利用method swizzling替換
NSDictionary
中的description
方法:
可以參考這篇博客:解決 NSDictionary 輸出中文字符亂碼(Unicode)問題宜鸯,但是使用這個方法也有諸多問題,比如需要給每個工程加上這個擴展遮怜,替換系統(tǒng)方法存在一定風險淋袖。在lldb上做手腳:
我只是希望能在debug的時候讓NSDictionary
打印中文,并非想改變NSDictionary
的實現(xiàn)锯梁,所以想到即碗,在lldb上做手腳應該是一個比較合適的方法。
前兩天剛剛裝了chisel陌凳,感覺在lldb上做手腳的方案應該可行剥懒,所以想先研究一下chisel是怎么工作的,然后發(fā)現(xiàn)用戶其實可以在chisel中自定義命令合敦。
正在我研究chisel源碼的時候初橘,突然發(fā)現(xiàn)其中居然有個pjson
命令(☆_☆),一試充岛,原來正符合我的需要保檐。
雖然這個方法不能在NSLog
的時候也正常顯示NSDictionary
對象中的中文,但是平時debug我基本都使用lldb上的命令崔梗,所以這個局限對我來說也沒有什么影響夜只。
原理
為什么用pjson
就可以正確打印出NSDictionary
對象中的中文呢?
先看看chisel對pjson
命令的實現(xiàn)蒜魄,在/commands/FBPrintCommands.py
中:
def run(self, arguments, options):
objectToPrint = arguments[0]
pretty = 1 if options.plain is None else 0
jsonData = fb.evaluateObjectExpression('[NSJSONSerialization dataWithJSONObject:{} options:{} error:nil]'.format(objectToPrint, pretty))
jsonString = fb.evaluateExpressionValue('(NSString*)[[NSString alloc] initWithData:{} encoding:4]'.format(jsonData)).GetObjectDescription()
print jsonString
雖然我對Python不太熟盐肃,但是大概能明白,在lldb中使用pjson
权悟,相當于先將這個NSDictionary
對象序列化成NSData
對象,然后在轉(zhuǎn)換成NSString
對象輸出推盛。
試了試用這種方法轉(zhuǎn)換出的字符串峦阁,的確可以正確顯示中文:
NSDictionary *dic = @{@"我" : @"哈哈"};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", jsonString);
2015-12-27 15:09:28.012 XSQPJsonNDemo[1796:1977106] {
"我" : "哈哈"
}
編碼
雖然解決了這個問題,但是仍然對編碼感覺很困惑耘成。
解決 NSDictionary 輸出中文字符亂碼(Unicode)問題 中用了將NSString
轉(zhuǎn)換成char *
再轉(zhuǎn)換回NSString
的方法榔昔,為什么經(jīng)過這兩次轉(zhuǎn)換就能讓中文正確顯示了呢驹闰?
@implementation NSDictionary (Unicode)
- (NSString*)my_description {
NSString *desc = [self my_description];
desc = [NSString stringWithCString:[desc cStringUsingEncoding:NSUTF8StringEncoding] encoding:NSNonLossyASCIIStringEncoding];
return desc;
}
@end
什么都不懂@_@,上網(wǎng)補充了一點知識:
-
\Uxxxx
是UTF-16的編碼(第一個Unicode平面)撒会,比如歐元符(€)的編碼為\U20ac
嘹朗。 - NSString自身使用的是UTF-16:
An NSString object encodes a Unicode-compliant text string, represented as a sequence of UTF–16 code units. All lengths, character indexes, and ranges are expressed in terms of 16-bit platform-endian values, with index values starting at 0.
按照上面轉(zhuǎn)換兩次的思路,我寫了這樣幾行代碼:
NSString *string = @"\U20ac";
char *cstring = [string cStringUsingEncoding:NSUTF8StringEncoding];
NSString *trans = [[NSString alloc] initWithCString:cstring encoding:NSNonLossyASCIIStringEncoding];
第二行把\U20ac
轉(zhuǎn)換成了一個char *
诵肛,這個char *
字符串使用的編碼方式是UTF-8屹培,而UTF-8中,英文字母和數(shù)字的編碼和ASCII一致怔檩,故得到的char *
是這樣的:
|char[0]|char[1]|char[2]|char[3]|char[4]|char[5]|char[6]|
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|''|'U'|'2'|'0'|'a'|'c'|\0|
|0x5c|0x55|0x32|0x30|0x61|0x63|0x00|
第三行中褪秀,假裝cstring
就是一些bit位,將其轉(zhuǎn)化為一個NSString
對象薛训,而不進行任何轉(zhuǎn)碼媒吗。因為NSString
本身是使用的是UTF-16,故在它看來乙埃,這些bit位組合在一起闸英,得到了@"€"
。
然后我又想,為什么第二行要選擇UTF-8的編碼方式呢?直接轉(zhuǎn)成ASCII是不是也可以呢笔呀?
NSString *string = @"\\U20ac";
char *cstring = [string cStringUsingEncoding:NSASCIIStringEncoding];
NSString *trans = [[NSString alloc] initWithCString:cstring encoding:NSNonLossyASCIIStringEncoding];
試了一下贮尉,這樣也能得到正確的結(jié)果。但是當一開始的string
中包含ASCII以外的字符時舌仍,cstring
就會為NULL
,執(zhí)行第三行時崩潰。
拋開NSString
如果我只是單純在寫C代碼加派,為什么運行下面這兩行代碼時,終端可以打印出中文跳芳?
char *chinese = "中文";
printf("%s", chinese);
這里面芍锦,chinese
只是一個字符數(shù)組,不包含任何編碼信息飞盆,為什么最終打印的結(jié)果不是亂碼呢娄琉?
運行到這里的時候,我查看了chinese
變量吓歇,發(fā)現(xiàn)其中存的已經(jīng)是“中文”二字的UTF-8編碼了孽水。是誰定義由“UTF-8”作為編碼方式呢?猜測應該是Xcode editor城看?
想到打印到終端和打印到文件的原理應該類似女气,如果輸出到了文件,那么當我去查看這個文件的時候测柠,這個文件本身有一個編碼方式炼鞠,如果編碼方式和文件中的內(nèi)容不符缘滥,則會看到亂碼。那終端是不是也應該會有自己的編碼方式谒主?還真有朝扼。
由于editor和終端都使用UTF-8的編碼方式,所以在代碼中的“中文”二字霎肯,打印到終端后能正確顯示擎颖。
做了個小實驗:
NSString *string = @"€";
char *cstring = [string cStringUsingEncoding:NSMacOSRomanStringEncoding];
printf("%s", cstring);
這里把歐元符轉(zhuǎn)換成了Mac OS Roman的編碼方式,存放入cstring
這個char *
字符串中姿现,然后打印肠仪。如果終端為UTF-8編碼,則打印出亂碼备典,而換成Mac OS Roman編碼后异旧,則能正確打印歐元符。
參考
NSString
chisel
解決 NSDictionary 輸出中文字符亂碼(Unicode)問題
UTF-16
2016.3.20更新
想來這篇博客講了這么多如何解決打印不出中文的問題提佣,卻依然沒有提到吮蛹,為什么NSDictionary
在輸出到控制臺的時候打印不出中文。
雖然我們不知道NSDictionary
究竟是怎么實現(xiàn)description
方法的拌屏,但是官方文檔中好像給出了一點蛛絲馬跡:
description
A string that represents the contents of the dictionary, formatted as a property list (read-only)
這里說到了property list潮针。根據(jù)property list的文檔,它可以被寫作三種形式:XML倚喂、二進制和ASCII每篷。瀏覽了一下它們的文檔后,感覺ASCII格式與我們看到的端圈、打印出來的NSDictionary
迷之相似焦读。且在講到用ASCII來表示NSString時,文檔中提到:
Though the property list format uses ASCII for strings, note that Cocoa uses Unicode. Since string encodings vary from region to region, this representation makes the format fragile. You may see strings containing unreadable sequences of ASCII characters; these are used to represent Unicode characters.
而蘋果在一封郵件中舱权,明確的提到了矗晃,NSDictionary
和NSArray
都會打印出“old-style ASCII property list”。雖然這封郵件的時間有點早宴倍,且description
方法很容易隨著iOS版本的升級而改動张症,但是至少,它還是正面解釋了為什么NSDictionary
打印不出中文鸵贬。