故事起源于這周踩的一個小坑,tomcat本地調啟動web時報錯。錯誤提示說有個xml文件的編碼有問題,我點進去看了看沒看出啥異常,開頭跟其他xml一樣指定了utf-8的格式,除了是小寫的,我傻乎乎地去改成了大寫。。。然后。。??隙ㄊ菦]解決啊?。?!咳咳,果斷請教同事去了,瞅一眼,扔過來一串參數-Dfile.encoding=UTF-8,讓我在webserver的VM OPTION里面加上,成了。
恩,又是那個古老的梗,(咦,好了!但是這是為什么呢。。。)雖然從頭到尾我都不懂,但是還是篩選掉一些例如-D是什么鬼之類的問題吧。O(∩_∩)O。。。問題的挖掘經歷了幾個階段:
1.file.encoding的系統默認值是什么(其實我也不知道有沒有系統默認值這么個東西。??傊褪悄J值了啊。。。哈哈。。。)
首先自然想知道默認的encoding值是什么了。我新建了一個空的工程,隨便寫了個類,在main里面打印System.get 2.file.encoding在哪些環節會用到 找到一篇很好地文章http://cmsblogs.com/?p=1475。文章很系統地分析了java輸出格式的流程。在不同情況下的流程也是不同的。文章舉例了三種情況:1)直接輸出到console 2)部署到webserver輸出到web頁面 3)輸出到數據庫。這篇文章解決了我的第一個問題,也就是為什么輸出到web沒有用到那個UTF-8。因為System.getproperty()中得到的file.encoding代表的是輸出到console時編碼格式,自然跟web頁面沒關系。 此外我還知道了,java虛擬機在處理數據時,數據在內存中存儲的格式均為Unicode,包括.class文件以及外部輸入的數據。這樣我可以理解成,數據在內存中一律是以Unicode格式流轉的,需要輸出到哪個地方就再獲取對應的file.encoding,將Unicode格式的數據再轉換成file.encoding的格式輸出。仔細一想,其實這種方式也非常符合java的平臺無關特性,畢竟有Unicode這種大殺器(能涵蓋所有字符的編碼格式我也是醉了),那自然要用它來作為統一的數據處理格式,至于那些關乎平臺關乎場景的case,再各自分情況處理。 3.所謂的默認file.encoding值是從哪得到的 說實話這是讓我非常頭疼的問題,因為臣妾實在是對工具神馬的一竅不通?。?!鬼知道TMD寫在哪個配置文件的哪個角落里?。?!尼瑪,我繼續搜,不停地搜。好不容易找到了。。由IDE控制的針對console的file.encoding默認設置。我用的是intellij,在$ItellijHOME/Contents/Info.plist,(這個HOME是我瞎BB的,看得懂就好了啊。。),確實找到了這么一串 <key>VMOptions</key> <string>-Dfile.encoding=UTF-8 -XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea -Dsun.io.useCanonCaches=false -Djava.net.preferipv4Stack=true -Xverify:none -Xbootclasspath/a:../lib/boot.jar</string> <key>WorkingDirectory</key> 看到那個“-Dfile.encoding=UTF-8”木有??!成就感滿滿有木有?。?!故事告一段落,最開始找到的那個UTF-8來自于IDE的默認設置,并且管的是console,web容器的file.encoding在tomcat運行設置的VM OPTION重新指定為UTF-8,于是輸出的web頁面格式OK了。 但是!怎么可以就此止步!不是有getProperty嘛,那也有setProperty啊,你不是說輸出到哪就獲取對應的file.encoding然后轉換unicode到file.encoding再輸出么,那我先按utf-8輸出,再setProperty成別的編碼格式,再輸出相同內容是不是就亂碼了? 事實證明,企圖用setproperty來修改runtime的file.encoding根本不起作用,該輸出啥還是輸出啥。于是我查了一下資料,file.encoding的值在JVM初始化的時候指定的才是有效地,運行時file.encoding壓根就是一個只讀屬性,據說是初始化的時候存了一個cache,以后即使改了也只會讀取cache也就是初始化的值,有興趣的可以去讀一下源碼,相關的討論鏈接在這了http://stackoverflow.com/questions/1749064/how-to-find-the-default-charset-encoding-in-java。自己也照著試了一下,的確如此,我只有修改vm option才會輸出亂碼。關鍵的幾句代碼: 以上。new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();//查看有效的file.encoding,也就是初始化時候的那個cache值System.getProperty("file.encoding");//查看file.encoding的真實值,雖然真實,但是無效啊。。
新聞熱點
疑難解答