Windowsの文字コードって…

今のWindowsはファイル名もUnicodeになったので,「萌え♥.txt」のようなファイルが簡単に作れる(♥はWindowsの「文字コード表」ツールで入力できる)。ところがこのファイル,メモ帳では開けるが,xyzzyでは開けない。

最近のMacやLinuxは,ファイル名はUTF-8だ。これをそのままZipで束ねて,Windowsで解凍すると,名前が化ける。あらかじめconvmvでファイル名をShift JIS(正確にはCP932)に変換してからZipしなければならない(Linux 上で Windows 向けの zip ファイルを作る - ~fumi/ChangeLog 参照)。なんでUnicodeなのにShift JISなの? これでは「萌え♥.txt」は扱えないし。

MoodleのようなWebアプリでも,Windowsサーバを使った場合にこれが問題になる。MoodleはUTF-8で動いているので,ファイルをサーバに保存しようとすれば名前がUTF-8になる。ところが一般にUTF-8のバイト列はShift JISのバイト列ではないので,おかしくなる。UTF-16LEのバイト列にしたところで問題は解決できない。白井先生のfs_moodleはMoodleに徹底的にパッチを当てて,サーバのファイルシステムとやりとりするところをすべてShift JISにしたものだ。このような日本人の悩みは本家には伝わらず,パッチが本家にマージされることはない。もちろんLinuxやMac OS Xをサーバに使うか,ファイル名を英数字に限ってもらえれば問題ないが,近ごろはサーバ管理者もWindowsしか触りたがらない人が増えているし,ましてやエンドユーザにファイル名は英数字などと言ってもまったく無理である。

PHP 6になれば事態が好転するのだろうか。

もうちょっとテクニカルなことも含めて Moodleのフォーラム で議論している。Windowsに詳しいかたのご協力をお願いします。

心臓バクバク

 た、大変です。
 Moodleに、携帯電話のフルブラウザでアクセスしてデータフォルダ内に(絵文字の)ハート記号を含むフォルダを作って見たら、ハートがドキンコドキンコと脈動しています...理屈上は納得できるのですが、いざ自分の手の中で拍動するフォルダを見ると結構、不気味ですね...
(外字エリアかな。Windowsからどう見えるのか、明日の朝が楽しみ)

ファイルシステムがUTF-16LE

 調査していて面白いな、と思ったことが一点。
 WindowsNT系列はOSのコアではファイル名の文字コードをUTF-16LEに統一しているということに関してLinux派との間で揉め事になっている。これは"自由"の考え方の違いにあるように見える(どちらが良い、悪いではなく)。
 「Linuxではファイル名に使用できない文字コードの制限が少ないので何でもありな自由」と「WindowsNTは(最終的には)Unicodeをファイル名の文字コードに統一するので好きな名前を付けられるようになる自由」。
 悪くいえば放任かつ個人主義的傾向のあるLinuxの"自由"と、過保護と排他的傾向が表裏一体なWindows(Microsoft)の"自由"、さぁ、あなたはどちらを好みますか、と。うーん、私は命令されたり制限されたりするのが嫌いではないのですが、本件は被害を被る一方のみなので困っています;_;

今、ラインの黄金終わりました

という事で聴きながら xyzzy のソースを斜め読みしてたんですが…

UNICODE 定義してないのね。

という事は… CreateFile のような API はファイル名を UNICODE とは見てくれてないような。95 や 98 を見捨てていいなら UNICODE を定義してやればちゃんと期待通りの動きをするかもしれません。(あくまでも予想、もっとも副作用がどこかに出るかもしれない。) または MSLU (Microsoft Layer for Unicode) を使うか。

Moodle は PHP なので、Windows 版の PHP が同じような問題を持っている…のかな? PHP のソースを追っかければ見当がつきそうだけど眠いのでこの辺でZzzz....

んで寝る前にふと思った

dvioutなんかはその辺をどうしてるんでしょうね?
あれも xyzzy と同様の問題があってもおかしくないわけですが。

とメモだけして寝ようZzz....

ラインの黄金いいないいな

そうか,95/98/Meと互換性を持たせるためには#define UNICODEしてはいけないんですね。バイナリを二通り用意しなければならないんでしょうか。

TeX関連のコマンドが扱えるファイル名はそもそもShift JISもあやしいです(0x5cを含む文字など)。

Re: ファイルシステムがUTF-16LE

これはLinuxというか,UNIXがそもそもファイル名には0x00を除く任意のバイト列が使えるようになっているのですよね。

Windowsのシェルでハートパクパク見えたらおもしろいですね。Unicodeにはそもそも字形が時間変化する文字という概念はないのかな。

_wfopen()

Windowsに詳しい知人からメールでコメントいただきました:

---------------------------------------

情報が錯綜している感じですが,Windows(2k以降は)でもきちんと作れば当然
Unicode対応はできます.

フォーラムでも書かれていますが,システムコールであるCreateFile()は
マクロUNICODEが定義されているときは,CreateFileW(const wchar_t *, ...)となり,
UTF-16LEを扱えるので,ハートマークも正しく処理できます.
UNICODEが定義されていないときはCreateFileA(const char *, ...)となり,マルチバイト
文字列(通常日本ではcp932)を処理できます.

UNICODEマクロを定義した状態で,char*で受け付ける入力文字コードをUTF-8に限れば,
UTF-8<=>UTF-16変換は容易ですから,apacheの「Windows NT上ではUTF-8でファイル名を
扱える」は正しいと思われます.

またANSIのfopenのwchar_t版でも

#include <stdio.h>
main()
{
FILE *fp = _wfopen(L"\u2665.txt", L"w");
}
とすれば"ハートマーク.txt"のファイルを作れます.

ただし,上記のようにUNICODEが定義されているときとそうでないときで,charなどの型が
変わってしまうため,Windowsアプリケーションプログラマがそのあたりをきちんと考慮
して作る必要があります.

Windowsの作法としては,UNICODEが定義されいる/されていないのどちらでもきちんとコンパイル
できて,動くような枠組みは用意されていて,「Advanced Windows」にもそのやり方が書かれています.

しかし,なかなか面倒なので#define UNICODEを定義して再コンパイルするだけでUNICODE対応
というわけにはいかないのが実情だろうと思います.

_tfopen()

続きです(行頭のスペースがうまく入らない ^^;):

--------------------------------------

> ありがとうございます。今まで fopen() で書いてきたものは
> _wfopen() を使えばいいわけですか(ここでは #define
> UNICODE は不要ですか)。

はい._UNICODEうんぬんは,WinAPIの話でのみ問題になり,_wfopen()はCの関数の話
ですので不要です.
とは言え下記のように切り替えられるように作るなら必要と言ってもよいでしょう.

> 文字列リテラルは "w" に至るまで すべて L"..." にしないといけないのですね。

そうです.そのためにWindowsではtchar.hというヘッダが用意されていて,
_T("w")とすると,_UNICODEが定義されていればL"w"に,されていなければ"w"に展開
されるような仕組みがあります.たとえばstrlenも_UNICODEがのdef/undefに対して,
const char*, const wchar_t*を切り替えるマクロが用意されています.

cf. http://msdn.microsoft.com/ja-jp/library/78zh94ax(VS.80).aspx

> 汎用テキスト ルーチンのマップ
> TCHAR.H のルーチン _UNICODE および _MBCS が定義 _UNICODE が定義
> _MBCS が未定義の場合 されている場合 されている場合
> _tcslen strlen strlen wcslen

> きっとApache 2は大丈夫でもPHP 5が駄目でファイル名は ANSI系になってしまうんですね。

先程のメールでも書きましたが,入出力をUTF-8に限ればそんなに難しくはないん
ですが,cp932も受け付けるとなると,なかなか大変ですね.

それとは別に,密かにfopenのrやwにもccsフラグ拡張がされていたりして面白いです.

---
<<UTF-8で書かれたファイルをC関数でアクセスするサンプル>>
/*
http://msdn.microsoft.com/ja-jp/library/tw4k6df8(VS.80).aspx
*/
#define UNICODE
#define _UNICODE
#include <stdio.h>
#include <iostream> // for wcout
#include <locale> // for locale
#include <tchar.h> // for TCHAR(=wchar_t)

void write()
{
/*
c:/tmp/a.txtにstrの文字列をUTF-8で書き込む
*/
const TCHAR *str = _T("このファイルはUTF-8で書かれています.txt");
const TCHAR *name = _T("c:/tmp/a.txt");
FILE *fp;
fp = _tfopen(name, _T("w,ccs=UTF-8"));
if (fp) {
fwrite(str, sizeof(TCHAR), _tcslen(str), fp);
fclose(fp);
}
}

void read()
{
/*
c:/tmp/a.txtをUTF-8として読み込み,その内容をファイル名とするファイルを作る
*/
TCHAR str[1024];
const TCHAR *name = _T("c:/tmp/a.txt");
FILE *fp;
fp = _tfopen(name, _T("r,ccs=UTF-8"));
if (fp) {
int n = fread(str, sizeof(TCHAR), sizeof(str) / sizeof(TCHAR) - 1, fp);
printf("n=%d¥n", n);
str[n] = _T('¥0');
fclose(fp);
/* このファイルはUTF-8で書かれています.txt を出力 */
std::wcout << _T('[') << str << _T(']') << std::endl;
}
/* strの中身をファイル名とするファイルを作る */
fp = _tfopen(str, _T("w"));
if (fp) {
fclose(fp);
}
}

int main()
{
std::locale::global(std::locale("")); /* use Japanese Characters */
write();
read();
}

---
>C:¥tmp>ls *.txt
>このファイルはutf-8で書かれています.txt

-----

一点だけ,先程のサンプルの最初にsetlocaleしていたのは,wcharの中身をwcoutでcp
932であるコマンドプロンプトに表示するためです.ファイルアクセスするだけなら不
要です.

今年のバイロイト音楽祭

まだBartokRadioのオンデマンドから取得可能です。

と言う話は置いといて…

ちなみに xyzzy は編集ファイルについては CreateFile でファイルを開いてます。それ以外のファイルは fopen やら _fsopen やら、状況にあわせていろいろだったり。
apache2 や php はソース追っかけてないから不明。

なんにせよ勉強になりました。

Re: 今年のバイロイト音楽祭

どうもありがとうございます。

# バイロイト聴きながらの仕事,うらやましいなぁ。

_wfopen(),OK

 Borlandでも(当然ですが)_wfopen()で仰るとおりにハート文字を含むファイルが作成できました.そうか,_wfopen()だったのですね.どこかで見た記憶があったものの”他人事”(ではなかった...)だと思って真面目にドキュメントを読まなかったバチがあたりましたね.しかし,Borlandのヘルプで,キーワード入力では_wfopen()が出てこない...統合環境のエディタ上で_wfopen()を選択し,F1キーを押して関数のヘルプを呼び出すと出てくるのですけれども.
 なお,ユーザが入力した文字列がUTF-16LEかシフトJISかを自動判別するのは難しいでしょうね.字形は違うが文字コードが同じ場合がありますので.

 PHP6での実装がどうなるのか.Windows様のためだけに何か関数やシステム変数を用意して頂けるのでしょうか^^; ”このファイル名はシフトJIS”(システムのロケールに従った文字コード),あるいは”このファイル名はUnicode(のUTF-16LE)”(OSのネイティブな文字コード)という情報をfopen()などの引数に追加するのか(避けたい),あるいはwfopen()のような妙な関数を追加するのか(すみません),あるいは何かmbstring系のデフォルトの文字コードを保持するシステム変数同様の変数を用意するのか(これかな?).

白井先生の論文改訂版

ここでの議論も踏まえて白井先生のfs_moodle論文の改訂版が出ました:
http://moodle.org/mod/forum/post.php?reply=452217

コメントの表示オプション

お好みの表示方法を選択し、「設定の保存」をクリックすると、表示方法を変更することができます。