今度は、SJIS以外の文書を読み込むとVC++が警告を出してしまう問題の対策をしてみます。
やろうとしていることは単純です。怒られそうな文字コードを16進数表記の文字リテラルに置き換えるだけです。シフトJISならば、2バイト文字の2バイト目に隠れている「 \ 」が表に出てきて、この記号にはいろいろな役割があるせいで、処理が大変ですが、EUC-JP の2バイト文字は 0x80よりも大きいものばかりで、UTF-8 の方も、1バイト文字以外は0x80以上の列になるので、どちらの文字セットでも0x80以上のものだけを16進数表記の文字リテラル(例えば\x80)にすることで、うまく解決できるはずです。
この問題に対しては、以前から escapize.py なんて名前の python スクリプトを対処していました。これもワンライナーで簡潔に表記できるなら、こんな別ファイルのスクリプトを書くより扱いやすいでしょう。
そういうわけで perl で書くと、バッチコマンド for の再帰探索を利用してこうなります。
for /R %%i in (*_%CHARSET%.h) do (
perl -i.backup -pe "s/(.)/(ord($1)>=128)?sprintf('\\x%%x',ord($1)):$1/eg" "%%i"
)
ビルド処理が終わったら .backup を元に戻します。perl が使える環境ならば、これで解決です。
今度は、perl は入っていないけれどVC++ は入れてある環境で考えてみます。 これも前回のように C++/CLI で作るならば、簡単に正規表現が使えるので、上と同じものを書けるだろう、と作る前は思ってました。でもそう簡単にはいきません。正規表現は Stringクラス、つまり Unicode が対象です。UTF-8 や EUC-JP の生の文字コードが扱えません。
そもそも正規表現を使うまでもない内容なので、それはすんなり諦めて、他にC++/CLI でやる方法を考えてみました。C++/CLI では指定すれば UTF-8 や EUC-JP で文書を読み込むことができると分かりましたが、それは String クラス(Unicode)に変換するためのものでした。今回のような場合はバイナリー配列として読み込む必要があるようです。実際いろいろ作ってみたら、キーボードからの入力だと思い通りにならなかったり、難しいので、結局、純粋な C++ のプログラムとして書くことにしました。
ということで、
#include <string>
#include <iostream>
int main()
{
while ( !std::cin.eof() )
{
std::string line;
std::getline(std::cin, line);
for ( int i = 0; i < line.length(); i++ )
{
unsigned char c = line[i];
printf( (c >= 128)?"\\x%02X":"%c", c );
}
std::cout << std::endl;
}
return 0;
}
このファイル名を csesc.cpp などとして、前回のようにビルド用の環境設定をやったあと、次のコマンドで実行ファイル csesc.exe ができあがります。
cl /EHsc csesc.cpp
前回のコマンドと組み合わせて、次のような感じで動作確認できます。
echo こんにちは|csesc
echo こんにちは|cscnv –utf8|csesc
結果:
\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD
\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF
こういう実験もできます。csesc.cpp に適当に日本語のコメントを入れて、UTF-8(BOM無し)かEUC-JPで保存してみます。ちなみに、BOM無しUTF-8やEUC-JPで保存できる無料のエディタは サクラエディタ や EmEditor Free などがあります。
これをコンパイルすると警告が出ます。そこで、このフィルタ・コマンドの登場です。
cl /EHsc csesc.cpp
csesc <csesc.cpp >csesc_new.cpp
cl /EHsc csesc_new.cpp
なお、今回はEUC-JPのファイルも対象だったので、この方法をとりましたが、これが UTF-8 のファイルだけだったら、ファイルの頭にUTF-8であることを示すBOM("\xEF\xBB\xBF")をくっつけても解決できます。
(修正 \xAA の形式で、 \の後ろの’x’は必ず小文字じゃないといけません。)