2013年7月10日水曜日

MutiByteToWideCharToMultiByte その3

3. fopen()と_wfopen()

fopen()はC言語でファイルを開く際に使われる標準関数だが、Windowsには別に_wfopen()という独自関数がある。
FILE *fopen(const char *filename, const char *mode)
FILE *_wfopen(const wchar_t *filename, const wchar_t *mode)
そもそもNT系Windowsのカーネルはファイル名はユニコードで扱い、NTFSはファイル名をユニコードで保存している。
たとえば"あいうえお.txt"というファイルを開く場合、
fopen("あいうえお.txt", "rw")
は、まず"あいうえお.txt"というACPでエンコードされた文字列をユニコードに変換した後、そのユニコード文字列と合致する名前のファイルを検索し、見つかればそれを開く。
一方、
_wfopen(L"あいうえお.txt", L"rw")
は、"あいうえお.txt"というUTF-16LEでエンコードされた文字列(文字列リテラールの前にLを付ければ、その文字列はコンパイル時にUTF-16LEに変換される)をユニコードに変換してから処理を行う。

では、もし両関数に"魔法少女♥マジカルJEEBたん 第3話.mkv"という文字列を渡すとどうなるか?

_wfopen(L"魔法少女♥マジカルJEEBたん 第3話.mkv", L"rw")
は、ファイルが存在するならば成功する。
しかし
fopen("魔法少女♥マジカルJEEBたん 第3話.mkv", "rw")
は、ファイルの有無に関わらずWindowsでは失敗する。
なぜなら文字列中の''という文字はACPにはない文字なので、ユニコードへの変換ができないからである。
ACPに存在しない文字が使われていた場合、その文字は'?'(クエスチョンマーク)に置換えられ、その後の処理を行う。

4. Avisynthと文字列のエンコード

Avisynthの全関数のうちで最も重要なものは一体なんだろうか?

答えはもちろんEval()である。
Eval()はメモリ上に展開された文字列データをパースしてどのような処理を行えばいいのかを判別し、それを実行する機能である。
Eval()とは、まさにAvisynthインタプリタそのものなのだ。
AVISource("abcde.avi")
というスクリプトを実行するとき、Avisynth内部では
Eval("""AVISource("abcde.avi")""")
を行なっている。
どんなスクリプトもEval()なしでは決して動かない。

さて、この大事な大事なEval()は、ASCII非互換なエンコーディングの文字列をパースすることが出来ない。
よって、あなたはスクリプトをBOM付きUTF-8やUTF-16、UTF-32で書いてはいけない

では、Eval()の次に重要な関数はなにか?

答えはもちろんImport()だ。
Import()はスクリプトが書かれたファイルを開いて中のデータをメモリ上に展開し、Eval()を呼び出す機能である。
VirtualDubであれx264.exeであれ、avsを入力する際はImport()が働いている。
Import()なしで動くプログラムなんて、AvsPmodのプレビューとvsavsreaderのavsr.Eval()くらいしかない(これらは自らスクリプトをメモリ上に展開し、直接Eval()を呼び出している)。

さて、このとっても大事なImport()だが、ファイルを開く際にはfopen()を使っている。
よって、あなたは決してavsのファイル名にユニコード限定文字を使ってはならない

さらにもう一つ重要なことを述べておく。
Avisynthは開発開始よりこれまでずっとマルチバイト文字ソフトウェアであったため、内部関数もプラグインも文字列のエンコーディングはACPであることを前提として書かれてきた。
よってあなたは自分の使っているWindowsのシステムロケールのACP以外でスクリプトを保存してはならない。
そしてこれは、素材となる動画/音声ファイル名にもACPにない文字を使ってはならないということを意味する。

唯一の例外としてffms2のutf8オプションがある。
ffms2は主にwine上でAvisynthを使う非Windows環境のユーザーのため、このオプションを設けている。
しかしffms2が考慮しているのはffms2が読み込む対象のファイル名だけなので、それ以外に非ASCIIな文字がスクリプト中に存在すれば、結果は保証されないものとなるだろう。

0 件のコメント:

コメントを投稿