scramble cadenza

技術ネタのガラクタ置き場

ファイル名で使えない文字

イントロ

web アプリケーションで、ユーザーにファイルをダウンロードさせる機能を追加したいとする。
例えばダウンロードさせるファイルの名前をアプリ側で自動決定する場合、その名前にはどのような制限があるのだろうか。

何回か調べてる気がするので、まとめてみる。

前提

そもそも、ファイル名の制限は、OS ではなく、基本的にはファイルシステムで決まる。

ファイルシステム上の限界

ファイルシステムの限界はどこまでなのかというと Comparison of file systems - Wikipedia, the free encyclopedia にまとめられている。

古いファイルシステムでは、ファイル名は 8 文字、拡張子が 3 文字、ドットも含めて合計 12 文字までしかダメ、という時代もあったらしい。8.3形式 と呼ばれているのがそれだ。

有名どころのファイルシステム(NTFS, FAT系, ext4, xfs)では

  • ファイル名の長さは 255 バイト or 文字まで
  • NUL だけがファイル名として使えない(一部のファイルシステムでは / も禁止されている)

という仕様が多いようだ。

Windows で禁止されている予約語

ただし Windows は例外がある。
ファイルシステムだけでなく、OS レベルで予約されている文字があり、これらはファイル名として使えない。

Naming Files, Paths, and Namespaces (Windows) によると、以下の文字が予約されている。

< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)

手元に win 機が無かったので、残念ながら検証はしていない。
本当にダメなのか試してみたい。

結論

対応するファイルシステムのカバー範囲により、ファイル名に使える文字は異なる。
だが、現実的な落とし所として

  • windows を視野にいれるのであれば、windows で禁止されている予約語は使わないほうが良いと思う。
  • NULL文字も使えないファイルシステムが多いため、止めたほうが無難。
  • 長過ぎるファイル名は NG なので、そのあたりも調整する。
    • マルチバイト文字のときが面倒くさいが、より小さい方に合わせ 255 byte まで のほうががいいのかな。

ぐらいを考えておけば良さそうだろうか。

おまけ

手元の MacファイルシステムHFS+ だった。

f:id:mgi:20161010132555p:plain

https://en.wikipedia.org/wiki/Comparison_of_file_systems によると、制限は以下の通り

File system Maximum filename length Allowable characters in directory entries Maximum pathname length Maximum file size Maximum volume size
HFS Plus 255 UTF-16 characters Any valid Unicode Unlimited slightly less than 8 EiB slightly less than 8 EiB
# 制御文字のファイル名は作れる
`touch "\a"`
=> ""

# null 文字はダメだった
`touch "\x0"`
#  ArgumentError: string contains null byte
#        from (irb):1:in ``'
#        from (irb):1
#        from /Users/argerich/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'

# 256 byte は長すぎる
`touch #{"a" * 256}`
touch: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: File name too long

# 255 byte なら OK
`touch #{"a" * 255}`
=> ""

# 日本語で 255 文字はアウト
`touch #{"" * 255}`
touch: あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ: File name too long

>> "".bytesize
=> 3

# 3 * 85 = 255 なので、85 文字まで OK
>> `touch #{"" * 85}`
=> ""

# 86 文字目からアウト
>> `touch #{"" * 85 + "a"}`
touch: あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああa: File name too long

# UTF-16 が OK ならば、絵文字でもいけるはず。
`touch #{"\u{26C4}" * 85}`
=> ""

f:id:mgi:20161010132646p:plain

絵文字ファイル名で作れているっぽい。ls すると ? になるけど。

f:id:mgi:20161010132710p:plain

それにしてもWikipedia 先生。ファイル名の長さ、255 byte までなのでは?
Any valid Unicode の中に null 文字は入ってないのかな?

一部の文字は互換性維持のため、OS の API で制御しているとの噂もあるけど… うーむ。
情報が少なすぎて、これ以上調べるのは難しそうだった。一次情報どこにあるの。

試してみたものの、Wikipedia を引用してまとめたので、すごい微妙になってきた…
この記事は用法用量を守って、正しくお使いください。

参考