Windowsのパスがうまく指定\表示できない問題 in Python

Mac/Linuxしか触ってなかった人生から一転、毎日Windowsを使うお仕事に就きました。
最近はちょっとしたツールをよく作るのですが、Windowsで1番悩まされてるのは、Windowsのパスが"\"で連結されていることです。
コマンドプロンプトとcygwinでどちらも動かすようなコードを書く場合です。


例えばこんな感じのコードを書くと

import os

path = "C:\documents\nori\tama"

for d in os.listdir(path):
    print os.path.join(path, d)

もちろんこうなるわけで。

WindowsError: [Error 123] ファイル名、ディレクトリ名、またはボリューム ラベルの
構文が間違っています。: 'C:\\documents\nori\tama/*.*'

「"\"ってエスケープされるじゃん!なんでWindowsのパスは\でつなぐの?! /でいいじゃん!」


仕方がないので、こんなかんじに\\でパスを設定しておくようにしていました。

path = "C:\documents\nori\tama"
path = "C:\\documents\\nori\\tama"


でも\を\\に変えるのもいい加減に面倒なので、まじめに解決方法を考えましょう。

解決法1 raw文字列を使う

path = r"C:\documents\nori\tama"

raw文字列("の前にrをつける)とエスケープシーケンスが無効になるので、\\を重ねずにすみます。
ただし末尾に'\'はこられないので注意です。

参考:
Python におけるバックスラッシュ (\) の扱いと、row stirng と正規表現における注意点 | すぐに忘れる脳みそのためのメモ

解決法2 そもそもWindowsのパスは/で繋げる

path = "C:/documents/nori/tama"

別に \ じゃなくて / でも大丈夫なのです。


そもそもWindowsのパスはなぜバックスラッシュ?

なんで "\"なのか調べてみました。
本の虫: なぜDOSのパスはバックスラッシュなのか

簡単に言うと、

  • /が既にオプション指定文字だったから
  • 某社の陰謀

というような感じっぽい。

とりあえず "/"でつないでも大丈夫なことがわかったところで次の問題。

/ と \(¥)が混ざって表示される問題

さっきの raw文字列や / を使うことで、パスを指定することには成功したわけですが、次の問題です。

import os
path = "C:/documents/nori/tama"
for d in os.listdir(path):
    print os.path.join(path, d)

これを実行するとコマンドプロンプトで実行すると、こんな感じになります。

C:/documents/nori/tama\dir1
C:/documents/nori/tama\dir2
C:/documents/nori/tama\windowspath.py

/ と \ が混ざってて気持ち悪い! \(^◇^)/
動くけど!動くけど・・・。

raw文字列を使うと今度はシェルで実行した時に混ざるのです。

解決法3 os.sepでreplace

いろいろためしてみた結果、これがos.sepをでreplaceするのが1番よい気がしました。

os.sep

パス名を要素に分割するためにオペレーティングシステムで利用されている文字です。例えば POSIX では '/' で、Windowsでは '\\' です。

参考: 15.1. os — 雑多なオペレーティングシステムインタフェース — Python 2.7.14 ドキュメント

/で指定をしておいて、os.sepでreplaceします。

import os
path = "C:/documents/nori/tama".replace('/', os.sep)
for d in os.listdir(path):
    print os.path.join(path, d)

これで、コマンドプロンプトで実行すれば

C:\documents\nori\tama\dir1
C:\documents\nori\tama\dir2
C:\documents\nori\tama\windowspath.py

cygwin等で実行すれば

C:/documents/nori/tama/dir1
C:/documents/nori/tama/dir2
C:/documents/nori/tama/windowspath.py


どっちで実行しても良い感じになりました。やったー!

さいごに

他にもっとよい方法があれば教えて下さい!

Windowsのパスと表現するかdosのパスと表現するか迷ったのだけど、Windowsのパスとかいてしまった。 dosの、といったほうが正確な気がしてる。よくわからない。