os.listdir()からの非英数字リストの順序


108

私はデータのディレクトリを処理するためにpythonをよく使用します。最近、リストのデフォルトの順序がほとんど意味のないものに変更されていることに気付きました。たとえば、run01、run02、... run19、run20というサブディレクトリを含む現在のディレクトリにいる場合、次のコマンドからリストを生成します。

dir = os.listdir(os.getcwd())

それから私は通常この順序でリストを取得します:

dir = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08', ... ]

等々。順序は英数字でした。しかし、この新しい命令はしばらくの間私と一緒に残っています。

これらのリストの(表示される)順序を決定するのは何ですか?


pythonリスト内の順序は実際に関連があります(つまり、リストは順序付けされています)。私はNowayzに同意します。あなたが見ている奇妙な順序は、おそらくファイルシステムの機能です。私はこれが数年前にMacに接続されたサードパーティのネットワークファイルシステムで発生するのを見ました。
David P Simons

情報をありがとう、私はリスト注文コメントを削除しました。
marshall.ward 2012年

@ shog9わかりました、これで質問が尋ねられ、回答の種類(リンクされた回答でデータを並べ替える方法が提供されなかった)がわかりましたが、質問のトピックはあまり明確ではありませんでした(回答が表示されない検索を行った)タグはあまり
役に立た

@Dimitris:それはかなりの批判です-私はこれにタイトルを付けて2つの質問をマージしました。それで両方の答えのセットがここにあり、あなたの質問はそれを指摘し続けます。
Shog9 2013年

ところで、ここでの回答について私と同じくらい他の誰かが混乱している場合、それは私の質問がソートされたlistdir出力を要求する別の質問とマージされたためです。質問が統合された理由はわかりません。
marshall.ward 2017

回答:


63

順序は、ファイルシステムでファイルのインデックスが作成される方法に関係していると思います。本当にそれをある順序にしたい場合は、ファイルを取得した後でいつでもリストをソートできます。


128

組み込みsorted関数を使用して、必要に応じて文字列をソートできます。あなたの説明に基づいて、

sorted(os.listdir(whatever_directory))

または、.sortリストの方法を使用できます。

lst = os.listdir(whatever_directory)
lst.sort()

私はトリックをするべきだと思います。

os.listdirファイル名を取得する順序は、おそらくファイルシステムに完全に依存していることに注意してください。


1
番号が最初のファイル名を処理する場合、順序を変更しません(つまり、59.9780radps-0096は9.9746radps-0082より前です)。すべてが文字列なので、小数が正しく処理されないためだと思います。
エリオット

2
または、私が見つけたnatsortライブラリを使用します。
エリオット

5
sorted(listdir)私だけのために働いた。listdir.sort()TypeError: 'NoneType'オブジェクトは反復可能ではありません
paul_h

1
@AlexB-確かに... reverse=True降順で並べ替えるために渡すだけです。
mgilson 2018

1
@ user3895596- sorted最初に書かれたものは一行でいいと思いますか?
mgilson

43

ドキュメントごと

os.listdir(パス)

パスで指定されたディレクトリ内のエントリの名前を含むリストを返します。リストは任意の順序です。特別なエントリ「。」は含まれません。と '..'がディレクトリに存在する場合でも。

注文は信頼できず、ファイルシステムの成果物です。

結果を並べ替えるには、を使用しますsorted(os.listdir(path))


26

何らかの理由でPythonには、自然なソートを行うための組み込みの方法(1、10、2ではなく1、2、10)が付属していないため、自分で記述する必要があります。

import re
def sorted_alphanumeric(data):
    convert = lambda text: int(text) if text.isdigit() else text.lower()
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
    return sorted(data, key=alphanum_key)

これで、この関数を使用してリストをソートできます。

dirlist = sorted_alphanumeric(os.listdir(...))

問題: 上記の関数を使用して文字列(フォルダー名など)を並べ替え、Windowsエクスプローラーのように並べ替えたい場合、一部のエッジケースで正しく機能しません。
この並べ替え関数は、特定の「特殊」文字が含まれるフォルダー名がある場合、Windowsで誤った結果を返します。たとえば、この関数は並べ替えます1, !1, !a, aが、Windowsエクスプローラは並べ替えます!1, 1, !a, a

したがって、WindowsエクスプローラーがPythonで行うように正確並べ替えるには、Windows組み込み関数を使用する必要があります。 ctypesを介し StrCmpLogicalW(もちろん、これはUnixでは機能しません)。

from ctypes import wintypes, windll
from functools import cmp_to_key
def winsort(data):
    _StrCmpLogicalW = windll.Shlwapi.StrCmpLogicalW
    _StrCmpLogicalW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR]
    _StrCmpLogicalW.restype  = wintypes.INT

    cmp_fnc = lambda psz1, psz2: _StrCmpLogicalW(psz1, psz2)
    return sorted(data, key=cmp_to_key(cmp_fnc))

この関数は、 sorted_alphanumeric()ます。

ボーナス: winsort Windowsでもフルパスをソートできます

または、特にUnixを使用している場合は、 natsortライブラリ(pip install natsort)をして、正しい方法(正しい位置にあるサブフォルダー)でフルパスで並べ替える。

次のように使用して、フルパスをソートできます。

from natsort import natsorted, ns
dirlist = natsorted(dirlist, alg=ns.PATH | ns.IGNORECASE)

フォルダー名(または文字列一般)の通常の並べ替えには使用しないでくださいsorted_alphanumeric()。これは、上記の機能よりもかなり遅いためです。Windowsエクスプローラでの並べ替えを想定している場合
natsortedは、ライブラリで誤った結果が返されるので、それを使用winsort()してください。


完全に正常に動作します。print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']。予想通り。
user136036

natsortedWindowsエクスプローラーの一致機能を実装するために、未解決の未解決の問題があります。おそらくあなたは解決策を提供するべきですか?github.com/SethMMorton/natsort/issues/41
SethMMorton

8

デフォルトでは、順序はASCII値で決定されると思います。この問題の解決策はこれです

dir = sorted(os.listdir(os.getcwd()), key=len)

5

おそらくCがreaddir()返す順序だけです。次のCプログラムを実行してみてください。

#include <dirent.h>
#include <stdio.h>
int main(void)
{   DIR *dirp;
    struct dirent* de;
    dirp = opendir(".");
    while(de = readdir(dirp)) // Yes, one '='.
        printf("%s\n", de->d_name);
    closedir(dirp);
    return 0;
}

ビルドラインは次のようになります gcc -o foo foo.c

PSこれとあなたのPythonコードを実行しただけで、どちらもソートされた出力を提供したので、あなたが見ているものを再現できません。


1
あなたはsoted出力は、OS、ファイルシステム、ファイルの作成日時、最後のデフラグ中の行動、...など、多くの要因に依存して見ていることを理由
ヨアヒム・ザウアー

3
aaa = ['row_163.pkl', 'row_394.pkl', 'row_679.pkl', 'row_202.pkl', 'row_1449.pkl', 'row_247.pkl', 'row_1353.pkl', 'row_749.pkl', 'row_1293.pkl', 'row_1304.pkl', 'row_78.pkl', 'row_532.pkl', 'row_9.pkl', 'row_1435.pkl']                                                                                                                                                                                                                                                                                                 
sorted(aaa, key=lambda x: int(os.path.splitext(x.split('_')[1])[0]))

私の要件の場合、私はrow_163.pklここos.path.splitext('row_163.pkl')にあるようなケースにそれを分割します('row_163', '.pkl')それを分割ので、「_」にも基づいて分割する必要があります。

しかし、あなたの要件の場合には、あなたは次のようなことをすることができます

sorted(aa, key = lambda x: (int(re.sub('\D','',x)),x))

どこ

aa = ['run01', 'run08', 'run11', 'run12', 'run13', 'run14', 'run18']

また、ディレクトリを取得するために行うことができます sorted(os.listdir(path))

のような場合、'run01.txt'または'run01.csv'あなたはこのようにすることができます

sorted(files, key=lambda x : int(os.path.splitext(x)[0]))

2

「並べ替え」が期待どおりに動作しない場合があります。たとえば、次のようなディレクトリがあり、「ソート」を実行すると非常に奇妙な結果が得られます。

>>> os.listdir(pathon)
['2', '3', '4', '5', '403', '404', '407', '408', '410', '411', '412', '413', '414', '415', '416', '472']
>>> sorted([ f for f in os.listdir(pathon)])
['2', '3', '4', '403', '404', '407', '408', '410', '411', '412', '413', '414', '415', '416', '472', '5']

最初の文字を最初に比較するようですが、それが最大の場合は最後の文字になります。


2
これは予想される動作です。('5' > '403') is True
AXO

2
@AXOが正しいのは、現時点では数値の数値ではなく英数字の並べ替えを比較しているためです。期待どおりの並べ替えを行うには、フォルダに数字のパディングを使用することをお勧めします... ['002'、 '003'、 '004'、 '005'、 '403'、 '404'、 ' 405 '、' 406 ']
Andrew

2

ドキュメントから:

リストは任意の順序であり、特別なエントリ「。」は含まれていません。と '..'がディレクトリに存在する場合でも。

これは、順序がOS /ファイルシステムに依存している可能性があり、特に意味のある順序がないため、特に何も保証されないことを意味します。多くの回答が述べたように:必要に応じて、取得したリストを並べ替えることができます。

乾杯:)


2

エリオット答えはそれを完全に解決しますが、それはコメントなので、気付かれないようになり、誰かを助けることを目的として、それを解決策として繰り返し述べています。

natsortライブラリを使用:

Ubuntuおよびその他のDebianバージョンの場合は、次のコマンドを使用してライブラリをインストールします

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

このライブラリの使用方法の詳細はここにあります


1
それはより正確ですsorted()!おかげで
ファリドAlijani

1
In [6]: os.listdir?

Type:       builtin_function_or_method
String Form:<built-in function listdir>
Docstring:
listdir(path) -> list_of_strings
Return a list containing the names of the entries in the directory.
path: path of directory to list
The list is in **arbitrary order**.  It does not include the special
entries '.' and '..' even if they are present in the directory.

これは、彼らが解決策を提供することなく行動を見ている理由を説明しています。
ダニエルワトキンス

1
OPは、方法ではなく、理由を知りたいだけです。
デニス

@Denis、これを指摘してくれてありがとう-以前は気づかなかった
Dimitris

@DanielWatkins OK、そうではありません。)
デニス

0

os.listdirソートされたコマンドの提案された組み合わせは、Linuxでのls -lコマンドと同じ結果を生成します。次の例では、この仮定を検証します。

user@user-PC:/tmp/test$ touch 3a 4a 5a b c d1 d2 d3 k l p0 p1 p3 q 410a 409a 408a 407a
user@user-PC:/tmp/test$ ls -l
total 0
-rw-rw-r-- 1 user user 0 Feb  15 10:31 3a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 407a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 408a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 409a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 410a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 4a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 5a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 b
-rw-rw-r-- 1 user user 0 Feb  15 10:31 c
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d1
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d2
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d3
-rw-rw-r-- 1 user user 0 Feb  15 10:31 k
-rw-rw-r-- 1 user user 0 Feb  15 10:31 l
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p0
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p1
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p3
-rw-rw-r-- 1 user user 0 Feb  15 10:31 q

user@user-PC:/tmp/test$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.listdir( './' )
['d3', 'k', 'p1', 'b', '410a', '5a', 'l', 'p0', '407a', '409a', '408a', 'd2', '4a', 'p3', '3a', 'q', 'c', 'd1']
>>> sorted( os.listdir( './' ) )
['3a', '407a', '408a', '409a', '410a', '4a', '5a', 'b', 'c', 'd1', 'd2', 'd3', 'k', 'l', 'p0', 'p1', 'p3', 'q']
>>> exit()
user@user-PC:/tmp/test$ 

そのため、Pythonコードでよく知られているls -lコマンドの結果を再現したい人にとって、sorted(os.listdir(DIR))はかなりうまく機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.