2012年04月21日

C++/CLIでフィルタを作ってみる

先日バッチファイルを作ったとき、文字セット変換の部分をpythonのワンライナーで書いてみました。

python -c"import codecs,sys;sys.stdout=codecs.lookup('%CHARSET%')[-1](sys.stdout);f=codecs.open('text.tmp','r','sjis');print f.read();f.close()">text.txt

これで、SJISの文書をUTF-8にしたり、EUC-JPにしたりするわけです。でも、こんなことするのに、わざわざpythonインストールするのも大変なんで、どうしようかと考えてました。

まず考えたのは、perlで書くこと。patchコマンドを使うものを次に書こうと思っていて、Windows に patchコマンドを入れるんだったらMinGW/MSYSのものが入れやすいので( MinGW Developer Toolkitにチェックを入れる)、MinGW/MSYSを入れるとき、perlも入るので、perlでもいいと思ったわけです。

perlだと、かなりすっきりして、こんな感じになりました。(結局公開しなかったけど。)

perl -MEncode -pe "$_=encode('%CHARSET%',decode('SHIFT_JIS',$_));" text.tmp>text.txt

でも、patchコマンドの作業は、無ければ無いで手作業でできることなので、わざわざMinGW/MSYSを入れるのも大変だなと思って、他の手は無いかと考えてみました。

灯台もと暗し。C++でビルドする作業を自動化しようとしているわけですから、それもC++で書けばいいのです。C++/CLIで作る標準入力のデータを加工して、標準出力に出すだけのサンプルのような簡単なフィルタプログラムです。

以前、UTF-8に変換するプログラムをMultiByteToWideCharを使って書いたことありますが、面倒だった記憶しか残ってません。それは嫌なので、C++/CLIで作ってみました。.NET FrameworkのEncodingクラスが使えるなら、利用しない手はないわけです。結局こうなりました。

#using <System.dll>
using namespace System;
using namespace System::Text;
using namespace System::IO;

int main(array<System::String ^> ^args)
{
    TextReader^ input = Console::In;
    String^ charset;

    if (args->Length != 1)
    {
        return -1;
    }

    String^ sw = args[0]->ToLower();

    if (String::Compare(sw, "-utf-8") == 0) {
        charset = "UTF-8";
    }else if (String::Compare(sw, "-utf_8") == 0) {
        charset = "UTF-8";
    }else if (String::Compare(sw, "-utf8") == 0) {
        charset = "UTF-8";
    }else if (String::Compare(sw, "-euc-jp") == 0) {
        charset = "EUC-JP";
    }else if (String::Compare(sw, "-euc_jp") == 0) {
        charset = "EUC-JP";
    }else if (String::Compare(sw, "-eucjp") == 0) {
        charset = "EUC-JP";
    }else if (String::Compare(sw, "-euc") == 0) {
        charset = "EUC-JP";
    } else {
        Console::Error->Write(" 引数は-UTF_8と -EUC_JP のどちらか一方です。");
        return -1;
    }

    Encoding^ src = Encoding::Unicode;
    Encoding^ dst = Encoding::GetEncoding( charset );
    Stream^ output = Console::OpenStandardOutput();
    String^ line;

    while ((line = input->ReadLine()) != nullptr)
    {
        array<Byte>^temp = Encoding::Convert(src, dst, src->GetBytes(line + Environment::NewLine));
        output->Write(temp, 0, temp->Length);
    }
    return 0;
}

これを、cscnv.cpp なんて名前で保存して、こんなバッチファイルを作ってコンパイルします。VSVER=10.0 はVisual Studio 2010のことです。9.0 だと Visual Studio 2008 です。現在、既に評価版公開中の次期バージョンだと 11.0 です。

@echo off
set VSVER=10.0
set ARCH=x86
if %PROCESSOR_ARCHITECTURE% == x86 (
    set VCVARSALL_PATH="C:\Program Files\Microsoft Visual Studio %VSVER%\VC\vcvarsall.bat"
) else (
    set VCVARSALL_PATH="C:\Program Files (x86)\Microsoft Visual Studio %VSVER%\VC\vcvarsall.bat"
)
call %VCVARSALL_PATH% %ARCH%
cl /clr:safe cscnv.cpp

VC++ のツールメニューにあるVisual Studio のコマンドプロンプトを呼び出してから、ファイルのある場所に移動して、cl /clr:safe cscnv.cpp と入力してエンターでもいいです。

コンパイルが終わったら、コマンドプロンプトから、次のように打ち込んで、

echo こんにちは | cscnv –UTF8>test.txt

そのあと test.txt ファイルを開いて確かめます。うまくいっていれば、文字コードがUTF-8(BOM無し)のファイルができているはずです。

これで、python や perl などのコマンドを使わずに、shift-jis から、urf-8 や euc-jp への文字セットの変換ができるようになりました。

perl で一行で済ませられることを書くにしては、実質的な部分を比べても長いのは長いのですが、この基本形さえ押さえておけば、いろいろ応用できそうです。



posted by takayan at 12:46 | Comment(0) | TrackBack(0) | プログラミング | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]


この記事へのトラックバック