Macで実行ファイルのフォルダの取得方法

解決


NAGSYS  2013-11-07 23:57:30  No: 45523  IP: [192.*.*.*]

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にしても同じでした。

文字化けを防ぐ方法はあるのでしょうか?

編集    削除
Harry  2013-11-08 04:01:14  No: 45524  IP: [192.*.*.*]

ちょっと調べてみたら、正規化された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オンリー)のようにも見えます。

編集    削除
NAGSYS  2013-11-09 02:42:18  No: 45525  IP: [192.*.*.*]

UTF-8-MACだったんですね。
これは解決できない問題なんでしょうか。

MECSUtilsは古いバージョンのDelphiをUnicode対応にするためのユーティリティで、UTF-8-MACとは関係ないような気がします。


>推測ですが、Mac内で使用するなら一切変換が起こらないようにしてそのまま扱えば良いのではないでしょうか?

そのまま扱えればいいのですが、FireMonkeyでファイルを開いたりコピーしたり削除したりするためには、String型でなければならないようです。

どうしてもだめならフォルダ名に全角を使わないよう注意するしかないですかね。

編集    削除
Harry  2013-11-09 07:14:51  No: 45526  IP: [192.*.*.*]

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

編集    削除
DEKO  2013-11-09 16:35:01  No: 45527  IP: [192.*.*.*]

ParamStr() がアフォな事をやっているので、
以下の ParamStr_OSX() を使ってみてください。

function ParamStr_OSX(Index: Integer): UTF8string;
type
  PAnsiCharArray = array[0..0] of PAnsiChar;
begin
  result := UTF8String(PAnsiCharArray(ArgValues^)[Index]);
end;

編集    削除
DEKO  2013-11-09 16:40:12  No: 45528  IP: [192.*.*.*]

或いはこっちで。

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;

編集    削除
DEKO  2013-11-09 16:57:26  No: 45529  IP: [192.*.*.*]

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

編集    削除
DEKO  2013-11-09 17:06:27  No: 45530  IP: [192.*.*.*]

現行の ParamStr() で取得した文字列からは、どうやっても正しい文字列を取得できませんし、
正しい文字列へ変換する事もできないようです (誤変換されてしまっているので何を噛ませても駄目)。

QC の Workaround は、ANSI に誤変換されたものを UTF-8 へ変換しているだけなので、
ANSI に存在しない文字は変換されず、文字化けを引き起こしてしまいます。

...なので、大元の ParamStr() を修正するか、上記関数を使うしかないでしょう。

編集    削除
Yoshi  2013-11-10 01:58:29  No: 45531  IP: [192.*.*.*]

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;

編集    削除
NAGSYS  2013-11-13 23:44:15  No: 45532  IP: [192.*.*.*]

Harryさん
ありがとうございます。

Param(0)じゃなくてParamStr(0)でした。
MECSUtilsで、UTF-8-MAC(NFD)→NFC の変換ができるんですね。
でもWindows依存だとMacでは使えないですね。

☆RAD Studio XE4(Delphi) の FireMonkey でファイル名を変更する方法(Windows, Mac 両対応)/ファイルをコピーする方法(Windows, Mac 両対応)
のサイトは見ましたが、全角文字があるとダメなようです。


DEKOさん、Yoshiさん
教えていただいた方法で、正しく取得することができました。

ありがとうございました。

編集    削除
DEKO  2013-11-15 07:32:20  No: 45533  IP: [192.*.*.*]

今回の件のちょっとした補足記事を以下に書いておきました。

[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 には
# 実装されていますが...完成度が低いので未だ非公開です。

編集    削除