Os.listdir () 'den alfasayısal olmayan liste sıralaması


109

Veri dizinlerini işlemek için genellikle python kullanıyorum. Son zamanlarda, listelerin varsayılan sırasının neredeyse anlamsız bir şeye dönüştüğünü fark ettim. Örneğin, aşağıdaki alt dizinleri içeren geçerli bir dizindeysem: run01, run02, ... run19, run20, ve sonra aşağıdaki komuttan bir liste oluşturuyorum:

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

genellikle bu sırayla bir liste alırım:

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

ve bunun gibi. Sıra alfasayısaldı. Ama bu yeni düzen bir süredir bende kaldı.

Bu listelerin (görüntülenen) sırasını belirleyen nedir?


Python listeleri içindeki sıralama aslında ilgilidir (yani listeler sıralanmıştır). Nowayz'e katılıyorum: Gördüğünüz tuhaf düzen muhtemelen dosya sisteminin bir işlevidir. Bunun birkaç yıl önce bir Mac'e bağlı bir 3. taraf ağ dosya sistemi ile gerçekleştiğini gördüm.
David P Simons

Bilgi için teşekkür ederim, liste sipariş yorumunu kaldırdım.
marshall.

@ shog9 Tamam, şimdi sorunun sorulduğunu ve bir şekilde yanıtlandığını görebiliyorum (bağlantılı cevapta verileri sıralama yolu hiçbir zaman sağlanmadı), ancak soru konusu çok net değildi (cevabın görünmediği bir arama yapmak) ve etiketler pek yardımcı olmadı
Dimitris

@Dimitris: Bu adil bir eleştiri - Bunu tekrar yaptım ve iki soruyu birleştirdim, bu yüzden şimdi her iki cevap seti de burada bulunabilir ve sizinki buna işaret ediyor.
Shog9

BTW, buradaki cevaplarla ilgili benim kadar kafası karışıksa, bunun nedeni sorumun sıralı listdirçıktı isteyen başka bir soruyla birleştirilmesidir . Soruların neden birleştirildiğinden emin değilim.
marshall.ward

Yanıtlar:


63

Sanırım sıranın, dosyaların Dosya Sisteminizde indekslenme şekliyle ilgisi var. Gerçekten bir sıraya uymasını istiyorsanız, dosyaları aldıktan sonra listeyi her zaman sıralayabilirsiniz.


128

sortedDizeleri istediğiniz gibi sıralamak için yerleşik işlevi kullanabilirsiniz . Tanımladığınız şeye göre,

sorted(os.listdir(whatever_directory))

Alternatif olarak, .sortbir liste yöntemini de kullanabilirsiniz :

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

Bence hile yapmalı.

os.listdirDosya adlarını alma sırasının muhtemelen tamamen dosya sisteminize bağlı olduğunu unutmayın.


1
İlk numara dosya adlarıyla ilgileniyorsanız sırayı değiştirmez (yani 59.9780radps-0096 hala 9.9746radps-0082'den öncedir). Sanırım bunun nedeni her şeyin bir dizge olması, dolayısıyla ondalık sayı doğru şekilde işlenmiyor.
Elliot

2
Ya da az önce bulduğum natsort kitaplığını kullanın.
Elliot

5
Sadece sorted(listdir)benim için çalıştı. listdir.sort()bana verdi: TypeError: 'NoneType' nesnesi yinelenemez
paul_h

1
@AlexB - tabii ... reverse=Trueazalan sıralama yapmak için geç.
mgilson

1
@ user3895596 - Sanırım sortedilk yazılan şey tek bir satırda tamam mı?
mgilson

43

Başına belgeler :

os.listdir (yol)

Yol ile verilen dizindeki girişlerin adlarını içeren bir liste döndürür. Liste keyfi sıradadır . Özel girişleri içermez '.' ve '..' dizinde bulunsalar bile.

Sıraya güvenilemez ve dosya sisteminin bir ürünüdür.

Sonucu sıralamak için kullanın sorted(os.listdir(path)).


28

Her ne sebeple olursa olsun Python, doğal sıralamaya sahip olmak için yerleşik bir yolla gelmez (1, 10, 2 yerine 1, 2, 10 anlamına gelir), bu yüzden kendiniz yazmalısınız:

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)

Artık bir listeyi sıralamak için bu işlevi kullanabilirsiniz:

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

SORUNLAR: Dizeleri (örneğin klasör adları) sıralamak için yukarıdaki işlevi kullanırsanız ve bunların Windows Gezgini gibi sıralanmasını isterseniz, bazı uç durumlarda düzgün çalışmayacaktır.
Bu sıralama işlevi, içinde belirli 'özel' karakterlerin bulunduğu klasör adlarınız varsa, Windows'ta yanlış sonuçlar döndürür. Örneğin, bu işlev sıralanırken 1, !1, !a, aWindows Gezgini sıralayacaktır !1, 1, !a, a.

Dolayısıyla, Windows Explorer'ın Python'da yaptığı gibi sıralamak istiyorsanız , Windows yerleşik işlevi StrCmpLogicalW'yi ctypes aracılığıyla kullanmanız gerekir (bu elbette Unix'te çalışmaz):

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))

Bu işlev biraz daha yavaştır sorted_alphanumeric().

Bonus: Windows'ta tam yollarıwinsort da sıralayabilir .

Alternatif olarak, özellikle Unix kullanıyorsanız, tam yollara göre doğru bir şekilde (alt klasörler doğru konumda) sıralamak için natsortlibrary ( pip install natsort) kullanabilirsiniz.

Tam yolları sıralamak için şu şekilde kullanabilirsiniz:

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

sorted_alphanumeric()Yukarıdaki işlevden biraz daha yavaş olduğundan, yalnızca klasör adlarının (veya genel olarak dizelerin) normal sıralanması için kullanmayın . Windows Gezgini sıralaması bekliyorsanız
natsortedkitaplık size yanlış sonuçlar verecektir , bunun için kullanın winsort().


Mükemmel çalışıyor. print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']. Tam olarak beklendiği gibi.
user136036

natsortedWindows Gezgini eşleştirme işlevinin uygulanmasına ilişkin uzun süredir devam eden bir açık sorun var . Belki de bir çözüme katkıda bulunmalısınız? github.com/SethMMorton/natsort/issues/41
SethMMorton

8

Sanırım varsayılan olarak sipariş ASCII değeri ile belirleniyor. Bu sorunun çözümü şudur

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

5

Muhtemelen C'nin readdir()dönmesi emridir . Bu C programını çalıştırmayı deneyin:

#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;
}

Yapı çizgisi gibi bir şey olmalı gcc -o foo foo.c.

Not: Bunu ve Python kodunu çalıştırdım ve ikisi de bana sıralı çıktılar verdi, bu yüzden gördüklerinizi yeniden oluşturamıyorum.


1
Soted çıktı görmenizin nedeni işletim sistemi, dosya sistemi, dosya oluşturma zamanı, son birleştirme sırasındaki eylemler gibi birçok faktöre bağlı olabilir ...
Joachim Sauer

4
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]))

Benim ihtiyacım olması durumunda, row_163.pklburadaki gibi bir durum var, os.path.splitext('row_163.pkl')bu ('row_163', '.pkl')yüzden onu '_' bazında bölmek gerekiyor.

ama ihtiyacın olması durumunda şöyle bir şey yapabilirsin

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

nerede

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

ve ayrıca dizin almak için de yapabilirsiniz sorted(os.listdir(path))

ve beğenme durumunda 'run01.txt'ya da 'run01.csv'bunu yapabilirsin

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

Tartışmasız en iyi cevap burada.
Amit Amola

2

"Sıralama" nın her zaman beklediğim şeyi yapmadığını buldum. örneğin, aşağıdaki gibi bir dizinim var ve "sıralama" bana çok garip bir sonuç veriyor:

>>> 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']

Görünüşe göre ilk önce ilk karakteri karşılaştırıyor, eğer bu en büyüğü ise, sonuncusu olurdu.


2
Bu beklenen bir davranıştır. ('5' > '403') is True.
AXO

2
@AXO doğrudur, çünkü bu noktada sayıların niceliksel değerlerini değil, alfanümerik sıralamayı karşılaştırıyorsunuz. Beklentinize benzer bir sıralama elde etmek için, klasörlerinizde sayı dolgusu kullanmak isteyebilirsiniz ... ['002', '003', '004', '005', '403', '404', ' 405 ',' 406 ']
Andrew

2

Gönderen belgeler :

Liste keyfi sıradadır ve özel girişleri içermez '.' ve '..' dizinde bulunsalar bile.

Bu, sıranın muhtemelen işletim sistemine / dosya sistemine bağlı olduğu, özellikle anlamlı bir sıranın olmadığı ve bu nedenle belirli bir şey olmasının garanti edilmediği anlamına gelir. Belirtilen birçok cevap: tercih edilirse, alınan liste sıralanabilir.

Şerefe :)


2

Elliot'ın cevabı sorunu mükemmel bir şekilde çözüyor ama bu bir yorum olduğu için fark edilmiyor, bu yüzden birine yardım etmek amacıyla bir çözüm olarak yineliyorum.

Natsort kitaplığını kullanın:

Kütüphaneyi Ubuntu ve diğer Debian sürümleri için aşağıdaki komutla kurun

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

Bu kitaplığın nasıl kullanılacağına ilişkin ayrıntılar burada bulunur


1
Bu daha doğrudur sorted()! Teşekkürler
Färid Alijani

1

Önerilen os.listdirve sortedkomut kombinasyonu, ls -lLinux altındaki komutla aynı sonucu verir . Aşağıdaki örnek, bu varsayımı doğrular:

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$ 

Bu yüzden, iyi bilinen ls -lkomutun sonucunu python kodunda yeniden oluşturmak isteyen biri için sorted( os.listdir( DIR ) )oldukça iyi çalışıyor.


0
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
Bu, neden bir çözüm sunmadan davranışı gördüklerini açıklar.
Daniel Watkins

1
OP nasıl olduğunu değil nedenini bilmek istiyor.
Denis

@Denis bunu belirttiğiniz için teşekkürler - Daha önce fark etmemiştim
Dimitris

@DanielWatkins Tamam, Değil.)
Denis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.