Nicht alphanumerische Listenreihenfolge von os.listdir ()


108

Ich benutze oft Python, um Datenverzeichnisse zu verarbeiten. Kürzlich habe ich festgestellt, dass sich die Standardreihenfolge der Listen in etwas fast Unsinniges geändert hat. Wenn ich mich beispielsweise in einem aktuellen Verzeichnis befinde, das die folgenden Unterverzeichnisse enthält: run01, run02, ... run19, run20, und dann generiere ich eine Liste mit dem folgenden Befehl:

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

dann bekomme ich normalerweise eine Liste in dieser Reihenfolge:

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

und so weiter. Die Reihenfolge war früher alphanumerisch. Aber diese neue Ordnung ist mir schon eine Weile geblieben.

Was bestimmt die (angezeigte) Reihenfolge dieser Listen?


Die Reihenfolge innerhalb von Python-Listen ist tatsächlich relevant (dh Listen sind geordnet). Ich stimme Nowayz zu: Die seltsame Reihenfolge, die Sie sehen, ist wahrscheinlich eine Funktion des Dateisystems. Ich habe dies vor einigen Jahren mit einem Netzwerkdateisystem eines Drittanbieters gesehen, das an einen Mac angeschlossen ist.
David P Simons

Vielen Dank für die Info, ich habe den Kommentar zur Listenreihenfolge entfernt.
Marshall.ward

@ shog9 Ok, jetzt kann ich sehen, dass die Frage gestellt und irgendwie beantwortet wurde (die Art der Datensortierung wurde in der verknüpften Antwort nie angegeben), aber das Fragethema war nicht sehr klar (bei einer Suche wurde diese Antwort nicht angezeigt). und die Tags waren nicht sehr hilfreich
Dimitris

@Dimitris: Das ist eine faire Kritik - ich habe diese umbenannt und die beiden Fragen zusammengeführt, sodass jetzt beide Antworten hier zu finden sind und Ihre weiterhin darauf verweisen.
Shog9

Übrigens, wenn jemand so verwirrt ist wie ich über die Antworten hier, dann deshalb, weil meine Frage mit einer anderen Frage zusammengeführt wurde, die eine sortierte listdirAusgabe anfordert . Ich bin nicht sicher, warum die Fragen zusammengeführt wurden.
Marshall.ward

Antworten:


63

Ich denke, die Reihenfolge hat mit der Art und Weise zu tun, wie die Dateien in Ihrem Dateisystem indiziert werden. Wenn Sie wirklich möchten, dass die Reihenfolge eingehalten wird, können Sie die Liste nach dem Abrufen der Dateien jederzeit sortieren.


128

Sie können die integrierte sortedFunktion verwenden, um die Zeichenfolgen nach Ihren Wünschen zu sortieren. Basierend auf dem, was Sie beschreiben,

sorted(os.listdir(whatever_directory))

Alternativ können Sie die .sortMethode einer Liste verwenden:

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

Ich denke, sollte den Trick tun.

Beachten Sie, dass die Reihenfolge, in os.listdirder die Dateinamen abgerufen werden, wahrscheinlich vollständig von Ihrem Dateisystem abhängt.


1
Ändert die Reihenfolge nicht, wenn es sich um Dateinamen mit der Nummer 1 handelt (dh 59.9780radps-0096 liegt immer noch vor 9.9746radps-0082). Ich denke, das liegt daran, dass alles eine Zeichenfolge ist, sodass die Dezimalstelle nicht richtig behandelt wird.
Elliot

2
Oder benutze die Natsort-Bibliothek, die ich gerade gefunden habe.
Elliot

5
Hat nur sorted(listdir)für mich gearbeitet. listdir.sort()gab mir: TypeError: 'NoneType' Objekt ist nicht iterierbar
paul_h

1
@AlexB - sicher ... nur übergeben reverse=True, um es absteigend zu sortieren.
mgilson

1
@ user3895596 - Ich denke, dass das sortedzuerst geschriebene Ding es in einer einzigen Zeile macht OK?
mgilson

43

Gemäß der Dokumentation :

os.listdir (Pfad)

Gibt eine Liste zurück, die die Namen der Einträge in dem durch path angegebenen Verzeichnis enthält. Die Liste ist in beliebiger Reihenfolge . Die Sondereinträge '.' Sind nicht enthalten. und '..' auch wenn sie im Verzeichnis vorhanden sind.

Auf die Reihenfolge kann man sich nicht verlassen und sie ist ein Artefakt des Dateisystems.

Verwenden Sie zum Sortieren des Ergebnisses sorted(os.listdir(path)).


25

Python verfügt aus irgendeinem Grund nicht über eine integrierte Methode für eine natürliche Sortierung (dh 1, 2, 10 anstelle von 1, 10, 2). Sie müssen es also selbst schreiben:

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)

Mit dieser Funktion können Sie jetzt eine Liste sortieren:

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

PROBLEME: Wenn Sie die obige Funktion zum Sortieren von Zeichenfolgen (z. B. Ordnernamen) verwenden und möchten, dass diese wie im Windows Explorer sortiert werden, funktioniert dies in einigen Randfällen nicht ordnungsgemäß.
Diese Sortierfunktion gibt unter Windows falsche Ergebnisse zurück, wenn Sie Ordnernamen mit bestimmten Sonderzeichen enthalten. Diese Funktion wird beispielsweise sortiert 1, !1, !a, a, während der Windows Explorer sortiert !1, 1, !a, a.

Wenn Sie also genau wie Windows Explorer in Python sortieren möchten, müssen Sie die in Windows integrierte Funktion StrCmpLogicalW über ctypes verwenden (dies funktioniert natürlich nicht unter 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))

Diese Funktion ist etwas langsamer als sorted_alphanumeric().

Bonus: winsortKann auch vollständige Pfade unter Windows sortieren .

Alternativ, insbesondere wenn Sie Unix verwenden, können Sie die natsortBibliothek verwenden (pip install natsort ) verwenden, um nach vollständigen Pfaden auf korrekte Weise zu sortieren (dh Unterordner an der richtigen Position).

Sie können es so verwenden, um vollständige Pfade zu sortieren:

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

Verwenden Sie es nicht zum normalen Sortieren von Ordnernamen (oder Zeichenfolgen im Allgemeinen), da es etwas langsamer ist als die sorted_alphanumeric()oben beschriebene Funktion.
natsortedDie Bibliothek liefert falsche Ergebnisse, wenn Sie eine Windows Explorer-Sortierung erwarten. Verwenden Sie diese Option winsort().


Funktioniert einwandfrei. print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']. Genau wie erwartet.
user136036

Es gibt ein seit langem offenes Problem bei natsortedder Implementierung der Windows Explorer-Matching-Funktionalität. Vielleicht sollten Sie eine Lösung beitragen? github.com/SethMMorton/natsort/issues/41
SethMMorton

8

Ich denke, standardmäßig wird die Reihenfolge mit dem ASCII-Wert bestimmt. Die Lösung für dieses Problem ist diese

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

5

Es ist wahrscheinlich nur die Reihenfolge, in der C readdir()zurückkehrt. Versuchen Sie, dieses C-Programm auszuführen:

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

Die Build-Linie sollte so etwas wie sein gcc -o foo foo.c.

PS Ich habe gerade diesen und Ihren Python-Code ausgeführt, und beide haben mir eine sortierte Ausgabe gegeben, sodass ich nicht reproduzieren kann, was Sie sehen.


1
Der Grund, warum Sie eine Soted-Ausgabe sehen, kann von vielen Faktoren abhängen, wie z. B. Betriebssystem, Dateisystem, Zeitpunkt der Erstellung von Dateien, Aktionen während der letzten Defragmentierung, ...
Joachim Sauer

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

Als im Falle meiner Anforderung habe ich den Fall wie row_163.pklhier os.path.splitext('row_163.pkl')wird es in brechen('row_163', '.pkl') aufteilen ihn auch basierend auf '_' aufteilen.

aber im Falle Ihrer Anforderung können Sie so etwas tun

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

wo

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

und auch zum Abrufen von Verzeichnissen können Sie tun sorted(os.listdir(path))

und für den Fall von like 'run01.txt'oder 'run01.csv'du kannst so machen

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

2

Ich fand, dass "sort" nicht immer das tut, was ich erwartet hatte. zB habe ich ein Verzeichnis wie unten und die "Sortierung" gibt mir ein sehr seltsames Ergebnis:

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

Es scheint, dass es das erste Zeichen zuerst vergleicht, wenn das das größte ist, wäre es das letzte.


2
Dies ist das erwartete Verhalten. ('5' > '403') is True.
AXO

2
@AXO ist korrekt, da Sie zu diesem Zeitpunkt die alphanumerische Sortierung vergleichen, nicht die quantitativen Werte der Zahlen. Um eine Sortierung zu erhalten, die Ihren Erwartungen entspricht, können Sie Ihre Ordner mit Zahlen füllen ... ['002', '003', '004', '005', '403', '404', ' 405 ',' 406 ']
Andrew

2

Aus der Dokumentation :

Die Liste ist in beliebiger Reihenfolge und enthält keine speziellen Einträge '.' und '..' auch wenn sie im Verzeichnis vorhanden sind.

Dies bedeutet, dass die Reihenfolge wahrscheinlich vom Betriebssystem / Dateisystem abhängig ist, keine besonders aussagekräftige Reihenfolge aufweist und daher keine Garantie für eine bestimmte Reihenfolge besteht. Wie viele Antworten erwähnt: Falls gewünscht, kann die abgerufene Liste sortiert werden.

Prost :)


2

Elliots Antwort löst es perfekt, aber da es sich um einen Kommentar handelt, bleibt er unbemerkt. Mit dem Ziel, jemandem zu helfen, wiederhole ich ihn als Lösung.

Verwenden Sie die Natsort-Bibliothek:

Installieren Sie die Bibliothek mit dem folgenden Befehl für Ubuntu und andere Debian-Versionen

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

Details zur Verwendung dieser Bibliothek finden Sie hier


1
Das ist genauer als sorted()! Danke
Färid 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.

Dies erklärt, warum sie das Verhalten sehen, ohne eine Lösung anzubieten.
Daniel Watkins

1
OP will nur wissen warum, nicht wie.
Denis

@ Denis danke für den Hinweis - ich habe es vorher nicht bemerkt
Dimitris

@ DanielWatkins OK, nicht es ist nicht.)
Denis

0

Die vorgeschlagene Kombination von os.listdir und sortierten Befehlen generiert das gleiche Ergebnis wie der Befehl ls -l unter Linux. Das folgende Beispiel bestätigt diese Annahme:

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$ 

Für jemanden, der das Ergebnis des bekannten Befehls ls -l in seinem Python-Code reproduzieren möchte, funktioniert sortiert (os.listdir (DIR)) also ziemlich gut.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.