パスワードを忘れた? アカウント作成
181604 journal

tuneoの日記: Pythonのglobモジュールでドはまり 4

日記 by tuneo

UNIX風なワイルドカード展開を使おうと思ってドはまり。
メタ文字をエスケープできないじゃん!

カレントディレクトリに"[ほげほげ]てすと"というサブディレクトリを掘って、そこに置いたファイルa.txt, b.txt, ...の一覧をglob.glob()でゲットだぜ!ということをやりたいのだが。

>> from glob import glob
>> glob("[ほげほげ]てすと/*")
[]

glob.glob()では[]はワイルドカードになるから期待通りの結果が出ないのは当然だな。
……ということは、バックスラッシュでエスケープすればいいのかな?

>> glob(r"\[ほげほげ\]てすと/*")
[]

……ダメだ。お手上げorz。

os.popen()で/bin/lsシェルを呼ぶのは、なるべくならやりたくない。Windowsで動かなくなるしな。
Pythonで巧いこと済ませる方法はあるだろうか?

追記:ソースを追っかけたりしたんだが、よくよくfnmatch.pyを読んでみたらコメントに

There is no way to quote meta-characters.

と書いてあった。メタ文字をクォートする方法は無いのかorz。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Anonymous Coward on 2010年01月07日 23時14分 (#1699561)
    os.walkが便利です。
  • by okky (2487) on 2010年01月07日 23時52分 (#1699569) ホームページ 日記

    "\\\[ほげほげ\\\]"
    ではいかがでしょう?

    " と " で囲まれている範囲のエスケープで

    "\\\[ほげほげ\\\]"

    \[ほげほげ\]

    になります。これを glob パーサーが解釈して

    [ほげほげ]

    になる。

    --
    fjの教祖様
    • Pythonの文字列定数の処理においては
      ・"\\"→"\"
      ・"\["→"\["
      ですから、"\\\["は"\\["にしかなりません。というわけで、うまくいかんです。
      # そういうバックスラッシュ置換を抑止するために「r"..."」って書いてあるんですけどね。

      globモジュールからソースを追っかけてみたところ

      glob.glob()
        fnmatch.fnmatch()
          fnmatch.fnmatchcase()
            fnmatch.translate()
            re.match()

      という流れになってました。fnmatch.translate()でパターンを正規表現に変換して、re.match()を使ってマッチさせているというカラクリです。さて、今回使ったパターンが実際にどんな正規表現に変換されているのかというと。

      >> import fnmatch
      >> r = fnmatch.translate(ur"[ほげほげ]てすと/*")
      >> print r
      [ほげほげ]\て\す\と\/.*$
      >> r = fnmatch.translate(ur"\[ほげほげ\]てすと/*")
      >> print r
      \\[ほげほげ\\]\て\す\と\/.*$

      ……なんか色々違うorz。"\["と"\]"の処理もさることながら、[]の外側のひらがな一つ一つをバックスラッシュでエスケープしてるあたりもかなり胡散臭い(問題は無いんだけど、そうする必要もない)。当然、マッチさせてみてもスカです。

      >> import re
      >> print re.match(r, u"[ほげほげ]てすと/test.txt").group(0)
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'NoneType' object has no attribute 'group'

      正しくマッチする正規表現はr"\[ほげほげ\]てすと/.*.txt$"です。

      >> r = ur"\[ほげほげ\]てすと/.*.txt$"
      >> print re.match(r, u"[ほげほげ]てすと/test.txt").group(0)
      [ほげほげ]てすと/test.txt

      ちなみにokkyさんご提案のパターンだとこうなります。

      >> print translate(u"\\\[ほげほげ\\\]てすと/*.txt")
      \\\\[ほげほげ\\\\]\て\す\と\/.*\.txt$

      結局のところ正しい正規表現に変換できないfnmatch.translate()が原因ですね。

      しかし、このモジュールって昔っから(10年以上前から)あるんだけど、誰も気づかなかったのか……。

typodupeerror

長期的な見通しやビジョンはあえて持たないようにしてる -- Linus Torvalds

読み込み中...