ユニコードには結合文字というものが存在します。
たとえば「が」という文字の表現方法には、一文字で「が」と表現する方法と、「か」+「゛」のように濁点を結合する方法があります。
日本語以外でも結合文字は使われています。たとえばアクセント記号など。
結合文字を使った文字と結合文字を使わない文字が混在していると、検索にヒットしない(「が」と「か」+「゛」は別の文字)など、トラブルの元になりがち。
そこで一方に統一したい。
統一することをUnicode正規化といい、正規化には4種類の形式があります。
参考: Unicode正規化 – Wikipedia
- NFD(正規化形式D)
- NFC(正規化形式C)
- NFKD(正規化形式KD)
- NFKC(正規化形式KC)
ファイル名には、WindowsではNFCで正規化されます。
OSXではNFDをベースにした独自実装が用いられているようです。
正規化の方法が異なるため、WindowsとOSXでファイルをやりとりしたときに、意図せぬトラブルに遭遇するわけです。
Windowsでは、たとえばMECSUtils.MecsNormalize関数を使えば、NFCで正規化できます。
{$IFDEF MSWINDOWS}
uses MECSUtils;
{$ENDIF MSWINDOWS}
{$IFDEF MSWINDOWS}
function FileSystemStringForWin(const S: string): string;
var
W: WideString;
begin
if MECSUtils.MecsNormalize(S, W, NormalizationC) then
Result := W
else
Result := S;
end;
{$ENDIF MSWINDOWS}
問題はOSXですが、CFStringGetFileSystemRepresentation関数を使えば、ファイルシステムと同じ変換ができるようです。
Delphi 10.1 Berlinには、System.SysUtilsユニットにStringToFileSystemString関数があります。
このStringToFileSystemString関数がCFStringGetFileSystemRepresentation関数を使った変換処理をしてくれます。
{$IFDEF MACOS}
function FileSystemStringForMac(const S: string): string;
const
MAX_PATH = 1024;
var
Bytes: TBytes;
I: Integer;
begin
SetLength(Bytes, MAX_PATH);
if System.SysUtils.StringToFileSystemString(S, Bytes) then
begin
for I := 0 to Length(Bytes) - 1 do
if Bytes[I] = 0 then
Break;
Result := TEncoding.UTF8.GetString(Bytes, 0, I);
end;
end;
{$ENDIF MACOS}
OSの正規化の方法に合わせて文字列を変換するサンプルです。
function FileSystemString(const S: string): string;
begin
{$IFDEF MACOS}
Result := FileSystemStringForMac(S);
{$ENDIF MACOS}
{$IFDEF MSWINDOWS}
Result := FileSystemStringForWin(S);
{$ENDIF MSWINDOWS}
end;
procedure TForm1.FormCreate(Sender: TObject);
const
S1 = #$304C; // が'
S2 = #$304B + #$3099; // か゛
var
C: Char;
begin
for C in FileSystemString(S1) do
Memo1.Lines.Add(C);
for C in FileSystemString(S2) do
Memo1.Lines.Add(C);
end;
Windowsでは濁点は結合されました。
OSXでは濁点は分割されました。