解決wget中文亂碼問題的非完善方案

上一頁 | 首頁

1. 出現亂碼的原因

  http請求的流程如下:
  (1) 瀏覽器按URL編碼規則將URL(包括post或get提交的那一部分)字符串編碼之後發送給服務器;
  (2) 服務器將所收到的字符串轉換為unicode編碼,處理完瀏覽器的請求後再把它發送給瀏覽器;
  (3) 瀏覽器按指定的編碼顯示網頁。
  在不同的字符編碼格式下,瀏覽器對包含諸如中文之類的非ACSII字符的URL字符串的解析結果是不一樣的。從而,URL編碼對象就不一樣了。wget下載網頁文件時,實質上是一個瀏覽器。它同樣會對所提交的URL字符串按URL編碼規則編碼。因此,所下載的文件的文件名就是經過編碼(先是按URL規則編碼,接著是unicode編碼,最後是unicode到瀏覽器端所設定的編碼)之後的字符串。如果瀏覽器端採用的是unicode編碼,則結果是一串由'%'、數字和字母組成的字符串;否則,將unicode編碼格式的URL字符串解析為瀏覽器端的編碼格式,這可能會產生非ACSII字符。我們所說的亂碼,包括這兩種情況。也就是說,跟原來的文件名不一樣,我們就管它出現亂碼。

2. 解決方案

  由于上述原因,我們有必要修改wget的源代碼。對URL字符串進行編碼的源代碼文件是url.c。其中,url_file_name()的功能是根據URL判斷應該以什麼文件名保存文件。而該函數又調用了append_uri_pathel(),該函數調用了FILE_CHAR_TEST()宏,它用于判斷URL中的字符是不是特殊字符(也就是需要進行URL編碼的字符。當然,包括中文)。問題就出在這個宏身上了。為了不對中文轉義,需要將中文字符當作普通字符對待。將如下所示的FILE_CHAR_TEST()宏:
  #define FILE_CHAR_TEST(c, mask) \
    ((opt.restrict_files_nonascii && !c_isascii ((unsigned char)(c))) || \
    (filechr_table[(unsigned char)(c)] & (mask)))
修改為:
  #define FILE_CHAR_TEST(c, mask) \
    (((opt.restrict_files_nonascii && !c_isascii ((unsigned char)(c))) || \
    (filechr_table[(unsigned char)(c)] & (mask))) \
    && !((c|0x0fffffff) == 0xffffffff)) /* 排除中文 */
  另外,如果使用unicode編碼方案的話,就不會出現上述第二種亂碼。因此,可以將瀏覽器端的字符編碼環境設置為unicode。這裡,選擇UTF-8編碼方案(UCS Transformation Format)。方法是在main.c的i18n_initialize最後加上:'setlocale(LC_CTYPE, "zh_CN.UTF-8");'。
  這不是一個很好的解決方案。另外,它沒辦法解決其他語言的亂碼問題。當然,可以將url_file_name()中的URL編碼部份統統拿掉。這樣做的結果是,不出現亂碼,現時亦違背了URL編碼的初衷--安全。不過我還沒想到更簡單的方案。這是一種暫行方案。希望官方的解決方案的出現不會路漫漫其修遠兮。。。


版權 © 2014 石仔