質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

3124閲覧

ファイルパスの途中省略 について

shin0859

総合スコア15

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/16 08:03

python 初心者程度のshinです
上級者のアドバイスをご指示ください。

下記の様にtk.filedialogで得たフルパスを、 途中省略(...)のトータルバイトを指定バイト以下で表示する関数は pythonには有るのでしょうか。(VB、vbaではPathCompactPath) フルパス C:\Users\dare\安佐AppData\Local\Programs\Python\Python38" コンパクトパス C:\Users\dare\安佐AppData...\Python38 自分なりに作ったみたコードでの質問ですが

  ①そのような関数の有無及びご提示
②コードのより良い手直しのご指示
③ファイルパスのルート記号(¥,,/)は、os環境に依存し
split記号の使い分けに影響が有るのでしょうか

チェックも含めご指導宜しくお願いいたします。

該当のソースコード

python

1 def Compact_Path(self): 2 #raw文字列を使う(rまたはRを先頭に付けます) 3 fullpath = R"C:\Users\dare\安佐AppData\Local\Programs\Python\Python38" 4 fullbyte = 45 5 ar_str=fullpath.split('\') 6 ar_byt = [len(str.encode('shift_jis')) for str in ar_str] 7 n = len(ar_str) 8 print(ar_str) 9 print(ar_byt) 10 11 Nokori_byte = fullbyte - ar_byt.pop(0) - ar_byt.pop(-1) - 4 # (... + \ = 4) 12 print('Nokori=' + str(Nokori_byte)) 13 14 i = 0 15 num = 1 + ar_byt[1] 16 temp = "" 17 while num <= Nokori_byte: 18 num = num + 1 + ar_byt[i+1] 19 temp = temp + '\' + ar_str[i+1] 20 i += 1 21 22 Compact_Path = ar_str.pop(0) + temp + '...' + '\' + ar_str.pop(-1) 23 # Compact_Path = ar_str[0] + temp + '...' + '\' + ar_str[n-1] # 上記に同じ 24 25 print(str(Compact_Path)) 26 27

--- 環境 ---

python3.8
tkinter8.6
pycharm2021.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

自己解決

その後ですが、投稿 2021/07/17 13:35付け投稿コードですが
pop()は参照して、配列から削除するため
ar_byt.pop(0)などのpop()は間違えでar_byt.[0]などに訂正します


2021/07/17 18:15 teamikl様 の問に答えたいと思います

>例外1: fullpath = r"C:\Users\dare\日本旅行旅写真その1東北"
if (n>2): とし
==compact_path=ar_str[0] + '...' + sep + ar_str[n-1]
最初と最後の結合で文字オーバーも構いません
最後のフォルダ名が確認されれば良しとします

>例外2: fullpath = r"c:\photo"
ar_strが[c:,photo]の要素2つの場合
~else: で使い分けます

>tkinter の width が「文字数」
全角文字を2文字カウントとしますので、
==compact_byte = 24
==tk.Label(width=23,...)
で納まる様です

>もう一点、後から気付いた所
>Compact_Path = ... 関数名と同じ名前の変数に入れるのは、VBA の作法だったはず、
>Python では rerturn を使い値を返すようにします
上記の件ですが、
==compact_pathname = ar_str[0] + temp + '...' + sep + ar_str[n-1]
==return compact_pathname
右辺が長いため、この様な表示が正解でしょうか

以上の内容で解決済と考えます
teamikl様 いろいろ有難うございました

イメージ添付 [compactPath]以降はチェック用です
イメージ説明

コード全容を提示します

python

1 2 def Compact_path(self): 3 # raw文字列を使う(rまたはRを先頭に付けます) 4 # fullpath = r"C:\Users\dare\安佐AppData\Local\Programs\Python\Python38" 5 fullpath = r"C:\Users\dare\fghijklmnopq" 6 # fullpath = r"c:\test" 7 fullbyte = 24 8 9 sep = os.path.sep # windowsでは "\" 10 ar_byt = [self.len_count(str) for str in ar_str] 11 n = len(ar_str) 12 print(ar_str) 13 print(ar_byt) 14 15 if (n>2): 16 Nokori_byte = fullbyte - ar_byt[0] - ar_byt[-1] - 4 # (... + \ = 4) 17 print('Nokori=' + str(Nokori_byte)) 18 19 i = 0 20 num = 1 + ar_byt[1] 21 temp = "" 22 while num <= Nokori_byte: 23 num = num + 1 + ar_byt[i+1] 24 temp = temp + sep + ar_str[i+1] 25 i += 1 26 27 ar_str[0] + temp + '...' + sep + ar_str[n - 1] 28 compact_pathname = ar_str[0] + temp + '...' + sep + ar_str[n-1] 29 return compact_pathname 30 31 else: 32 return self.fullpath 33

投稿2021/07/20 08:28

編集2021/07/20 08:32
shin0859

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

teamikl様 のコメントから (7/20追加訂正)

python

1 # https: // www.headboost.jp / python - check - the - length - of - strings / 2 def len_count(self, str): 3 count = 0 4 for c in str: 5 if unicodedata.east_asian_width(c) in 'FWA': 6 count += 2 7 else: 8 count += 1 9 return count 10 11 12 def Compact_Path(self): 13 # raw文字列を使う(rまたはRを先頭に付けます) 14 sep = os.path.sep # windowsでは "\" 15 fullpath = R"C:\Users\dare\安佐AppData\Local\Programs\Python\Python38" 16 fullbyte = 45 17 ar_str=fullpath.split(sep) 18 ar_byt = [self.len_count(str) for str in ar_str] 19 n = len(ar_str) 20 print(ar_str) 21 print(ar_byt) 22 23 ~~Nokori_byte = fullbyte - ar_byt.pop(0) - ar_byt.pop(-1) - 4 # (... + \ = 4)~~ 24 Nokori_byte = fullbyte - ar_byt.[0] - ar_byt.[-1] - 4 # (... + \ = 4) 25 print('Nokori=' + str(Nokori_byte)) 26 27 i = 0 28 num = 1 + ar_byt[1] 29 temp = "" 30 while num <= Nokori_byte: 31 num = num + 1 + ar_byt[i+1] 32 temp = temp + sep + ar_str[i+1] 33 i += 1 34 35 ~~Compact_Path = ar_str.pop(0) + temp + '...' + sep + ar_str.pop(-1)~~ 36 Compact_Path = ar_str[0] + temp + '...' + sep + ar_str[n-1] ~~# 上記に同じ~~ 37 38 print(str(Compact_Path))

投稿2021/07/17 04:35

編集2021/07/20 07:08
shin0859

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2021/07/17 06:47

コードの改良案ですが、2つのリスト ar_str, ar_byt について for n,m in zip(ar_str, ar_byt): で2つのリストを同時に回せます。 ※ 但し、リストの長さが同じ場合。事前の pop を少し工夫する必要があり ⇛ リストの添字アクセス [i+i] や ループ変数 i を省略できます。 可能ならば for 文で処理できないかの検討 大抵の場合、コードも簡潔になり、実行効率もよく、 範囲外の添字アクセス起こらなくなる為、幾つかの例外も自然に回避できます。 現在のコードでは、fullpath に対して fullbyte が小さすぎる時に、 while num <= Nokori_byte が終わらずに i が増え続け、 リストの範囲外アクセスが起こりそうです。 ---- 文字数・バイト数に関しては encode('shift_jis')さえなければ、普通に len で文字数を得られます。 unicodedata.east_asian_width~で行っているのは、 「全角文字を2文字とカウント」する方法ですが、意図通りの挙動ですか?
shin0859

2021/07/17 07:52

追加コメント有難うございます 簡単にコメント出来るとこから > 「全角文字を2文字とカウント」する方法ですが、意図通りの挙動ですか? はい、意図するところです。 全角を2文字とカウントして、限られたwidthに表示するためです (最低でも、ドライブ名...\最後のフォルダー名) こんな感じの使い方です https://drive.google.com/file/d/1TS6T2J2baZAFFaUB9cxbHMyhjxDCxlhl/view?usp=sharing
shin0859

2021/07/17 08:08 編集

次の質疑ですが、 >現在のコードでは、fullpath に対して fullbyte が小さすぎる時に、 >while num <= Nokori_byte が終わらずに i が増え続け、 >リストの範囲外アクセスが起こりそうです。 今回の例でいいますと、最初と最後はwhileの対象外で numはひとつ先のバイト数を加算しています。それがNokori_byteをオーバーすると whileを抜けます。tempはひとつ前までの結合となります。 ar_str=['C:', 'Users', 'dare', '安佐AppData', 'Local', 'Programs', 'Python' , 'Python38'] ar_byt=[2, 5, 4, 11, 5, 8, 6, 8] そのことから 「終わらずに i が増え続け」の現象は起きないと思われます。
teamikl

2021/07/17 09:15

google drive の URL はアクセス拒否で見れませんでした。 > 全角を2文字とカウントして、限られたwidthに表示するためです tkinter の width が「文字数」指定なので、全角文字を2文字カウントは、 結果、全角文字では少なく、半角英数字のパスの方が多く表示できる状態になります。 意図通りであれば大丈夫ですが、確認で 「半角英語を多く含む場合」と、「日本語を多く含む場合」で表示テストしてみて下さい。 > 今回の例でいいますと、 今回の例 (fullpath) では大丈夫ですが、 filedialogで得たフルパスを・・・とあるので、対象を「任意のパス」とした時に ar_byt[i+1] や pop が例外を出さないように、注意が必要になります。 全角文字が2カウントな点の考慮も忘れずに。 もし自分自身でしか使わないプログラムで、 例外的な状況を常用外とするなら、必要十分な条件を満たすコードで良いかもしれませんが、 一応、現状の問題点を挙げておくと、長過ぎるファイル名、短過ぎるパスの対策がされてません。 例外1: fullpath = r"C:\Users\dare\長い日本語ファイル名20文字以上...~~~~.txt" # fullbyte が 負の値になりえる 例外2: fullpath = r"c:\test.txt" # pop する要素が足りない for文でリストを回す方法を検討する利点は、範囲外アクセス自体を回避できるので、 幾つかの例外的な状況を考えなくても済むようなロジックが組めます。 もう一点、後から気付いた所 Compact_Path = ... 関数名と同じ名前の変数に入れるのは、VBA の作法だったはず、 Python では rerturn を使い値を返すようにします
shin0859

2021/07/17 10:15

えっ width が「文字数」! ピクセル等と思っていました その辺の考え直しが必要ですかぁ しばらく、考えます
teamikl

2021/07/17 10:53 編集

正確には、Entry の widthオプション が文字数です。 (ピクセル用の別メソッドもあります winfo_width() ) tkinter の文字関連のウィジェットのみ width が文字数で、 ウィジェットによっては、height が行数だったりすることもあるので、注意が必要な点。 Frame や Canvas 等のwidthに関しては、ピクセル数です。 import tkinter as tk from tkinter.font import nametofont root = tk.Tk() font = "", 30 # 等幅フォント利用 # font = nametofont("TkFixedFont").copy() # font.config(size=30) tk.Label(root, text="日本語表示テスト", font=font).pack() tk.Label(root, text="llllllll"*2, font=font).pack() root.mainloop() 等幅フォント利用 (TkFixedFont) では全角文字はおよそ半角2文字分の幅なので、大丈夫そうです。 プロポーショナルフォントだと、表示幅に差が出ます。(小文字の l 等、横幅の狭い文字)
guest

0

①そのような関数の有無及びご提示

標準ライブラリの textwrap.shorten が近い処理をする関数です。

英文章を対象とした関数なのでそのままは使えませんが、利用する場合は
パス区切り文字 <-> 空白文字 <-> パス区切り文字の変換をすれば、応用可能です。
(ファイルパスでは不要かもしれませんが、例えば タブ幅を考慮したい場合等には便利)


②コードのより良い手直しのご指示

  • shift_jis への変換は悪手で、文字コード内にパスの区切り文字が含まれる事があるため、

 例えば、日本語のフォルダ名で表示化けや誤動作の原因になる可能性があります。
現状のコードの利用では大丈夫そうですが、潜在的なリスクになるので避けたほうが良い点。
詳しくは「0x5c ダメ文字」で検索。

  • 文字列長 != バイト長。必ず等しくなるとは限りません。
  • パスの分解

 従来の方法 os.path を使う os.path.dirname, os.path.basename
pathlibを使う方法 pathlib.Path (parent, name 属性でアクセス可能)

  • 文字列のスライスを使い

 path[:path.rindex("\", 0, width)] で、width未満、最後の区切り文字迄 で切り詰める。

python

1import os 2 3sep = os.path.sep # windowsでは "\" 4width = 45 5fullpath = R"C:\Users\dare\安佐AppData\Local\Programs\Python\Python38" 6 7print(fullpath[:fullpath.rindex(sep, 0, width)]) # => "C:\Users\dare\安佐AppData\Local\Programs" 8 9 10# TODO: 文字列が短い場合の対応 rindex がエラーになります。 11# min(width, len(path)) 12 13# TODO: パスの区切り文字が含まれない場合の対応 rindex がエラーになります。 14# 代案 rfind メソッドを使う。見つからない場合は -1 (末尾のindex)

例外的なケースが幾つか考えられるので、単体テスト(unittest)の導入をおすすめします。
毎回、テストの度に文字列を書き換えて試行は大変なので、
テストを自動化すれば、テストに掛かる時間を大幅に節約できます。

関数の入出力(引数・戻り値)を、
フルパスの文字列を受け取り、指定幅に切り詰めた文字列を返す関数とすると、
単体テストが作りやすくなります。


③ファイルパスのルート記号(¥,,/)は、os環境に依存し

split記号の使い分けに影響が有るのでしょうか

ルート記号?ではなく、区切り文字は
os.path.sep で、実行環境におけるパスの区切り文字を判別できます。


tkinter タグがあることから、GUIへの描画となるともう少し続きが合って、、
等幅フォントの場合は、文字数指定で切り詰められますが、

実際の表示幅はフォントと描画したい文字に依存するため、
font.metrics 等の情報を用いて判断する必要が出てきます。⇛ パス毎に分解して、文字毎の幅を計算

PathCompactPathではこの点、フォントも考慮して表示領域幅に対しての切り詰めを行います。

投稿2021/07/16 11:22

編集2021/07/16 12:03
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shin0859

2021/07/17 04:37 編集

teamikl様 いつもコメント有難うございます コメントの中身ですが、いろいろ厳密的な対応策を示して頂いていますが、 選択ファイルは自分(orユーザー)でファイル名を付けるので、イレギュラー的にはならないと考えています。 又Compact_Pathはユーザーが選んだファイルのファイルパスが長い場合、 途中省略記号(...)で表示し、最後のファイル名を表記し確認用としての、パスと考えています。 (文字バイト数も厳密では有りません、収まれば良い程度と考えています。) それらの考えからコメントを参考にして、下記の点を修正してみました。 パスの分解 → sep = os.path.sep で shift_jis への変換は悪手 → https://www.headboost.jp/python-check-the-length-of-strings/ で 別場所に表示します   何か気が付いた点が有りましたら アドバイス宜しくお願いいたします。
guest

0

compactpath 0.5.0を使ってみてください。

投稿2021/07/16 09:09

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shin0859

2021/07/17 02:13

ppaul様 遅い返事ですが、コメント有難うございます 確認しよう思いましたが、compactpathの使用例がどこにも見つかりません その情報を提示していただければ助かります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問