ลำดับรายการที่ไม่ใช่ตัวเลขและตัวอักษรจาก os.listdir ()


111

ฉันมักใช้ python เพื่อประมวลผลไดเรกทอรีของข้อมูล เมื่อเร็ว ๆ นี้ฉันสังเกตเห็นว่าลำดับเริ่มต้นของรายการได้เปลี่ยนไปเป็นสิ่งที่เกือบจะไร้สาระ ตัวอย่างเช่นถ้าฉันอยู่ในไดเร็กทอรีปัจจุบันที่มีไดเร็กทอรีย่อยต่อไปนี้: run01, run02, ... run19, run20 จากนั้นฉันจะสร้างรายการจากคำสั่งต่อไปนี้:

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

จากนั้นฉันมักจะได้รับรายการตามลำดับนี้:

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

และอื่น ๆ คำสั่งที่ใช้เป็นตัวเลขและตัวอักษร แต่คำสั่งซื้อใหม่นี้ยังคงอยู่กับฉันมาระยะหนึ่งแล้ว

อะไรคือการกำหนดลำดับ (แสดง) ของรายการเหล่านี้


คำสั่งซื้อภายในรายการหลามมีความเกี่ยวข้องจริง (เช่นรายการจะเรียงลำดับ) ฉันเห็นด้วยกับ Nowayz: ลำดับแปลก ๆ ที่คุณเห็นน่าจะเป็นหน้าที่ของระบบไฟล์ ฉันเห็นสิ่งนี้เกิดขึ้นเมื่อสองสามปีก่อนพร้อมกับระบบไฟล์เครือข่ายของบุคคลที่สามที่แนบมากับ mac
David P Simons

ขอบคุณสำหรับข้อมูลฉันได้ลบความคิดเห็นลำดับรายการแล้ว
marshall.ward

@ shog9 ตกลงตอนนี้ฉันเห็นว่ามีการถามคำถามและประเภทของคำตอบ (ไม่เคยให้วิธีการเรียงลำดับข้อมูลในคำตอบที่เชื่อมโยง) แต่หัวข้อคำถามไม่ชัดเจนมากนัก (ทำการค้นหาคำตอบไม่ปรากฏ) และแท็กก็ไม่เป็นประโยชน์
Dimitris

@ Dimitris: นั่นเป็นการวิจารณ์ที่ยุติธรรม - ฉันตั้งชื่อคำถามนี้ใหม่และรวมสองคำถามเข้าด้วยกันดังนั้นตอนนี้คำตอบทั้งสองชุดสามารถพบได้ที่นี่และของคุณยังคงชี้ไปที่มัน
Shog9

BTW หากใครก็ตามที่สับสนเหมือนกับฉันเกี่ยวกับคำตอบที่นี่เป็นเพราะคำถามของฉันถูกรวมเข้ากับคำถามอื่นที่ขอlistdirผลลัพธ์ที่เรียงลำดับ ฉันไม่แน่ใจว่าทำไมจึงรวมคำถามเข้าด้วยกัน
marshall ตั้งแต่

คำตอบ:


62

ฉันคิดว่าคำสั่งนั้นเกี่ยวข้องกับวิธีการจัดทำดัชนีไฟล์บน FileSystem ของคุณ หากคุณต้องการให้เป็นไปตามลำดับคุณสามารถจัดเรียงรายการหลังจากได้รับไฟล์แล้ว


131

คุณสามารถใช้sortedฟังก์ชันbuiltin เพื่อจัดเรียงสตริงตามที่คุณต้องการ จากสิ่งที่คุณอธิบาย

sorted(os.listdir(whatever_directory))

หรือคุณสามารถใช้.sortวิธีการของรายการ:

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

ฉันคิดว่าควรทำเคล็ดลับ

โปรดทราบว่าลำดับที่os.listdirได้รับชื่อไฟล์อาจขึ้นอยู่กับระบบไฟล์ของคุณอย่างสมบูรณ์


1
ไม่เปลี่ยนลำดับหากจัดการกับชื่อไฟล์ที่เป็นตัวเลข (เช่น 59.9780radps-0096 ยังอยู่ก่อน 9.9746radps-0082) ฉันคิดว่าเป็นเพราะทุกอย่างเป็นสตริงดังนั้นทศนิยมจึงไม่ได้รับการปฏิบัติอย่างถูกต้อง
Elliot

2
หรือใช้ไลบรารี natsort ซึ่งฉันเพิ่งพบ
Elliot

5
เพียงsorted(listdir)ทำงานให้ฉัน listdir.sort()ให้ฉัน: TypeError: วัตถุ 'NoneType' ไม่สามารถทำซ้ำได้
paul_h

1
@AlexB - แน่นอน ... แค่ผ่านreverse=Trueเพื่อให้เรียงจากมากไปหาน้อย
mgilson

1
@ user3895596 - ฉันคิดว่าsortedสิ่งที่เขียนก่อนเป็นบรรทัดเดียวตกลงไหม
mgilson

43

ตามเอกสารประกอบ :

os.listdir (เส้นทาง)

ส่งคืนรายการที่มีชื่อของรายการในไดเร็กทอรีที่กำหนดโดยพา ธ รายการที่อยู่ในลำดับโดยพลการ ไม่รวมรายการพิเศษ '.' และ ".. " แม้ว่าจะมีอยู่ในไดเร็กทอรีก็ตาม

ไม่สามารถพึ่งพาคำสั่งได้และเป็นส่วนของระบบไฟล์

sorted(os.listdir(path))เพื่อจัดเรียงผลการใช้


28

Python ไม่ว่าด้วยเหตุผลใดก็ตามไม่ได้มาพร้อมกับวิธีการเรียงลำดับตามธรรมชาติในตัว (หมายถึง 1, 2, 10 แทนที่จะเป็น 1, 10, 2) ดังนั้นคุณต้องเขียนเอง:

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 Explorer จะทำงานไม่ถูกต้องในบางกรณีขอบ
ฟังก์ชันการเรียงลำดับนี้จะแสดงผลลัพธ์ที่ไม่ถูกต้องใน Windows หากคุณมีชื่อโฟลเดอร์ที่มีอักขระ 'พิเศษ' บางตัวอยู่ ตัวอย่างเช่นฟังก์ชันนี้จะจัดเรียง1, !1, !a, aในขณะที่ Windows Explorer จะเรียงลำดับ!1, 1, !a, aจะจัดเรียง

ดังนั้นหากคุณต้องการเรียงลำดับเหมือนกับที่ Windows Explorer ทำใน Pythonคุณต้องใช้ฟังก์ชันStrCmpLogicalWในตัวของ Windows ผ่าน ctypes (แน่นอนว่าจะใช้ไม่ได้กับ 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ยังสามารถจัดเรียงเส้นทางแบบเต็มบน WindowsWindows

หรือโดยเฉพาะอย่างยิ่งถ้าคุณใช้ Unix คุณสามารถใช้natsortไลบรารี (pip install natsort ) เพื่อจัดเรียงตามเส้นทางแบบเต็มได้อย่างถูกต้อง (หมายถึงโฟลเดอร์ย่อยในตำแหน่งที่ถูกต้อง)

คุณสามารถใช้วิธีนี้เพื่อจัดเรียงเส้นทางแบบเต็ม:

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

อย่าใช้สำหรับการเรียงลำดับชื่อโฟลเดอร์ตามปกติ (หรือสตริงโดยทั่วไป) เนื่องจากทำงานช้ากว่าsorted_alphanumeric()ฟังก์ชันด้านบนเล็กน้อย
natsortedไลบรารีจะให้ผลลัพธ์ที่ไม่ถูกต้องหากคุณคาดว่าจะมีการเรียงลำดับของ Windows Explorer ดังนั้นใช้winsort()สำหรับสิ่งนั้น


ทำงานได้ดีอย่างสมบูรณ์ print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']. ตรงตามที่คาดไว้
user136036

มีปัญหาในการเปิดnatsortedใช้งานฟังก์ชันการจับคู่ Windows Explorer มานาน บางทีคุณควรมีส่วนร่วมในการแก้ปัญหา? github.com/SethMMorton/natsort/issues/41
SethMMorton

9

ฉันคิดว่าโดยค่าเริ่มต้นคำสั่งจะถูกกำหนดด้วยค่า 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.

ป.ล. เพียงแค่รันสิ่งนี้และโค้ด Python ของคุณและทั้งคู่ก็ให้เอาต์พุตที่เรียงลำดับแล้วดังนั้นฉันจึงไม่สามารถจำลองสิ่งที่คุณเห็นได้


1
สาเหตุที่คุณเห็นผลลัพธ์ที่คัดแยกออกมาอาจขึ้นอยู่กับปัจจัยหลายอย่างเช่นระบบปฏิบัติการระบบไฟล์เวลาในการสร้างไฟล์การดำเนินการระหว่างการจัดเรียงข้อมูลครั้งล่าสุด ...
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]))

ในกรณีของความต้องการของฉันฉันมีกรณีเช่น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]))

คำตอบที่ดีที่สุดอย่างไม่ต้องสงสัยที่นี่
Amit Amola

2

ฉันพบว่า "เรียงลำดับ" ไม่ได้ทำตามที่คาดหวังไว้เสมอไป เช่นฉันมีไดเร็กทอรีด้านล่างและ "sort" ให้ผลลัพธ์ที่แปลกมาก:

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

จากเอกสารประกอบ :

รายการเป็นไปตามลำดับโดยพลการและไม่รวมรายการพิเศษ "." และ ".. " แม้ว่าจะมีอยู่ในไดเร็กทอรีก็ตาม

ซึ่งหมายความว่าลำดับอาจขึ้นอยู่กับระบบปฏิบัติการ / ระบบไฟล์ไม่มีลำดับที่มีความหมายเป็นพิเศษดังนั้นจึงไม่รับประกันว่าจะเป็นอะไรโดยเฉพาะ ดังที่ได้กล่าวถึงหลายคำตอบ: หากต้องการคุณสามารถจัดเรียงรายการที่ดึงข้อมูลได้

ไชโย :)


2

คำตอบของ Elliotช่วยแก้ปัญหาได้อย่างสมบูรณ์แบบ แต่เนื่องจากเป็นความคิดเห็นจึงไม่มีใครสังเกตเห็นโดยมีจุดประสงค์เพื่อช่วยเหลือใครบางคนฉันจึงย้ำว่าเป็นวิธีแก้ปัญหา

ใช้ไลบรารี natsort:

ติดตั้งไลบรารีด้วยคำสั่งต่อไปนี้สำหรับ Ubuntu และ Debian เวอร์ชันอื่น ๆ

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

รายละเอียดวิธีการใช้ห้องสมุดนี้มีอยู่ที่นี่


1
ที่เป๊ะปังกว่าsorted()! ขอบคุณ
Färid Alijani

1

การรวมos.listdirและsortedคำสั่งที่นำเสนอจะสร้างผลลัพธ์เช่นเดียวกับls -lคำสั่งภายใต้ Linux ตัวอย่างต่อไปนี้ยืนยันข้อสันนิษฐานนี้:

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$ 

ดังนั้นสำหรับคนที่ต้องการสร้างผลลัพธ์ของls -lคำสั่งที่รู้จักกันดีในโค้ด python ก็sorted( os.listdir( DIR ) )ใช้ได้ดีทีเดียว


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
สิ่งนี้อธิบายว่าเหตุใดพวกเขาจึงเห็นพฤติกรรมโดยไม่เสนอวิธีแก้ปัญหา
Daniel Watkins

1
OP แค่อยากรู้ว่าทำไมไม่ใช่อย่างไร
เดนิส

@ เดนิสขอบคุณที่ชี้ให้เห็น - ฉันไม่เคยสังเกตมาก่อน
Dimitris

@DanielWatkins ตกลงไม่ใช่มันไม่ใช่)
เดนิส
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.