短い名前から長い名前へ変換

 もう15年以上昔の話です。今は32ビットや64ビットのWindowsですが、その頃は16ビットのパソコンでした。16ビットのパソコンは、無駄に長いファイル名を使用することができないため、ファイル名8文字、拡張子3文字の合わせて11文字のファイル名しか使えませんでした。

 その名残でファイル/フォルダ名には、長い名前と短い名前があります。今はそんなに使うことはありませんが、時々古いアプリケーションを使っているとそんなことに気をつけなければ、いけないことが出てきます。ものは試し、ちょっと体験してみましょう。

 まだ短い名前は存在しています。

 まずWindowsのスタートから→すべてのプログラム→アクセサリに進み、コマンドプロンプトを立ち上げてください。恐らくプロンプトには次のように表示されていると思います。 
C:\Documents and Settings\ユーザー名>
 これは、Cドライブのルート(root)ディレクトリに"Documents and Settings"というディレクトリがあり、更にユーザー名のディレクトリがあることを表しています。
 では、"cd ../.."と入力してみてください。
Cドライブのルートディレクトリに移動しましたね。
 次に"cd docume~1"と入力してみてください。

 当然"docume~1"ディレクトリに移動したわけですが、実はこのディレクトリは"Documents and Settings"の事なんです。本当は名前を全部か書きたいのですが、ファイル名8文字以内という基準に合わせるため、最初の6文字+~数字で表しています。
 ファイル名の最初の6文字が同じものが出来るとそのファイルの短いファイル名は最初の6文字まで同じで数字が増えていきます。
 こんな風にファイル/フォルダには短い名前と長い名前が存在します。それでは、本題の短い名前から長い名前へ変換を考えていきましょう。

 .NET Frameworkの動作を確認してみる

 今回は、.NET FrameworkのSystem.IOの中にあるDirectoryInfoとPathクラスを使います。インタラクティブシェルを使って動作を確認しながら作ってみましょう。
まず使うクラスをインポートします。

 >>> import System
 >>> from System.IO import DirectoryInfo
 >>> from System.IO import Path

また今回テストしてみる短いパス名を変数pathとして指定し、これからまずrootディレクトリを取得してみましょう。

 >>> path = "C:\DOCUME~1\ALLUSE~1\DOCUME~1\MYPICT~1"
 >>> path
 'C:\\DOCUME~1\\ALLUSE~1\\DOCUME~1\\MYPICT~1'
 >>> Path.DirectorySeparatorChar
 <System.Char object at 0x000000000000002B [\]>
 >>> Path.DirectorySeparatorChar.ToString()
 '\\'
 >>> root = Path.GetPathRoot(path)
 >>> root.ToString()
 'C:\\'

 指定した変数pathとは若干中身が異なります。これはエスケープコードの影響です。
ディレクトリを分ける文字は'\'の一文字ですが.NETのToString関数を使うと漏れなくエスケープコードが付いてきます。
 さて話は逸れましたが、ルートディレクトリはPathクラスのGetPathRoot()関数を使って取得します。次にサブフォルダの情報をフォルダ毎に分けて入手します。

 >>> folders = path[root.Length:].split(Path.DirectorySeparatorChar)
 >>> folders
 ['DOCUME~1', 'ALLUSE~1', 'DOCUME~1', 'MYPICT~1']
 
 >>> folders = path.Substring(root.Length).Split(Path.DirectorySeparatorChar)
 >>> folders
 Array[str](('DOCUME~1', 'ALLUSE~1', 'DOCUME~1', 'MYPICT~1'))

前者はPythonの関数を使った場合、後者は.NET Frameworkのオブジェクトっぽく書いた場合です。そう大差ありませんので、好みの問題かと思います。
これで十分な情報が得られそうです。サブディレクトリを検索しながら長いパス名を作ってみましょう。

 >>> result = root
 >>> for name in folders:
...   di = DirectoryInfo(result) ←ディレクトリ情報を扱うクラス
...   fsi = di.GetFileSystemInfos(name) ←短い名前で検索
...   if(fsi.Length == 1): ←検索した結果、1つだけフォルダを見つけたら
...     result = fsi[0].FullName ←それを次の検索先フォルダにする。
...   else:
...     print "Error:"+name
...
 >>> res
 'C:\\Documents and Settings\\All Users\\Documents\\My Pictures'

まとめてスクリプトにすると次のようになります。

import System
from System.IO import DirectoryInfo
from System.IO import Path

def GetLongPath(path):
  root = Path.GetPathRoot(path)
  folders = path.Substring(root.Length).Split(Path.DirectorySeparatorChar)

  result = root
  for name in folders:
    di = DirectoryInfo(result)
    fsi = di.GetFileSystemInfos(name)
    if(fsi.Length == 1):
      result = fsi[0].FullName
    else:
      print "Error:"+ name
  return result

short = "C:\DOCUME~1\ALLUSE~1\DOCUME~1"
long = GetLongPath(short)

print "短いパス:"+ short,
print "を"
print "長いパス:"+ long
print "に変換しました。"

raw_input()

出力画面:
 短いパス:C:\DOCUME~1\ALLUSE~1\DOCUME~1 を
 長いパス:C:\Documents and Settings\All Users\Documents
 に変換しました。