XE4 FireMonkeyでMacのプログラムを作成しています。
実行ファイルと同じ場所にテキストファイルを置いていて、それを読み書きするために実行ファイルの場所のフォルダ名を取得しようとしています。
ExePath := Utf8ToString(Param(0));
で取得できることはわかったのですが、フォルダ名の全角文字があると文字化けする場合があります。
いま分かっているのは、デスクトップに新規にフォルダを作って、
/User/Test/Desktop/名称未設定フォルダ/Test.app/Contents/MacOS/Test
というフォルダ構成にし、これをUtf8ToString(Param(0))で変換すると、
/User/Test/Desktop/名称未設定フォルタ◆?/Test.app/Contents/MacOS/Test
となってしまいます。(◆は中に白文字で?があります)
「名称未設定フォルダ」を「名称未設定フォルダx」に変えると正常に変換できるのですが、「名称未設定フォルダ1」にするとダメでした。
Utf8ToStringをUtf8ToUncodeStringやUtf8ToWideStringにしても同じでした。
文字化けを防ぐ方法はあるのでしょうか?
ちょっと調べてみたら、正規化されたUnicode文字列でUTF-8-MACというものらしいですね。
http://ja.wikipedia.org/wiki/UTF-8#Unicode.E6.AD.A3.E8.A6.8F.E5.8C.96.E3.81.A8.E3.81.AE.E9.96.A2.E4.BF.82
推測ですが、Mac内で使用するなら一切変換が起こらないようにしてそのまま扱えば良いのではないでしょうか?
表示したり、Windowsに渡す場合はMECSUtilsでなんとかなるのかな…?
でも、リファレンスを見ると、MECSUtils自体がプラットフォーム依存(Windowsオンリー)のようにも見えます。
UTF-8-MACだったんですね。
これは解決できない問題なんでしょうか。
MECSUtilsは古いバージョンのDelphiをUnicode対応にするためのユーティリティで、UTF-8-MACとは関係ないような気がします。
>推測ですが、Mac内で使用するなら一切変換が起こらないようにしてそのまま扱えば良いのではないでしょうか?
そのまま扱えればいいのですが、FireMonkeyでファイルを開いたりコピーしたり削除したりするためには、String型でなければならないようです。
どうしてもだめならフォルダ名に全角を使わないよう注意するしかないですかね。
MECSUtilsは様々な文字列操作が行なえる関数群ですので、古いバージョンのDelphiを…というわけでもないですよ。
(Delphiで不十分な)サロゲートペアに対応した関数や、Unicodeの「正規化」とか、(わずかですが)HTML関連もありますし。
☆MECSUtils リファレンス
http://ht-deko.minim.ne.jp/tech021.html
今回の例で言えば、UTF-8-MAC(NFD)→NFC に変換するMecsNormalizeとか、正規化の種類を判定するMecsIsNormalizedですね。
でもWindows依存で、Macでは使えなさそう…? さらに言えば、UTF-8-MACをNFCに変換して、Macのファイルシステムが受け付けるのか…?
そういえばParam(0)じゃなくてParamStr(0)ですよね。そのまま扱う…の例がないかな〜と思って、ParamStr(0)で検索してみたらありました。
☆RAD Studio XE4(Delphi) の FireMonkey でファイル名を変更する方法(Windows, Mac 両対応)/ファイルをコピーする方法(Windows, Mac 両対応)
http://dhive.jp/blog/yama/category/delphi
>RenameFile(ExtractFilePath(ParamStr(0)) + 'test.txt', ExtractFilePath(ParamStr(0)) + 'rename.txt');
>TFile.Copy(ExtractFilePath(ParamStr(0)) + 'test.txt', ExtractFilePath(ParamStr(0)) + 'test by Copy.txt');
これら↑のように、特に何事も無く取り扱っちゃダメなんですか?
>FireMonkeyでファイルを開いたりコピーしたり削除したりするためには、String型でなければならないようです
思ったんですが、ParamStr(0)で返される値は元々Stringではないのですか。 だとしたら、Utf8ToStringで変換してなぜ大丈夫なんだろう…?
TPathなんてのもあるんですね。ParamStr(0)に対応するメソッドは無いみたいですが。
☆System.IOUtils.TPath
http://docwiki.embarcadero.com/Libraries/XE5/ja/System.IOUtils.TPath
ParamStr() がアフォな事をやっているので、
以下の ParamStr_OSX() を使ってみてください。
function ParamStr_OSX(Index: Integer): UTF8string;
type
PAnsiCharArray = array[0..0] of PAnsiChar;
begin
result := UTF8String(PAnsiCharArray(ArgValues^)[Index]);
end;
或いはこっちで。
function ParamStr_OSX(Index: Integer): string;
type
PAnsiCharArray = array [0..0] of PAnsiChar;
begin
if Index < ArgCount then
Result := string(UTF8String(PAnsiCharArray(ArgValues^)[Index]))
else
Result := '';
end;
QC#119758 のようですね。
Report No: 119758 Status: Open
[MacOS] ParamStr(0) returns wrong string on MacOS (UTF-8 problem)
http://qc.embarcadero.com/wc/qcmain.aspx?d=119758
QCWIN:Defect_No=119758
現行の ParamStr() で取得した文字列からは、どうやっても正しい文字列を取得できませんし、
正しい文字列へ変換する事もできないようです (誤変換されてしまっているので何を噛ませても駄目)。
QC の Workaround は、ANSI に誤変換されたものを UTF-8 へ変換しているだけなので、
ANSI に存在しない文字は変換されず、文字化けを引き起こしてしまいます。
...なので、大元の ParamStr() を修正するか、上記関数を使うしかないでしょう。
Cocoaを使って取得する方法です。
XE4で動作確認していますが、他バージョンで動くかはわかりません。
uses
Macapi.Foundation, Macapi.ObjectiveC;
function GetAppPath: string;
var
AppPathKey: Pointer;
AppBundle: NSBundle;
NSAppPath: NSString;
AutoReleasePool: NSAutoreleasePool;
begin
AutoReleasePool := TNSAutoreleasePool.Create;
try
AppPathKey := (NSSTR('CFBundleExecutablePath') as ILocalObject).GetObjectID;
AppBundle := TNSBundle.Wrap(TNSBundle.OCClass.mainBundle);
NSAppPath := TNSString.Wrap(AppBundle.infoDictionary.objectForKey(AppPathKey));
Result := UTF8ToString(NSAppPath.UTF8String);
finally
AutoReleasePool.release;
end;
end;
Harryさん
ありがとうございます。
Param(0)じゃなくてParamStr(0)でした。
MECSUtilsで、UTF-8-MAC(NFD)→NFC の変換ができるんですね。
でもWindows依存だとMacでは使えないですね。
☆RAD Studio XE4(Delphi) の FireMonkey でファイル名を変更する方法(Windows, Mac 両対応)/ファイルをコピーする方法(Windows, Mac 両対応)
のサイトは見ましたが、全角文字があるとダメなようです。
DEKOさん、Yoshiさん
教えていただいた方法で、正しく取得することができました。
ありがとうございました。
今回の件のちょっとした補足記事を以下に書いておきました。
[ParamStr() がおかしい (Delphi Forum)]
http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1305
> でもWindows依存だとMacでは使えないですね。
OS X で正規化したいのであれば、
NSString.precomposedStringWithCanonicalMapping (NFC)
NSString.decomposedStringWithCanonicalMapping(NFD)
NSString.precomposedStringWithCompatibilityMapping(NFKC)
NSString.decomposedStringWithCompatibilityMapping(NFKD)
を使えば可能です。
# 作りかけで放置中の MECsUtils 2.00 beta には
# 実装されていますが...完成度が低いので未だ非公開です。