彷徨うITエンジニアの雑記

ITインフラ関連の雑記とか

NTFSで使用可能なファイル名の文字について

2020/3/26 一部UnicodeのコードポイントとUTF-16エンコードが混在していたので修正しました。

WindowsNTFSは色々な文字をファイル名に使用することができますが、実際どこまで可能なのか試してみました。
とりあえず結果は以下の通りです。

f:id:wandering_engineer:20200326001647j:plain


試した文字の種類は以下の通りです。


途中のUnicodeに関する説明はちょっと自信無いです。Unicode難しい。。。
理解し易いように、文字情報を取得するPowershellの関数を作成しました。

Function Get-CharInfo {
    [string]$char=$Args[0]

    # 文字数を取得
    $char_length=($char | Measure-Object -Character).Characters

    # UFT-16に変換してバイト列を取得
    $char_byte_utf16=[System.Text.Encoding]::BigEndianUnicode.GetBytes($char)
    # UFT-16のバイト列を16進数表示
    $char_hex_utf16=[System.BitConverter]::ToString($char_byte_utf16)
    # UTF-16のバイト数を取得
    $char_byte_count_utf16=$char_byte_utf16.Count

    # UTF8に変換してバイト数を取得
    $char_byte_count_utf8=[System.Text.Encoding]::UTF8.GetByteCount($char)
    # CP932≒S-JISに変換してバイト数を取得
    $char_byte_count_cp932=[System.Text.Encoding]::GetEncoding(932).GetByteCount($char)

    $hash_table=[ordered]@{
        "Character" = $char;
        "Character Length" = $char_length;
        "UTF-16 Bytes(Hex)" = $char_hex_utf16;
        "UTF-16 Byte Count" = $char_byte_count_utf16;
        "UTF8 Byte Count" = $char_byte_count_utf8;
        "CP932 Byte Count" = $char_byte_count_cp932
    }

    return $hash_table

}


シングルバイト文字、2バイト文字

割愛します。

サロゲートペア文字

サロゲートペア文字はUnicodeBMP(Basic Multilingual Plane)に含まれない文字で、4Byte = 16bit(上位サロゲート) + 16bit(下位サロゲート)で構成されます。
Windowsの内部コードはUnicodeのため、文字数的には2文字扱いとなります。
例えば、𠮷(U+20BB7 => 0xD842 + 0xDFB7)、𩸽(U+29E3D => 0xD867 + 0xDE3D)などが該当します。
なお、非常用漢字・旧漢字とUnicode BMPの間に関係性はありません。

PS E:\work> # サロゲートペア文字(ほっけ)
PS E:\work> $char_surrogate='??'
PS E:\work> Get-CharInfo $char_surrogate

Name                           Value
----                           -----
Character                      𩸽
Character Length               2
UTF-16 Bytes(Hex)              D8-67-DE-3D
UTF-16 Byte Count              4
UTF8 Byte Count                4
CP932 Byte Count               2

* Powershellのコンソールはサロゲート非対応なので?に文字化けします。なお、スクリプトから実行する場合はファイルをUTF-8 with BOMで保存する必要があります。

絵文字

大抵の絵文字はUnicodeBMP、またはサロゲートペア文字に含まれます。
ただし、Unicode12.0で追加された絵文字には22バイト=11文字に達するものも存在します。
これはEmoji ZWJ sequenceとして定義されるもので、200Dで複数の絵文字を連結して1個の文字として表現する、らしいです。*1
男性+握手+女性=家族、とか、面白いです。

PS E:\work> # 絵文字(笑顔)
PS E:\work> $char_emoji_smile='??'
PS E:\work> Get-CharInfo $char_emoji_smile

Name                           Value
----                           -----
Character                      😁
Character Length               2
UTF-16 Bytes(Hex)              D8-3D-DE-01
UTF-16 Byte Count              4
UTF8 Byte Count                4
CP932 Byte Count               2


PS E:\work> # 絵文字(家族)
PS E:\work> $char_emoji_family='????????'
PS E:\work> Get-CharInfo $char_emoji_family

Name                           Value
----                           -----
Character                      🧑‍🤝‍🧑
Character Length               8
UTF-16 Bytes(Hex)              D8-3E-DD-D1-20-0D-D8-3E-DD-1D-20-0D-D8-3E-DD-D1
UTF-16 Byte Count              16
UTF8 Byte Count                18
CP932 Byte Count               8


異体字セレクタ(IVS)

異体字は同じ文字でも字形の異なる文字で、基底文字 + 異体字セレクタ(IVS) で表現され、該当の字形がフォントに含まれていれば表示可能です。
IVS自体がサロゲートペアのため、3文字扱いとなります。
例えば、飴(U+98F4)と飴󠄀(U+98F4,U+E0100)などが該当します。
* U+E0100はUnicodeのコードポイントで、UTF-16エンコードは0xDB40(固定値) + 0xDD00(Index) です。

PS E:\work> # 飴の基底文字
PS E:\work> $char_base='飴'
PS E:\work> Get-CharInfo $char_base

Name                           Value
----                           -----
Character                      飴
Character Length               1
UTF-16 Bytes(Hex)              98-F4
UTF-16 Byte Count              2
UTF8 Byte Count                3
CP932 Byte Count               2


PS E:\work> # 飴の異体字
PS E:\work> $char_variant='飴??'
PS E:\work> Get-CharInfo $char_variant

Name                           Value
----                           -----
Character                      飴󠄀
Character Length               3
UTF-16 Bytes(Hex)              98-F4-DB-40-DD-00
UTF-16 Byte Count              6
UTF8 Byte Count                7
CP932 Byte Count               4


合成済み文字、結合文字

合成済み文字と結合文字はUnicodeの正規化の違いです。
Unicodeでは複数の文字を結合して1個の文字を表現することが出来ます。
例えば、"ポ"という文字にはU+30DDが割り当てられていますが、ポ = ホ(U+30DB) + °(U+309A)で表現することも可能です。
前者を合成済み文字、NFC正規化と呼び、WindowsLinux、最近のMacOSで使用されます。
後者を結合文字、NFD正規化と呼び、昔のMacOSで使用されていたようです。結合文字は2文字扱いです。

PS E:\work> # 合成済み文字
PS E:\work> $char_nfc='ポ'
PS E:\work> Get-CharInfo $char_nfc

Name                           Value
----                           -----
Character                      ポ
Character Length               1
UTF-16 Bytes(Hex)              30-DD
UTF-16 Byte Count              2
UTF8 Byte Count                3
CP932 Byte Count               2


PS E:\work> # 結合文字
PS E:\work> $char_nfd=[System.Activator]::CreateInstance([System.String], [char[]]@(0x30db, 0x309a))
PS E:\work> Get-CharInfo $char_nfd

Name                           Value
----                           -----
Character                      ポ
Character Length               2
UTF-16 Bytes(Hex)              30-DB-30-9A
UTF-16 Byte Count              4
UTF8 Byte Count                6
CP932 Byte Count               3



以上です。