2012年05月09日

Open JTalk を利用する日本語読み上げ bash スクリプト

先日、Windows 用にバッチファイル ojtalk.bat を作ってみましたが、今度は似たような内容で Linux 用に bash スクリプトを作ってみました。対象は Ubuntu 12.04 と MinGW です。それ以外の場合は、実際にインストールされている場所に DICDIR と VDIRBASE の値を書き換えれば、おそらく動くでしょう。このスクリプトを /usr/local/bin あたりにコピーして使います。

Ubuntu 12.04 では、open-jtalk、open-jtalk-mecab-naist-jdic、hts-voice-nitech-jp-atr503-m001が apt-get や Ubuntuソフトウェアセンターでインストールできるので、それでインストールされているとします。

このスクリプトでは読み上げ文字列の指定は三種類あります。

まず、ちょっとした言葉はコマンドラインに書きます。

ojtalk こんにちは
長い文章の時は、ファイルに utf-8 エンコードで保存して -f オプションで指定します。
ojtalk -f hoge.txt
標準入力を読ませたいときは、文字を付けない '-' をコマンドラインに置きます。
cat hoge.txt|ojtalk -
上記の読み上げ文字列の指定が複数あるときは、左から順に処理します。ただし文字列の合計の長さは bash の限界までです。

その他に、音声ファイルを作るときは、-ow hoge.wav とします。このとき、しゃべらせたくないときは、-quiet も一緒に指定します。hts-voice フォルダに他の音声データが入っているときは -vname mei_normal といった感じで音声を替えられます。このとき、その中に voice.info ファイルが入っていると、その情報を参照します。

さて、以前からの課題である GNOME orca の日本語化の件ですが、この bash スクリプトを使えば、python から次の形で呼び出せますから、以前よりシンプルに作れると思います。

os.system(u'ojtalk こんにちは'.encode('utf8'))

まず、このスクリプトを利用したものを作ってから、そのあとで Speech Dispatcher 経由のものを考えることにします。

以下、ojtalk ソース:

#!/bin/bash

if [ `uname|grep "MINGW"` ]; then
	DICDIR=/usr/local/dic
	VDIRBASE=/usr/local/hts-voice
	DEFAULT_VOICE_NAME=hts_voice_nitech_jp_atr503_m001
	mingw=1
else
	DICDIR=/var/lib/mecab/dic/open-jtalk/naist-jdic
	VDIRBASE=/usr/share/hts-voice
	DEFAULT_VOICE_NAME=nitech-jp-atr503-m001
	mingw=0
fi

# default parameters
MESSAGE='open jtalk'
S=48000
P=240
A=0.55
G=0
B=0.0
U=0.5
Z=1600
JM=1.0
JF=1.0
JL=1.0
MEI_S=48000
MEI_P=240
MEI_A=0.55
MEI_G=0
MEI_B=0.0
MEI_U=0.5
MEI_Z=6000
MEI_JM=0.7
MEI_JF=0.5
MEI_JL=1.0

if [ $mingw == 1 ]; then
	CURDIR=`dirname $0`
	TEMPTXTFILE=$CURDIR/_text.txt
	TEMPWAVFILE=$CURDIR/_wave.wav
else
	TEMPTXTFILE=$(mktemp /tmp/ojtalk_txt_XXXXXXXX)
	TEMPWAVFILE=$(mktemp /tmp/ojtalk_wav_XXXXXXXX)
fi

L_SWITCH=
QUIET_SWITCH=0
VNAME_VALUE=
VDIR_VALUE=
S_VALUE=
P_VALUE=
A_VALUE=
G_VALUE=
B_VALUE=
U_VALUE=
Z_VALUE=
JM_VALUE=
JF_VALUE=
JL_VALUE=
OT_VALUE=/dev/null
OW_VALUE=
TEXT=

error(){
	cmd=`basename $0`
	message="Bad parameter!"
	if [ "$1" ]; then message=$1; fi
	echo "$cmd: $message";
	exit 1;
}

if [ ! -d $DICDIR ]; then
	error "No dic directory : $DICDIR"
fi

if [ ! -d $VDIRBASE ]; then
	error "No voice directory : $VDIRBASE"
fi

while [ $1 ]; do
	if [ $1 == -l ]; then
		L_SWITCH=-l
	elif [ $1 == - ]; then
		data=$(tr '\n\r' '  ')
		TEXT="$TEXT $data"
	elif [ $1 == -vname ]; then
		shift
		if [ ! $1 ]; then error; fi
		VNAME_VALUE=$1
	elif [ $1 == -vdir ]; then
		shift
		if [ ! $1 ]; then error; fi
		VDIR_VALUE=$1
	elif [ $1 == -quiet ]; then
		QUIET_SWITCH=1
	elif [ $1 == -f ]; then
		shift
		if [ ! $1 ]; then error; fi
		if [ -f $1 ]; then
			data=$(cat $1|tr '\n\r' '  ')
			TEXT="$TEXT $data"
		else
			error "No text file: $1"
		fi

	elif [ $1 == -ow ]; then
		shift
		if [ ! $1 ]; then error; fi
		OW_VALUE=$1
	elif [ $1 == -ot ]; then
		shift
		if [ ! $1 ]; then error; fi
		OT_VALUE=$1
	elif [ $1 == -s ]; then
		shift
		if [ ! $1 ]; then error; fi
		S_VALUE=$1
	elif [ $1 == -p ]; then
		shift
		if [ ! $1 ]; then error; fi
		P_VALUE=$1
	elif [ $1 == -a ]; then
		shift
		if [ ! $1 ]; then error; fi
		A_VALUE=$1
	elif [ $1 == -g ]; then
		shift
		if [ ! $1 ]; then error; fi
		G_VALUE=$1
	elif [ $1 == -b ]; then
		shift
		if [ ! $1 ]; then error; fi
		B_VALUE=$1
	elif [ $1 == -u ]; then
		shift
		if [ ! $1 ]; then error; fi
		U_VALUE=$1
	elif [ $1 == -jm ]; then
		shift
		if [ ! $1 ]; then error; fi
		JM_VALUE=$1
	elif [ $1 == -jf ]; then
		shift
		if [ ! $1 ]; then error; fi
		JF_VALUE=$1
	elif [ $1 == -jl ]; then
		shift
		if [ ! $1 ]; then error; fi
		JL_VALUE=$1
	elif [ $1 == -z ]; then
		shift
		if [ ! $1 ]; then error; fi
		Z_VALUE=$1
	else
		TEXT="$TEXT $1"
	fi
	shift
done

if [ ! $VDIR ]; then
	if [ $VNAME_VALUE ]; then
		VDIR=$VDIRBASE/$VNAME_VALUE
	else
		VDIR=$VDIRBASE/$DEFAULT_VOICE_NAME
	fi
fi
if [ ! -d $VDIR ]; then
	error "No voice directory : $VDIR"
fi

if [ -f $VDIR/voice.info ]; then
	while read line; do
		if [[ $line =~ ^MESSAGE= ]]; then
			MESSAGE=`echo $line|sed "s/^MESSAGE=//"`
		fi
		if [[ $line =~ ^s=  ]]; then
			S=`echo $line |sed "s/^s=//"`
		fi
		if [[ $line =~ ^p=  ]]; then
			P=`echo $line |sed "s/^p=//"`
		fi
		if [[ $line =~ ^a=  ]]; then
			A=`echo $line |sed "s/^a=//"`
		fi
		if [[ $line =~ ^g=  ]]; then
			G=`echo $line |sed "s/^g=//"`
		fi
		if [[ $line =~ ^b=  ]]; then
			B=`echo $line |sed "s/^b=//"`
		fi
		if [[ $line =~ ^u=  ]]; then
			U=`echo $line |sed "s/^u=//"`
		fi
		if [[ $line =~ ^z=  ]]; then
			Z=`echo $line |sed "s/^z=//"`
		fi
		if [[ $line =~ ^jm= ]]; then
			JM=`echo $line |sed "s/^jm=//"`
		fi
		if [[ $line =~ ^jf= ]]; then
			JF=`echo $line |sed "s/^jf=//"`
		fi
		if [[ $line =~ ^jl= ]]; then
			JL=`echo $line |sed "s/^jl=//"`
		fi
	done < $VDIR/voice.info
fi

if [[ $VNAME_VALUE =~ ^mei_ ]]; then
	if [ ! $S_VALUE  ]; then  S_VALUE=$MEI_S;  fi
	if [ ! $P_VALUE  ]; then  P_VALUE=$MEI_P;  fi
	if [ ! $A_VALUE  ]; then  A_VALUE=$MEI_A;  fi
	if [ ! $G_VALUE  ]; then  G_VALUE=$MEI_G;  fi
	if [ ! $B_VALUE  ]; then  B_VALUE=$MEI_B;  fi
	if [ ! $U_VALUE  ]; then  U_VALUE=$MEI_U;  fi
	if [ ! $Z_VALUE  ]; then  Z_VALUE=$MEI_Z;  fi
	if [ ! $JM_VALUE ]; then JM_VALUE=$MEI_JM; fi
	if [ ! $JF_VALUE ]; then JF_VALUE=$MEI_JF; fi
	if [ ! $JL_VALUE ]; then JL_VALUE=$MEI_JL; fi
else
	if [ ! $S_VALUE  ]; then  S_VALUE=$S;  fi
	if [ ! $P_VALUE  ]; then  P_VALUE=$P;  fi
	if [ ! $A_VALUE  ]; then  A_VALUE=$A;  fi
	if [ ! $G_VALUE  ]; then  G_VALUE=$G;  fi
	if [ ! $B_VALUE  ]; then  B_VALUE=$B;  fi
	if [ ! $U_VALUE  ]; then  U_VALUE=$U;  fi
	if [ ! $Z_VALUE  ]; then  Z_VALUE=$Z;  fi
	if [ ! $JM_VALUE ]; then JM_VALUE=$JM; fi
	if [ ! $JF_VALUE ]; then JF_VALUE=$JF; fi
	if [ ! $JL_VALUE ]; then JL_VALUE=$JL; fi
fi

if [ "$TEXT" ]; then
	echo $TEXT>$TEMPTXTFILE
else
	echo $MESSAGE>$TEMPTXTFILE
fi
if [ ! -f $TEMPTXTFILE ]; then
	error "No temp file : $TEMPTXTFILE"
fi

open_jtalk \
-x  $DICDIR \
-td $VDIR/tree-dur.inf \
-tm $VDIR/tree-mgc.inf \
-tf $VDIR/tree-lf0.inf \
-tl $VDIR/tree-lpf.inf \
-md $VDIR/dur.pdf \
-mm $VDIR/mgc.pdf \
-mf $VDIR/lf0.pdf \
-ml $VDIR/lpf.pdf \
-dm $VDIR/mgc.win1 \
-dm $VDIR/mgc.win2 \
-dm $VDIR/mgc.win3 \
-df $VDIR/lf0.win1 \
-df $VDIR/lf0.win2 \
-df $VDIR/lf0.win3 \
-dl $VDIR/lpf.win1 \
-ow $TEMPWAVFILE \
-ot $OT_VALUE \
-s  $S_VALUE \
-p  $P_VALUE \
-a  $A_VALUE \
-g  $G_VALUE \
-b  $B_VALUE \
$L_SWITCH \
-u  $U_VALUE \
-em $VDIR/tree-gv-mgc.inf \
-ef $VDIR/tree-gv-lf0.inf \
-cm $VDIR/gv-mgc.pdf \
-cf $VDIR/gv-lf0.pdf \
-jm $JM_VALUE \
-jf $JF_VALUE \
-jl $JL_VALUE \
-k  $VDIR/gv-switch.inf \
-z  $Z_VALUE \
$TEMPTXTFILE

if [ $mingw == 0 ]; then
	if [ -f "$TEMPWAVFILE" ]; then
		if [ $QUIET_SWITCH == 0 ]; then
			aplay -q $TEMPWAVFILE
		fi
	fi
fi
if [ "$OW_VALUE" ]; then
	cp $TEMPWAVFILE $OW_VALUE
fi
rm -f $TEMPWAVFILE
rm -f $TEMPTXTFILE

exit 0


拍手する
posted by takayan at 02:30 | Comment(0) | TrackBack(0) | 音声合成 | このブログの読者になる | 更新情報をチェックする

2012年05月02日

「カーネーション」の総集編は5月3日、4日

見たいのに気付いていない人もいるかもしれないので。

カーネーションの総集編が、NHKの「大型連休」中にあります。

連続テレビ小説「カーネーション」総集編 前編「あこがれ」
チャンネル     :総合
放送日     :2012年 5月 3日(木)
放送時間     :午前8:20〜午前9:51(91分)

連続テレビ小説「カーネーション」総集編 後編「あなたの愛は生きています」
チャンネル     :総合
放送日     :2012年 5月 4日(金)
放送時間     :午前8:20〜午前9:51(91分)

ちなみに、サブタイトルはそれぞれ第一週と最終週と同じ

尾野さんの別の顔が見られます!ということで、尾野真千子が出ている「外事警察」の特別編集版もあります。
放送日時:5月4日(金) 午前0:25〜1:54<3日深夜>

ソース:http://www.nhk.or.jp/drama/sp_201205/

 

それから、朝ドラ恒例の「梅ちゃん先生」の開始1ヶ月ダイジェストが総合とBSプレミアムであります。

まだまだ間に合う!「梅ちゃん先生」
○5/3(木)午後5:30〜5:59【総合】
○5/4(金)午後10:30〜10:59【BSプレミアム】

ソース:http://www9.nhk.or.jp/umechan/rel/index.html#rel01

十分間に合います。



拍手する
posted by takayan at 02:00 | Comment(0) | TrackBack(0) | 映画・ドラマ | このブログの読者になる | 更新情報をチェックする

2012年05月01日

Open JTalk を Windows でビルドするバッチファイル

前回、hts_engine_API の最新バージョンを間違えてしまったので、書き換えたものを作ろうとしたのですが、折角なのでバッチファイルの内部でバージョン番号を書き換えないでいいものを作ったほうが便利そうだったので、そうしてみました。

さらに、以前ソルーションファイルを作って、VC++を起動してビルドできるようにしていましたが、そのファイルも今回の成果をいろいろ追加して入れてみました。VC++2008用ですが、2010で開けば自動的に変換するので利用できます。

ojtalk.bat の仕様も変えて、他の音声に切り替えやすいようにしてみました。あとで説明しますが、音響モデルのフォルダに省略時のパラメータを書いたテキストがあれば、それを使うようにしています。

 

■ ビルド方法

open_jtalk.exe のビルドには、Visual Studio C++ 2010Visual Studio C++ 2008 のどちらかが必要です。2010Express 版でもいいはずです。それからファイルの解凍のために 7zip か tar が必要です。

ファイルは次のリンクからダウンロードします。

openjtalk_buildbatch-003.zip

展開して、そのフォルダに、Open JTalkのビルドに必要な3つのアーカイブを入れます。リンク先から最新版をダウンロードしてください。
open_jtalk-*
hts_voice_nitech_jp_atr503_m001-*
hts_engine_API-*

最初に、prepare.bat を実行します。すると、アーカイブを展開し、インストール先(標準設定でc:\open_jtalk)に、この時点で転送できるファイルを全て転送してくれます。

prepare.bat で準備がすんだら、make.bat を実行します。すると、各フォルダーの Makefile.mak の手順に従い open_jtalk.exe を生成し、インストール先のbinフォルダーに転送します。これで終わりです。

ちゃんと動くかどうかは、インストール先にある ojtalk.bat をダブルクリックして実行すれば確かめられます。「open jtalk」としゃべってくれれば、とりあえずOKです。一度ojtalk.bat を実行すると、インストール先に text.txt が作られるので、このファイルに文章を書き込み、その都度、ojtalk.bat をダブルクリックすると、その内容を読み上げてくれるはずです。

 

■ ソルーションファイル

make.bat を使えば、十分なのですが、open_jtalk\open_jtalk の中にある open_jtalk.sln を使うと、Visual Studio の画面を開いて、ビルドできます。ただし、通常は、ソルーション構成は Release か Debug、ソルーションプラットフォームは Win32 にしてください。必要ないとは思いますが、他の設定を利用するには、prepare.bat の実行前に、 build.info を編集しておく必要があります。

なお、Visual Studio を通して辞書をコンパイルすると相当時間がかかります。気長に待ってください。

 

■ 利用例

今回付属してある ojtalk.bat では、インストール先のvoiceフォルダ(デフォルトだとc:\open_jtalk\voice)に音響モデルのファイルをフォルダごと入れるようにしています。さらにその中に voice.info というファイルがあれば、省略時の -s、-p、-a、-g、-b、-u、-z、-jm、-jf、-jl の値があるとします。mei の利用の仕方は以前紹介しましたが、例えば、フォルダ mei_normal の中に、次の内容の voice.info を入れておきます。

s=48000
z=6000
p=240
a=0.55
jm=0.7
jf=0.5
jl=1.0

音響モデルの指定を –vname でできるようにしたので、次のコマンドで Mei が挨拶します。

ojtalk.bat –vname mei_normal こんにちは

さらに、Windows スクリプティングホスト(wsf) を使うと次のようなこともできます。

<job id="time">
<script language="JScript">
d = new Date();
m = d.getMinutes();
m = (m=="0")?"ちょうど":m+"分";
WScript.CreateObject("WScript.Shell").Run("c:\\open_jtalk\\ojtalk.bat -vname mei_normal "+d.getHours()+"時"+m+"です。",0,true);
</script>
</job>

このような内容の time.wsf というファイルを作って、ダブルクリックすると、時報を伝えてくれます。

さらに、これをインストールしたフォルダに入れておいて、デスクトップにショートカットを作ります。ショートカットを作ると、そのプロパティを開いてショートカットキーも登録できるようになります。例えば、それをCTRL+ALT+Tなどとすると、このショートカットキーがかぶらなければ、このキー操作でいつでも時刻を教えてくれます。またこのスクリプトを、タスクスケジューラに登録して、繰り返し間隔などを設定すれば、定期的に現在時刻を教えてくれるようになります。

 

■ ちょっと複雑な設定

普通に使うならば、上に書いた方法で十分ですが、make.bat でテキストの文字セットを UTF-8 や EUC-JP にしたり、x64向けのビルドをする場合は、build.info を書き換えればできるように作っています。ソルーションファイルを使う場合も同じです。

自動でパッチを当てるときは、パッチを当てるかどうかを「=」の右に文字があるかないかで判断しています。ただこの build.info についての説明はあまり必要ないだろうし、説明も長くなるので割愛します。

 

■ patch コマンドの導入

今回の修正程度ならば、手作業の方が手っ取り早いですが、一応 patch コマンドの導入方法も書いておきます。MinGW/MSYS を利用する方法です。次のサイトから、mingw-get-inst-*.exe という名前のインストーラをダウンロードしてきて実行します。(現時点の最新版は mingw-get-inst-20120426.exe)

MinGW - Minimalist GNU for Windows -  Browse Files at SourceForge.net 

インストール場所は標準のC:\MinGWにします。今回の利用だけならば、コンポーネントは何も選択しなくてもいいです。インストーラの使い方が分からないときは、こちらのサイトに詳しく書いてあります。Windows で MinGW バージョン 20110530 のインストールとテスト実行

インストールが終わったら、次のコマンドを、コマンドプロンプトを開いて実行します。

cd c:\MinGW\bin
mingw-get install msys-patch

これで導入完了。この場所にあれば、PATH を通さなくても、今回のバッチファイルでは patch が実行されるようにしています。ついでに、折角調べたので忘れないように書いておくと tar コマンドは「mingw-get install msys-tar-bin」で入ります。

なお、このように patch コマンドを導入しても、現在のバージョンに対応したパッチが用意されていないと自動でパッチは適用されません。今回用意しているのは、open_jtalk-1.05、hts_engine_API-1.06 向けのパッチです。



拍手する
posted by takayan at 00:38 | Comment(3) | TrackBack(0) | 音声合成 | このブログの読者になる | 更新情報をチェックする

2012年04月25日

Open JTalk を 64ビットWindows用にビルドする方法

Open JTalk を x64 Windows 向けにビルドする方法を調べてみました。

単純にターゲット環境を /MACHINE:x64 のスイッチで64ビットにしてビルドすると、Mecabのコード部分でコンパイルエラーが出ます。feature_index.cppとwrite.cpp です。原因は型の曖昧さエラーです。これを下記のページを参考にしてキャストで型を明示的に指定して解決します。

64ビットWindows用にMeCabをビルドする (Visual Studio 2010を利用)

open_jtalk_1.05 に使われている feature_index.cpp では310行目、write.cpp は235行目が問題の箇所となっています。

feature_index.cpp:310
-            case 't':  os_ << (size_t)path->rnode->char_type;     break;
+            case 't':  os_ << (unsigned int)path->rnode->char_type;     break;

writer.cpp:235
-          case 'L': *os << std::strlen(sentence); break;
+          case 'L': *os << (unsigned int)std::strlen(sentence); break;

とりあえず、これだけの修正でビルドが通ります。しかし、実際に動かすと、応答無しになってしまいます。

VC++でデバッグして調べてみると、hts_engine_API-1.06.tar.gz にある HTS_audio.c の167行目、関数 waveOutOpen 呼び出しでの引数の型が原因だと分かりました。4番目と5番目の引数のキャストを DWORD から DWORD_PTR に変えると、エラーが起きなくなります。DWORDとしてあるのはx64を考慮していない頃の古い定義によるものです。

HTS_audio.c:167
-   error = waveOutOpen(&audio->hwaveout, WAVE_MAPPER, &audio->waveformatex, (DWORD) HTS_Audio_callback_function, (DWORD) audio, CALLBACK_FUNCTION);
+   error = waveOutOpen(&audio->hwaveout, WAVE_MAPPER, &audio->waveformatex, (DWORD_PTR) HTS_Audio_callback_function, (DWORD_PTR) audio, CALLBACK_FUNCTION);

以上、3行修正して、x64向けのビルドをすると、open_jtalk.exe は 64ビットアプリケーションとして動くようになります。

このくらい手作業で十分ですが、折角なので、先日コメント欄で指摘してもらった長文で音声がおかしくなる問題を修正したものも含めて、パッチを三つ作ってみました。

open_jtalk-1.05_x64_patch.zip

patch コマンドがあれば、展開したフォルダに該当するパッチファイルを置いて、例えば、次のコマンドでパッチが当てられます。

patch -p1 < open_jtalk-1.05_x64.patch



拍手する
posted by takayan at 23:53 | Comment(0) | TrackBack(0) | 音声合成 | このブログの読者になる | 更新情報をチェックする

2012年04月22日

16進エスケープシーケンスにするフィルタ

今度は、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’は必ず小文字じゃないといけません。)



拍手する
posted by takayan at 19:34 | Comment(0) | TrackBack(0) | プログラミング | このブログの読者になる | 更新情報をチェックする