從NSDictionary打印不出中文開始

一直以來,我都被一個問題小小困擾疑枯,就是當我在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
{
  "我" : "哈哈"
}



除此之外袁梗,之前還考慮了幾種解決辦法:

  1. 利用method swizzling替換NSDictionary中的description方法:
    可以參考這篇博客:解決 NSDictionary 輸出中文字符亂碼(Unicode)問題宜鸯,但是使用這個方法也有諸多問題,比如需要給每個工程加上這個擴展遮怜,替換系統(tǒng)方法存在一定風險淋袖。

  2. 在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對象中的中文呢?
先看看chiselpjson命令的實現(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)補充了一點知識:

  1. \Uxxxx是UTF-16的編碼(第一個Unicode平面)撒会,比如歐元符(€)的編碼為\U20ac 嘹朗。
  2. 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)容不符缘滥,則會看到亂碼。那終端是不是也應該會有自己的編碼方式谒主?還真有朝扼。

OSX上的Terminal的“偏好設(shè)置”

由于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.

而蘋果在一封郵件中舱权,明確的提到了矗晃,NSDictionaryNSArray都會打印出“old-style ASCII property list”。雖然這封郵件的時間有點早宴倍,且description方法很容易隨著iOS版本的升級而改動张症,但是至少,它還是正面解釋了為什么NSDictionary打印不出中文鸵贬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俗他,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子阔逼,更是在濱河造成了極大的恐慌拯辙,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涯保,居然都是意外死亡,警方通過查閱死者的電腦和手機周伦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門夕春,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人专挪,你說我怎么就攤上這事及志。” “怎么了寨腔?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵速侈,是天一觀的道長。 經(jīng)常有香客問我迫卢,道長倚搬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任乾蛤,我火速辦了婚禮每界,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘家卖。我一直安慰自己眨层,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布上荡。 她就那樣靜靜地躺著趴樱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酪捡。 梳的紋絲不亂的頭發(fā)上叁征,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音沛善,去河邊找鬼航揉。 笑死,一個胖子當著我的面吹牛金刁,可吹牛的內(nèi)容都是我干的帅涂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼尤蛮,長吁一口氣:“原來是場噩夢啊……” “哼媳友!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起产捞,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤醇锚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焊唬,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡恋昼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赶促。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片液肌。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸥滨,靈堂內(nèi)的尸體忽然破棺而出嗦哆,到底是詐尸還是另有隱情,我是刑警寧澤婿滓,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布老速,位于F島的核電站,受9級特大地震影響凸主,放射性物質(zhì)發(fā)生泄漏橘券。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一秕铛、第九天 我趴在偏房一處隱蔽的房頂上張望约郁。 院中可真熱鬧,春花似錦但两、人聲如沸鬓梅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绽快。三九已至,卻和暖如春紧阔,著一層夾襖步出監(jiān)牢的瞬間坊罢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工擅耽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留活孩,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓乖仇,卻偏偏與公主長得像憾儒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乃沙,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容