« 正規表現の先読みと後読み | トップページ | こんな風になってしまった »

2023年7月 1日 (土)

tkinterdnd2で、ドラッグ&ドロップしたファイルのフルパスの取得

梅雨らしく、雨。

でも、降ったり止んだりで、ザーザーと降り続く、という感じではなかった。

 

Pythonのtkinterdnd2で、ドラッグ&ドロップしたファイルのフルパスの取得で、ちょっと苦労したので、それについて。

ネットで検索したのだけれど、皆さん、あまりこういう使い方をしていない?
それとも、困っていない?

自分はかなり困ったんだけれど。(苦笑)

tkinterdnd2で、ドラッグ&ドロップしたファイルのフルパスを取得しようとすると、フォルダ名やファイル名にスペースがある時とない時とで、データの扱い方が違う。

サンプルで書くと、こういう感じになる。

c:\aa\bb.txt {d:\ee e\f f.txt} {d:\gg\gg\g g.txt} c:\h\i\jj.txt c:\h\i\jj.txt {d:\kk\k kk\k.txt}

何もスペースがないと、スペース区切り。
フォルダ名やファイル名にスペースが入っていると、「{}」で囲われたスペース区切りになる。

こういう状態から、フルパスを取得しようとすると、どうすればいいのだ?!?!となってしまった。

正規表現を駆使してやろうとするが、なかなかうまくいかない。

もう一息!というところまではいくんだけれど、イレギュラー処理、つまり、一般的にはあり得ないファイル名に対応しようとすると、対処しきれなかった。
例えば、ファイル名に「.」が何個もある(拡張子が複数あるような感じ)とか、拡張子の中にスペースがあるとか、拡張子に記号や日本語があるとか。
一般的な命名規則の場合は、自分の考えた、正規表現のフルパス記号でなんとかなったんだけれどね。

困ったので、Bing Search(Bing AIチャット)に相談したら、なるほどなぁ、そういうやり方もあるんだ、と、感心させられた。

フルパスを抜き出す処理だけを下記に。

import tkinter as tk
from tkinterdnd2 import *
import re

#(略)
filepath = event.data

# フォルダの区切りが「/」になっているので、Windows用に「\」に置き換え
filepath = filepath.replace("/", "\\")

pattern = r"{.+?}|[^ ]+"
filepaths = re.findall(pattern, filepath)
#filepaths = sorted(filepaths)
filename = "a.txt"

# ファイル名が空でない場合
if filename:
    # ファイルを追記モードで開く
    with open(filename, "a", encoding="utf-8") as file:
        # ファイルにパスを書き込む
        for filepath in filepaths:
            # フォルダ名、ファイル名に、スペースがあると、前後を「{}」で閉じられているので、それを削除
            file.write(re.sub("{|}", "", filepath + "\n"))
#(略)

簡単に解説。

データを受け取ったら、まず、フォルダの区切り文字をWindows用に置き換える。

そして、正規表現のfindallで、その条件に合うものを抜き出してくれるので、そのパターンを作成。

Bing Searchは、「{.+?}|[^ ]+」というのを出してくれた。

「|」で区切られているので、orということで、どちらかの時、それを抜き出してくれる。

左側の方は、「{}」で囲われている文字列を抜き出してくれる。
つまり、フォルダ名かファイル名にスペースの入っているフルパスが対象となる。

これはわかりやすい。

次の「[^(スペース1個)]+」が感心した。

これは、「スペースではないものが連続している場合」ということで、フォルダ名やファイル名にスペースがなければ、連続した文字列となり、拡張子の後に、区切り文字としてスペースがあるから、それまでのものを抜き出してくれる、というもの。

なるほどねぇ、と、目から鱗!

これで、どちらであっても、フルパスで抜き出してくれるようになった。

本当なら、「{}」をなくしたいので、「{(.+?)}|([^ ]+)」という風にしたいのだけれど、findall関数では、()が2個以上あると、エラーでダメだった。

テキストエディターとかそういうものでは、問題ないのだけれど。

バグなのかなんなのか。

仕方ないので、ファイルに書き込みする時に、「re.sub("{|}"・・・)」として、それらを削除するようにした。

ちょっと煮え切らないけれど、これが一番納得のいくやり方となった。

これは、tkinterdnd2の仕様に関わることだけれど、フォルダ名やファイル名にスペースがあってもなくても、「{}」で囲ってくれていれば、抜き出すのも楽になったのにねぇ。

ちなみに、上記のやり方の前に思いついたものとして、フルパスを正規表現で抜き出すのではなく、区切り文字を変えてやるやり方。

filepath = re.sub("} {| {|} ", "|||", filepath)
filepath = re.sub("{|}", "", filepath)
filepath = re.sub(" (?=[a-zA-Z]:\\\)", "|||", filepath)
filepaths = filepath.split("|||")

「}(スペース){|(スペース){|}(スペース)」というのは、「{}」の前後に、スペースがあるので、それの組み合わせ。

スペース区切りではなく、絶対に出てこないであろう文字列「|||」を、新たに区切り文字にする。

配列の一番最初と最後に、「{}」がある可能性があるので、それを削除。

次に、残りのスペース区切りを「|||」にするために、ドライブ文字の前のスペースを対象に、「|||」に置き換え。
(ここで、まさか、昨日書いた、正規表現の肯定先読みを使うことになろうとは!)

最後は、その区切り文字「|||」で、変数に分割して挿入。

というやり方でやったのである。

このやり方であれば、ファイル名がどうであれ、区切り文字だけをきちんと対処すればいいので、ある意味楽。

が、やはり、フルパスを対象に抜き出したかったので、前者を採用した次第。

先述したけれど、区切り文字、もうちょっと、扱いやすくしてくれればよかったのにねぇ。

|

« 正規表現の先読みと後読み | トップページ | こんな風になってしまった »

コメント

コメントを書く



(ウェブ上には掲載しません)




« 正規表現の先読みと後読み | トップページ | こんな風になってしまった »