Thứ tự danh sách không phải chữ và số từ os.listdir ()


108

Tôi thường sử dụng python để xử lý thư mục dữ liệu. Gần đây, tôi nhận thấy rằng thứ tự mặc định của danh sách đã thay đổi thành một thứ gì đó gần như vô nghĩa. Ví dụ: nếu tôi đang ở trong thư mục hiện tại có chứa các thư mục con sau: run01, run02, ... run19, run20, sau đó tôi tạo danh sách từ lệnh sau:

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

thì tôi thường nhận được một danh sách theo thứ tự sau:

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

và như thế. Thứ tự từng là chữ và số. Nhưng đơn đặt hàng mới này vẫn còn với tôi trong một thời gian.

Điều gì là xác định thứ tự (hiển thị) của các danh sách này?


Thứ tự trong danh sách python thực sự có liên quan (tức là danh sách được sắp xếp theo thứ tự). Tôi đồng ý với Nowayz: thứ tự kỳ lạ mà bạn đang thấy có thể là một chức năng của hệ thống tệp. Tôi đã thấy điều này xảy ra vài năm trước với hệ thống tệp mạng của bên thứ 3 được đính kèm vào máy mac.
David P Simons

Cảm ơn bạn vì thông tin, tôi đã xóa nhận xét về thứ tự danh sách.
marshall.ward

@ shog9 Ok, bây giờ tôi có thể thấy rằng câu hỏi đã được hỏi và đã được trả lời (cách sắp xếp dữ liệu chưa bao giờ được cung cấp trong câu trả lời được liên kết) nhưng chủ đề câu hỏi không rõ ràng lắm (thực hiện tìm kiếm mà câu trả lời không xuất hiện) và các thẻ không hữu ích lắm
Dimitris

@Dimitris: đó là một lời chỉ trích công bằng - Tôi đã sắp xếp lại câu hỏi này và hợp nhất hai câu hỏi, vì vậy bây giờ cả hai bộ câu trả lời có thể được tìm thấy ở đây và của bạn vẫn đang chỉ ra nó.
Shog9

BTW nếu bất kỳ ai khác cũng bối rối như tôi về các câu trả lời ở đây, đó là bởi vì câu hỏi của tôi đã được hợp nhất với một câu hỏi khác yêu cầu listdirđầu ra được sắp xếp . Tôi không chắc tại sao các câu hỏi được hợp nhất.
marshall.ward

Câu trả lời:


63

Tôi nghĩ thứ tự liên quan đến cách các tệp được lập chỉ mục trên FileSystem của bạn. Nếu bạn thực sự muốn làm cho nó tuân theo một số thứ tự, bạn luôn có thể sắp xếp danh sách sau khi nhận được tệp.


128

Bạn có thể sử dụng sortedhàm nội trang để sắp xếp các chuỗi theo cách bạn muốn. Dựa trên những gì bạn mô tả,

sorted(os.listdir(whatever_directory))

Ngoài ra, bạn có thể sử dụng .sortphương pháp danh sách:

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

Tôi nghĩ nên làm thủ thuật.

Lưu ý rằng thứ tự os.listdirlấy tên tệp có thể hoàn toàn phụ thuộc vào hệ thống tệp của bạn.


1
Không thay đổi thứ tự nếu xử lý tên tệp đầu số (nghĩa là 59,9780radps-0096 vẫn trước 9,9746radps-0082). Tôi nghĩ rằng đó là bởi vì mọi thứ là một chuỗi, vì vậy số thập phân không được xử lý đúng cách.
Elliot

2
Hoặc sử dụng thư viện natsort, mà tôi vừa tìm thấy.
Elliot

5
Chỉ sorted(listdir)làm việc cho tôi. listdir.sort()đã cho tôi: TypeError: Không thể lặp lại đối tượng 'NoneType'
paul_h 11/11/17

1
@AlexB - chắc chắn ... chỉ cần vượt qua reverse=Trueđể sắp xếp giảm dần.
mgilson,

1
@ user3895596 - Tôi nghĩ rằng sortedthứ được viết đầu tiên nó nằm trong một dòng duy nhất OK?
mgilson

43

Theo tài liệu :

os.listdir (đường dẫn)

Trả về một danh sách có chứa tên của các mục trong thư mục được cung cấp bởi đường dẫn. Danh sách theo thứ tự tùy ý . Nó không bao gồm các mục đặc biệt '.' và '..' ngay cả khi chúng có trong thư mục.

Không thể dựa vào thứ tự và là một cấu phần của hệ thống tập tin.

Để sắp xếp kết quả, hãy sử dụng sorted(os.listdir(path)).


26

Python vì bất kỳ lý do gì không đi kèm với một cách tích hợp để sắp xếp tự nhiên (nghĩa là 1, 2, 10 thay vì 1, 10, 2), vì vậy bạn phải tự viết nó:

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)

Bây giờ bạn có thể sử dụng chức năng này để sắp xếp danh sách:

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

VẤN ĐỀ: Trong trường hợp bạn sử dụng hàm trên để sắp xếp các chuỗi (ví dụ: tên thư mục) và muốn chúng được sắp xếp giống như Windows Explorer, nó sẽ không hoạt động bình thường trong một số trường hợp cạnh.
Chức năng sắp xếp này sẽ trả về kết quả không chính xác trên Windows, nếu bạn có tên thư mục với các ký tự 'đặc biệt' nhất định trong đó. Ví dụ, hàm này sẽ sắp xếp 1, !1, !a, a, trong khi Windows Explorer sẽ sắp xếp !1, 1, !a, a.

Vì vậy, nếu bạn muốn sắp xếp chính xác như Windows Explorer làm trong Python, bạn phải sử dụng hàm tích hợp sẵn của Windows StrCmpLogicalW thông qua ctypes (điều này tất nhiên sẽ không hoạt động trên 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))

Chức năng này hơi chậm hơn sorted_alphanumeric().

Phần thưởng: winsortcũng có thể sắp xếp các đường dẫn đầy đủ trên Windows .

Ngoài ra, đặc biệt nếu bạn sử dụng Unix, bạn có thể sử dụng natsortthư viện ( pip install natsort) để sắp xếp theo các đường dẫn đầy đủ theo cách chính xác (nghĩa là các thư mục con ở đúng vị trí).

Bạn có thể sử dụng nó như thế này để sắp xếp các đường dẫn đầy đủ:

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

Không sử dụng nó để phân loại thông thường chỉ tên thư mục (hoặc chuỗi nói chung), vì nó chậm hơn một chút so với sorted_alphanumeric()chức năng ở trên.
natsortedthư viện sẽ cung cấp cho bạn kết quả không chính xác nếu bạn muốn Windows Explorer sắp xếp, vì vậy hãy sử dụng winsort()cho điều đó.


Hoạt động hoàn toàn tốt. print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']. Đúng như mong đợi.
user136036

Có một vấn đề mở từ lâu về natsortedviệc triển khai chức năng phù hợp với Windows Explorer. Có lẽ bạn nên đóng góp một giải pháp? github.com/SethMMorton/natsort/issues/41
SethMMorton

8

Tôi nghĩ theo mặc định, thứ tự được xác định bằng giá trị ASCII. Giải pháp cho vấn đề này là

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

5

Nó có lẽ chỉ là đơn đặt hàng mà C readdir()trả lại. Hãy thử chạy chương trình C này:

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

Dòng xây dựng phải giống như thế gcc -o foo foo.c.

PS Chỉ chạy cái này và mã Python của bạn, và cả hai đều cung cấp cho tôi đầu ra được sắp xếp, vì vậy tôi không thể tái tạo những gì bạn đang thấy.


1
Lý do mà bạn nhìn thấy đầu ra soted thể phụ thuộc vào rất nhiều yếu tố, chẳng hạn như hệ điều hành, hệ thống tập tin, thời điểm tạo ra các tập tin, hành động trong thời gian chống phân mảnh cuối cùng, ...
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]))

Trong trường hợp yêu cầu của tôi, tôi có trường hợp như row_163.pklở đây os.path.splitext('row_163.pkl')sẽ chia nó thành ('row_163', '.pkl')vì vậy cần phải chia nó dựa trên '_' cũng được.

nhưng trong trường hợp yêu cầu của bạn, bạn có thể làm điều gì đó như

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

Ở đâu

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

và cũng để truy xuất thư mục, bạn có thể làm sorted(os.listdir(path))

và đối với trường hợp thích 'run01.txt'hoặc 'run01.csv'bạn có thể làm như thế này

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

2

Tôi thấy "sắp xếp" không phải lúc nào cũng làm được những gì tôi mong đợi. Ví dụ: tôi có một thư mục như bên dưới, và "sort" cho tôi một kết quả rất lạ:

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

Có vẻ như nó so sánh ký tự đầu tiên trước, nếu đó là ký tự lớn nhất, nó sẽ là ký tự cuối cùng.


2
Đây là hành vi được mong đợi. ('5' > '403') is True.
AXO

2
@AXO đúng, vì tại thời điểm này, bạn đang so sánh sắp xếp chữ và số, không phải giá trị định lượng của các số. Để có được một loại tương tự như mong đợi của bạn, bạn có thể muốn sử dụng đệm số trên các thư mục của mình ... ['002', '003', '004', '005', '403', '404', ' 405 ',' 406 ']
Andrew

2

Từ tài liệu :

Danh sách theo thứ tự tùy ý và không bao gồm các mục đặc biệt '.' và '..' ngay cả khi chúng có trong thư mục.

Điều này có nghĩa là thứ tự có thể phụ thuộc vào hệ điều hành / hệ thống tệp, không có thứ tự đặc biệt có ý nghĩa và do đó không được đảm bảo là bất kỳ thứ gì cụ thể. Như nhiều câu trả lời đã đề cập: nếu được ưu tiên, danh sách truy xuất có thể được sắp xếp.

Chúc mừng :)


2

Câu trả lời của Elliot giải quyết nó một cách hoàn hảo nhưng vì nó là một bình luận, nó không được chú ý vì vậy với mục đích giúp đỡ ai đó, tôi nhắc lại nó như một giải pháp.

Sử dụng thư viện natsort:

Cài đặt thư viện bằng lệnh sau cho Ubuntu và các phiên bản Debian khác

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

Chi tiết về cách sử dụng thư viện này xem tại đây


1
Đó là chính xác hơn sorted()! Cảm ơn
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.

Điều này giải thích tại sao họ đang nhìn thấy hành vi mà không đưa ra giải pháp.
Daniel Watkins

1
OP chỉ muốn biết tại sao, không phải như thế nào.
Denis

@Denis nhờ để chỉ ra điều này - tôi không để ý nó trước
Dimitris

@DanielWatkins OK, không phải vậy.)
Denis

0

Sự kết hợp được đề xuất giữa os.listdir và các lệnh đã sắp xếp tạo ra kết quả tương tự như lệnh ls -l trong Linux. Ví dụ sau xác minh giả định này:

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$ 

Vì vậy, đối với những người muốn tạo lại kết quả của lệnh ls -l nổi tiếng trong mã Python của mình, sắp xếp (os.listdir (DIR)) hoạt động khá tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.