2015年6月17日 星期三

命令列繁體簡體中文互轉 (可用於批次轉檔)

簡體中文轉正體中文, 遇錯字會掉整列。 瀏覽簡體中文網頁時, 有 「新同文堂」 ( firefox 版 chrome 版) 外掛可以幫忙轉成正體中文。 但是如果想要在 linux 命令列上批次處理幾百個或幾千個任意的 html/txt/csv/json/svg/gpx/xml/... 文字資料檔, 那該怎麼辦呢?

隨便抓一個簡體中文檔, 存檔叫做 simplified.txt 。 (例如我這樣產生以下的實驗用簡體中文文字檔: lynx -dump 網址 > simplified.txt。) 然後剪貼下面的程式碼:

    #!/usr/bin/perl
    use Text::Iconv;
    use Getopt::Std;

    %opts = (
        f => "utf8", # from encoding
        t => "utf8", # to encoding
    );

    getopts('f:t:', \%opts);

    $iconv = Text::Iconv->new($opts{f}, $opts{t});

    while (<>) {
        print $iconv->convert($_);
    }

存在 /usr/bin 底下, 叫做 pliconv 好了。 第一列的 "#!..." 必須緊貼著最左上角! 上面不能有空列、 左邊不能有空格。 記得要 chmod a+x /usr/bin/pliconv。 這個程式的用法類似 iconv: pliconv -f utf8 -t gb2312 simplified.txt 這會把 utf8 編碼的 simplified.txt 轉成 gb2312 編碼, 印在螢幕上。 (當然就變成一堆亂碼。) 或者也可以從 stdin 讀資料: pliconv -f utf8 -t gb2312 < simplified.txt

再來, 請把這一句剪貼到命令列上: (一整句不要換列) function sc2tc () { pliconv -f utf8 -t gb2312 $@ | pliconv -f gb2312 -t big5 | pliconv -f big5 -t utf8 ; } 接下來 (直到登出為止) 就可以這樣轉檔: sc2tc < simplified.txt > traditional.txt 。 如果成功的話, 可以把上面定義 sc2tc 函數那一句貼到 ~/.bashrc 裡面去, 這樣以後都可以用。

注意! 遇到無法轉碼的字, 這個程式會默默地把一整列都省略掉! 如果你有比較熟悉的 「比較對照兩個文字檔」 的工具 (例如 vimdiff 會看到如本文插圖) 可以打開來比較一下, 會發現我的資料檔第 78 列 「本书采用创意共享...」 那一整列都被省略掉了。 你可以把 pliconv 的第一列 #!/usr/bin/perl 改成 #!/usr/bin/perl -W 這樣至少可以看見警告訊息、 得知哪些地方轉換失敗, 這個部分現在我仍無解。

* * * 以下是開發歷程記錄, 可略過不看 * * *

這個妙招的概念來自 麥克星球 Linux Fedora 心得筆記。 不過 iconv 非常易碎: 只要遇到一個字出錯就停下來, 剩下的就不轉了。

Perl 就好多了, 可以以列為單位。 其實很久以前我就已經定義了這兩個簡潔的 shell 函數跟 alias:

function enc() { perl -MEncode -pe "Encode::from_to(\$_,$1,$2)" ; }
alias b2u='enc big5 utf8'

然後就可以這樣把 big5 編碼的舊檔案轉成 utf8 編碼: b2u < old.txt > new.txt 所以原先我想: 如果定義: function sc2tc () { enc utf8 gb2312 $@ | enc gb2312 big5 | enc big5 utf8; } 那麼就可以這樣轉檔: sc2tc < simplified.txt > traditional.txt 。 但是實際測試一下, 我猜可能是因為 perl 的 Encode 模組對簡中支援不佳, 總之會掉很多字。 (又, 如果用 alias 定義 sc2tc 會失敗; 一定要用 function 定義才可以。)

最後我在 mailing list 上求救, 多虧 許仲佑老師 推薦 libtext-iconv-perl 套件, 於是我採用 iconv 較佳的轉換引擎、 perl 較有彈性較不怕錯的 「每列獨立處理」 才得到以上成功的結果。

我也進一步試著把讀進來的資料拆成一個個獨立的字元, 想說每個字轉檔, 遺失的內容就更少了。 當然, 程式碼還必須加上:

    binmode(STDIN, ":encoding($opts{f})");
    binmode(STDOUT, ":encoding($opts{t})");

不過還是失敗。 (因為 split 會拆成一個一個的 byte...) 算了, 以後再研究吧。

2 則留言:

  1. https://github.com/BYVoid/OpenCC

    回覆刪除
    回覆
    1. 大感謝! 我在 lubuntu 底下 apt-get install opencc
      就可以簡轉正了。 可是找不到設定檔?
      從 github 抓回來的檔案裡面有 data/config/*.json
      可是這樣下的話: opencc -c s2twp.json
      它說 Configuration file parse error ?
      所以要如何正體轉簡體呢?

      很讚耶! 除了 「爲」 這個字在我的
      fonts-arphic-ukai 字型裡看起來不太順眼...

      刪除