Delphi 10.1 BerlinでファイルシステムにあわせてUnicode正規化する

ユニコードには結合文字というものが存在します。

たとえば「が」という文字の表現方法には、一文字で「が」と表現する方法と、「か」+「゛」のように濁点を結合する方法があります。
日本語以外でも結合文字は使われています。たとえばアクセント記号など。

結合文字を使った文字と結合文字を使わない文字が混在していると、検索にヒットしない(「が」と「か」+「゛」は別の文字)など、トラブルの元になりがち。
そこで一方に統一したい。

統一することを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では濁点は結合されました。

win

OSXでは濁点は分割されました。

mac

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です