Làm cách nào để sắp xếp các chuỗi unicode theo thứ tự bảng chữ cái trong Python?


97

Python sắp xếp theo giá trị byte theo mặc định, có nghĩa là é đứng sau z và những thứ hài hước không kém khác. Cách tốt nhất để sắp xếp theo thứ tự bảng chữ cái trong Python là gì?

Có một thư viện cho điều này? Tôi không tìm thấy gì cả. Việc sắp xếp tốt hơn nên có hỗ trợ ngôn ngữ để nó hiểu rằng åäö nên được sắp xếp sau z trong tiếng Thụy Điển, nhưng ü đó nên được sắp xếp theo u, v.v. Do đó, hỗ trợ Unicode là một yêu cầu khá lớn.

Nếu không có thư viện cho nó, cách tốt nhất để làm điều này là gì? Chỉ cần thực hiện một ánh xạ từ ký tự đến một giá trị số nguyên và ánh xạ chuỗi thành một danh sách số nguyên với giá trị đó?


11
Lưu ý rằng điều này thậm chí còn phụ thuộc vào ngôn ngữ: Trong tiếng Thụy Điển (như bạn nêu) "Ä" đứng sau "Z", nhưng trong tiếng Đức, "Ä" thường được sắp xếp là "AE".
balpha

@Georg: Có lý do gì khiến bạn mở tiền thưởng cho việc này không? Câu locale.strcolltrả lời là đúng khi bạn cần phân loại Unicode bằng cách sử dụng ngôn ngữ của người dùng và ICU trả lời những gì bạn muốn khi bạn cần nhiều hơn thế (đối chiếu sử dụng nhiều ngôn ngữ). Hầu hết thời gian, bạn muốn locale.strcoll.
Glenn Maynard

@Glenn: Tôi muốn biết ICUlocale.strcoll hoạt động tốt như thế nào và đặc biệt là ICU làm gì tốt hơn hàm Python. Về cơ bản, một số chú ý hơn cho câu hỏi.
Georg Schölly

1
@Georg: Gần đây tôi đã chơi rất nhiều với Thuật toán đối chiếu Unicode, như bạn có thể thấy từ câu trả lời của tôi. Chẳng hạn, nó thực sự xuất sắc để có thể sắp xếp --locale=de__phonebookkhi bạn cần. Mô-đun Perl vượt qua bộ kiểm tra UCA và tập lệnh tôi cung cấp giúp việc chơi với toàn bộ UCA dễ dàng hơn rất nhiều cùng với tất cả các tùy chọn của nó bao gồm cả ngôn ngữ, chỉ từ dòng lệnh. Có thể không trả lời các câu hỏi, nhưng nó vẫn cần được đánh giá cao thú vị. Nếu bạn ở Thụy Sĩ, tôi chắc chắn rằng bạn có thể sử dụng tính linh hoạt. :)
tchrist

Câu trả lời:


75

Thư viện ICU của IBM thực hiện điều đó (và nhiều hơn nữa). Nó có các ràng buộc Python: PyICU .

Cập nhật : Sự khác biệt cốt lõi trong việc sắp xếp giữa ICU và locale.strcollICU sử dụng Thuật toán đối chiếu Unicode đầy đủ trong khi strcollsử dụng ISO 14651 .

Sự khác biệt giữa hai thuật toán đó được tóm tắt ngắn gọn tại đây: http://unicode.org/faq/collation.html#13 . Đây là những trường hợp đặc biệt khá kỳ lạ, hiếm khi quan trọng trong thực tế.

>>> import icu # pip install PyICU
>>> sorted(['a','b','c','ä'])
['a', 'b', 'c', 'ä']
>>> collator = icu.Collator.createInstance(icu.Locale('de_DE.UTF-8'))
>>> sorted(['a','b','c','ä'], key=collator.getSortKey)
['a', 'ä', 'b', 'c']

Điều này có hoạt động giống nhau cho Python 2 và Python 3 không? Tôi đã sử dụng locale.strxfrmtừ câu trả lời của u0b34a0f6ae và nó có vẻ hoạt động và thanh lịch hơn nhiều và không yêu cầu bất kỳ phần mềm bổ sung nào.
sup

Không hoạt động với Python3 đối với tôi, sudo pip3 install PyICUkhông cài đặt được và đối với Python2 cũng vậy.
imrek

Tôi đã phải cài đặt libicu-devel.x86_64 cho pyICU để biên dịch và cài đặt từ Pip. Nó hoạt động, mặc dù đầu ra từ lệnh 'sắp xếp' cuối cùng là: ['a', '\ xc3 \ xa4', 'b', 'c']
Mike Stoddart

53

Tôi không thấy điều này trong các câu trả lời. Ứng dụng của tôi sắp xếp theo ngôn ngữ bằng cách sử dụng thư viện chuẩn của python. Nó là khá dễ dàng.

# python2.5 code below
# corpus is our unicode() strings collection as a list
corpus = [u"Art", u"Älg", u"Ved", u"Wasa"]

import locale
# this reads the environment and inits the right locale
locale.setlocale(locale.LC_ALL, "")
# alternatively, (but it's bad to hardcode)
# locale.setlocale(locale.LC_ALL, "sv_SE.UTF-8")

corpus.sort(cmp=locale.strcoll)

# in python2.x, locale.strxfrm is broken and does not work for unicode strings
# in python3.x however:
# corpus.sort(key=locale.strxfrm)

Câu hỏi cho Lennart và những người trả lời khác: Không ai biết 'ngôn ngữ' hoặc không làm được nhiệm vụ này?


Nhân tiện 1) Tôi không nghĩ rằng locale.strxfrm bị hỏng đối với `str 'được mã hóa UTF-8; Tôi làm chuẩn bởi ứng dụng và kết luận rằng việc sử dụng CMP = strcoll trên các đối tượng unicode là rẻ hơn so với giải mã tất cả sang UTF-8 và sử dụng key = strxfrm
u0b34a0f6ae

6
Nhân tiện 2) Mô-đun ngôn ngữ sẽ chỉ hoạt động với các ngôn ngữ được tạo của bạn (đối với hộp Linux), không phải bất kỳ ngôn ngữ tùy ý nào. "locale -a" sẽ cho bạn biết
u0b34a0f6ae

6
@Georg: Tôi tin rằng ngôn ngữ chỉ hỗ trợ ánh xạ chuỗi con-> collating_element đơn giản. Nó không xử lý những thứ như mở rộng (æ được sắp xếp là "ae"), sắp xếp trọng âm tiếng Pháp (các chữ cái được sắp xếp từ trái sang phải, nhưng trọng âm từ phải sang trái), sắp xếp lại và có thể là một vài thứ nữa. Thông tin chi tiết tại đây (bộ tính năng UCA đầy đủ): unicode.org/reports/tr10 và tại đây (đối chiếu ngôn ngữ): chm.tu-dresden.de/edv/manuals/aix/files/aixfiles/LC_COLLATE.htm
Rafał

2
Để trả lời rõ ràng câu hỏi: Có, đó nhiệm vụ. Rõ ràng có một số trường hợp đặc biệt mà Thuật toán đối chiếu Unicode hoàn chỉnh xử lý tốt hơn, nhưng trừ khi bạn đã biết thì rất có thể bạn sẽ không nhận thấy.
Lennart Regebro

1
Vấn đề lớn nhất ở đây là: bạn phải đặt ngôn ngữ chung cho toàn bộ ứng dụng. - Bạn không thể chỉ có nó để so sánh trong tầm tay.
Robert Siemer

9

Hãy thử Thuật toán đối chiếu Unicode trong Python của James Tauber . Nó có thể không hoạt động chính xác như bạn muốn, nhưng có vẻ rất đáng để xem. Để biết thêm thông tin về các vấn đề, hãy xem bài đăng này của Christopher Lenz.


Điều đó ít nhất cũng khắc phục được vấn đề chung. Tôi đoán các phiên bản nhạy cảm với ngôn ngữ của danh sách đối chiếu cũng có thể được tạo.
Lennart Regebro

Điều này không cho phép bạn chỉ định ngôn ngữ và tệp cấu hình tham chiếu gây ra lỗi ValueError.
thebjorn

8

Bạn cũng có thể quan tâm đến pyuca :

http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/

Mặc dù nó chắc chắn không phải là cách chính xác nhất, nhưng nó là một cách rất đơn giản để ít nhất là làm đúng. Nó cũng đánh bại ngôn ngữ trong một ứng dụng web vì ngôn ngữ không an toàn cho chuỗi và đặt cài đặt ngôn ngữ trong toàn bộ quá trình. Nó cũng dễ thiết lập hơn PyICU dựa vào thư viện C bên ngoài.

Tôi đã tải tập lệnh lên github vì bản gốc đã bị lỗi tại thời điểm viết bài này và tôi phải sử dụng bộ nhớ đệm web để lấy nó:

https://github.com/href/Python-Unicode-Collation-Algorithm

Tôi đã sử dụng thành công tập lệnh này để sắp xếp thành thạo văn bản tiếng Đức / tiếng Pháp / tiếng Ý trong một mô-đun plone.


+1 cho pyuca. Nó khá nhanh (3 giây để sắp xếp 28000 từ), là python thuần túy và không yêu cầu phụ thuộc.
michaelmeyer

7

Một câu trả lời tóm tắt và mở rộng:

locale.strcolltrong Python 2, và locale.strxfrmtrên thực tế sẽ giải quyết được vấn đề và thực hiện tốt công việc, giả sử rằng bạn đã cài đặt ngôn ngữ được đề cập. Tôi cũng đã thử nghiệm nó trong Windows, nơi mà các tên miền khác nhau một cách khó hiểu, nhưng mặt khác, nó dường như có tất cả các ngôn ngữ được cài đặt theo mặc định.

ICUkhông nhất thiết làm điều này tốt hơn trong thực tế, tuy nhiên nó còn làm được nhiều hơn thế . Đáng chú ý nhất là nó có hỗ trợ cho bộ chia có thể chia văn bản bằng các ngôn ngữ khác nhau thành các từ. Điều này rất hữu ích cho các ngôn ngữ không có dấu tách từ. Bạn sẽ cần phải có một kho từ ngữ để sử dụng làm cơ sở cho việc phân tách, vì nó không được bao gồm.

Nó cũng có các tên dài cho các ngôn ngữ để bạn có thể nhận được các tên hiển thị đẹp cho ngôn ngữ, hỗ trợ cho các lịch khác ngoài Gregorian (mặc dù tôi không chắc giao diện Python có hỗ trợ điều đó) và rất nhiều hỗ trợ ngôn ngữ ít nhiều ít hiểu biết khác .

Vì vậy, tất cả: Nếu bạn muốn sắp xếp theo thứ tự bảng chữ cái và phụ thuộc vào ngôn ngữ, bạn có thể sử dụng localemô-đun, trừ khi bạn có yêu cầu đặc biệt hoặc cũng cần thêm chức năng phụ thuộc vào ngôn ngữ, như bộ tách từ.


6

Tôi thấy các câu trả lời đã hoàn thành một công việc xuất sắc, chỉ muốn chỉ ra một điểm kém hiệu quả trong việc viết mã trong Human Sort . Để áp dụng bản dịch char-by-char có chọn lọc cho một chuỗi unicode s, nó sử dụng mã:

spec_dict = {'Å':'A', 'Ä':'A'}

def spec_order(s):
    return ''.join([spec_dict.get(ch, ch) for ch in s])

Python có một cách tốt hơn, nhanh hơn và ngắn gọn hơn nhiều để thực hiện tác vụ phụ trợ này (trên chuỗi Unicode - phương thức tương tự cho chuỗi byte có một đặc điểm kỹ thuật khác và hơi ít hữu ích hơn! -):

spec_dict = dict((ord(k), spec_dict[k]) for k in spec_dict)

def spec_order(s):
    return s.translate(spec_dict)

Lệnh mà bạn chuyển đến translatephương thức có thứ tự Unicode (không phải chuỗi) làm khóa, đó là lý do tại sao chúng ta cần bước xây dựng lại từ char-to-char ban đầu spec_dict. (Các giá trị trong chính tả mà bạn chuyển để dịch [trái ngược với khóa, phải là thứ tự] có thể là thứ tự Unicode, chuỗi Unicode tùy ý hoặc Không có để xóa ký tự tương ứng như một phần của bản dịch, vì vậy, thật dễ dàng chỉ định "bỏ qua a ký tự nhất định cho mục đích phân loại "," ánh xạ ä cho ae cho mục đích phân loại ", và tương tự).

Trong Python 3, bạn có thể thực hiện bước "xây dựng lại" đơn giản hơn, ví dụ:

spec_dict = ''.maketrans(spec_dict)

Xem tài liệu để biết các cách khác mà bạn có thể sử dụng maketransphương thức tĩnh này trong Python 3.


Phương pháp này rất hay nhưng không cho phép bạn đặt á giữa az và b
Barney


1

Gần đây, tôi đã sử dụng zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) cho tác vụ này. Ví dụ: sắp xếp ß tiếng Đức:

>>> import zope.ucol
>>> collator = zope.ucol.Collator("de-de")
>>> mylist = [u"a", u'x', u'\u00DF']
>>> print mylist
[u'a', u'x', u'\xdf']
>>> print sorted(mylist, key=collator.key)
[u'a', u'\xdf', u'x']

zope.ucol cũng bao bọc ICU, vì vậy sẽ là một giải pháp thay thế cho PyICU.


1

Một giải pháp UCA hoàn chỉnh

Cách đơn giản nhất, dễ nhất và đơn giản nhất để thực hiện việc này là tạo chú thích cho mô-đun thư viện Perl, Unicode :: Collate :: Locale , là một lớp con của mô-đun Unicode :: Collate tiêu chuẩn . Tất cả những gì bạn cần làm là chuyển cho hàm tạo một giá trị ngôn ngữ của "xv"Thụy Điển.

(Bạn có thể không đánh giá cao điều này đối với văn bản tiếng Thụy Điển, nhưng vì Perl sử dụng các ký tự trừu tượng, bạn có thể sử dụng bất kỳ điểm mã Unicode nào mà bạn muốn - bất kể nền tảng hoặc phiên bản! Rất ít ngôn ngữ mang lại sự tiện lợi như vậy. Tôi đề cập đến điều đó vì tôi đã chiến đấu với gần đây đã thua trận với Java rất nhiều vì vấn đề khó chịu này.)

Vấn đề là tôi không biết cách truy cập mô-đun Perl từ Python - ngoài việc sử dụng chú thích shell hoặc đường ống hai cạnh. Vì vậy , tôi đã cung cấp cho bạn một tập lệnh làm việc hoàn chỉnh được gọi là ucsort mà bạn có thể gọi để thực hiện chính xác những gì bạn đã yêu cầu một cách hoàn toàn dễ dàng.

Tập lệnh này tuân thủ 100% với Thuật toán đối chiếu Unicode đầy đủ , với tất cả các tùy chọn chỉnh sửa được hỗ trợ !! Và nếu bạn đã cài đặt mô-đun tùy chọn hoặc chạy Perl 5.13 trở lên, thì bạn có toàn quyền truy cập vào các ngôn ngữ CLDR dễ sử dụng. Xem bên dưới.

Trình diễn

Hãy tưởng tượng một tập hợp đầu vào được sắp xếp theo cách này:

b o i j n l m å y e v s k h d f g t ö r x p z a ä c u q

Sắp xếp mặc định theo điểm mã mang lại:

a b c d e f g h i j k l m n o p q r s t u v x y z ä å ö

mà sách của mọi người không chính xác. Sử dụng tập lệnh của tôi, sử dụng Thuật toán đối chiếu Unicode, bạn nhận được thứ tự sau:

% perl ucsort /tmp/swedish_alphabet | fmt
a å ä b c d e f g h i j k l m n o ö p q r s t u v x y z

Đó là kiểu UCA mặc định. Để nhận ngôn ngữ Thụy Điển, hãy gọi ucsort theo cách này:

% perl ucsort --locale=sv /tmp/swedish_alphabet | fmt
a b c d e f g h i j k l m n o p q r s t u v x y z å ä ö

Đây là một bản demo đầu vào tốt hơn. Đầu tiên, bộ đầu vào:

% fmt /tmp/swedish_set
cTD cDD Cöd Cbd cAD cCD cYD Cud cZD Cod cBD Cnd cQD cFD Ced Cfd cOD
cLD cXD Cid Cpd cID Cgd cVD cMD cÅD cGD Cqd Cäd cJD Cdd Ckd cÖD cÄD
Ctd Czd Cxd cHD cND cKD Cvd Chd Cyd cUD Cld Cmd cED Crd Cad Cåd Ccd
cRD cSD Csd Cjd cPD

Theo điểm mã, sắp xếp theo cách này:

Cad Cbd Ccd Cdd Ced Cfd Cgd Chd Cid Cjd Ckd Cld Cmd Cnd Cod Cpd Cqd
Crd Csd Ctd Cud Cvd Cxd Cyd Czd Cäd Cåd Cöd cAD cBD cCD cDD cED cFD
cGD cHD cID cJD cKD cLD cMD cND cOD cPD cQD cRD cSD cTD cUD cVD cXD
cYD cZD cÄD cÅD cÖD

Nhưng việc sử dụng UCA mặc định làm cho nó sắp xếp theo cách này:

% ucsort /tmp/swedish_set | fmt
cAD Cad cÅD Cåd cÄD Cäd cBD Cbd cCD Ccd cDD Cdd cED Ced cFD Cfd cGD
Cgd cHD Chd cID Cid cJD Cjd cKD Ckd cLD Cld cMD Cmd cND Cnd cOD Cod
cÖD Cöd cPD Cpd cQD Cqd cRD Crd cSD Csd cTD Ctd cUD Cud cVD Cvd cXD
Cxd cYD Cyd cZD Czd

Nhưng trong ngôn ngữ Thụy Điển, theo cách này:

% ucsort --locale=sv /tmp/swedish_set | fmt
cAD Cad cBD Cbd cCD Ccd cDD Cdd cED Ced cFD Cfd cGD Cgd cHD Chd cID
Cid cJD Cjd cKD Ckd cLD Cld cMD Cmd cND Cnd cOD Cod cPD Cpd cQD Cqd
cRD Crd cSD Csd cTD Ctd cUD Cud cVD Cvd cXD Cxd cYD Cyd cZD Czd cÅD
Cåd cÄD Cäd cÖD Cöd

Nếu bạn muốn sắp xếp chữ hoa trước chữ thường, hãy làm như sau:

% ucsort --upper-before-lower --locale=sv /tmp/swedish_set | fmt
Cad cAD Cbd cBD Ccd cCD Cdd cDD Ced cED Cfd cFD Cgd cGD Chd cHD Cid
cID Cjd cJD Ckd cKD Cld cLD Cmd cMD Cnd cND Cod cOD Cpd cPD Cqd cQD
Crd cRD Csd cSD Ctd cTD Cud cUD Cvd cVD Cxd cXD Cyd cYD Czd cZD Cåd
cÅD Cäd cÄD Cöd cÖD

Các loại tùy chỉnh

Bạn có thể làm nhiều việc khác với ucsort . Ví dụ, đây là cách sắp xếp tiêu đề bằng tiếng Anh:

% ucsort --preprocess='s/^(an?|the)\s+//i' /tmp/titles
Anathem
The Book of Skulls
A Civil Campaign
The Claw of the Conciliator
The Demolished Man
Dune
An Early Dawn
The Faded Sun: Kesrith
The Fall of Hyperion
A Feast for Crows
Flowers for Algernon
The Forbidden Tower
Foundation and Empire
Foundations Edge
The Goblin Reservation
The High Crusade
Jack of Shadows
The Man in the High Castle
The Ringworld Engineers
The Robots of Dawn
A Storm of Swords
Stranger in a Strange Land
There Will Be Time
The White Dragon

Bạn sẽ cần Perl 5.10.1 hoặc cao hơn để chạy tập lệnh nói chung. Để được hỗ trợ ngôn ngữ, bạn phải cài đặt mô-đun CPAN tùy chọn Unicode::Collate::Locale. Ngoài ra, bạn có thể cài đặt phiên bản phát triển của Perl, 5.13+, bao gồm mô-đun đó theo tiêu chuẩn.

Quy ước gọi điện

Đây là một nguyên mẫu nhanh, vì vậy ucsort hầu như không được ghi lại bằng tài liệu. Nhưng đây là TỔNG HỢP của nó về những gì nó chấp nhận chuyển đổi / tùy chọn trên dòng lệnh:

    # standard options
    --help|?
    --man|m
    --debug|d

    # collator constructor options
    --backwards-levels=i
    --collation-level|level|l=i
    --katakana-before-hiragana
    --normalization|n=s
    --override-CJK=s
    --override-Hangul=s
    --preprocess|P=s
    --upper-before-lower|u
    --variable=s

    # program specific options
    --case-insensitive|insensitive|i
    --input-encoding|e=s
    --locale|L=s
    --paragraph|p
    --reverse-fields|last
    --reverse-output|r
    --right-to-left|reverse-input

Yeah, ok: đó thực sự là danh sách đối số mà tôi sử dụng cho cuộc gọi đến Getopt::Long, nhưng bạn hiểu rồi đấy. :)

Nếu bạn có thể tìm ra cách gọi trực tiếp các mô-đun thư viện Perl từ Python mà không cần gọi tập lệnh Perl, thì hãy làm như vậy. Tôi chỉ không biết bản thân mình như thế nào. Tôi muốn tìm hiểu cách làm.

Trong thời gian chờ đợi, tôi tin rằng tập lệnh này sẽ làm được những gì bạn cần làm trong tất cả những gì cụ thể của nó - và hơn thế nữa! Bây giờ tôi sử dụng cái này cho tất cả việc sắp xếp văn bản. Cuối cùngcũng làm được những gì tôi cần trong một thời gian dài.

Nhược điểm duy nhất là --localelập luận này khiến hiệu suất của các ống giảm xuống, mặc dù nó đủ nhanh để phân loại thông thường, không theo ngôn ngữ nhưng vẫn tuân thủ 100% UCA . Vì nó tải mọi thứ trong bộ nhớ, bạn có thể không muốn sử dụng nó trên các tài liệu gigabyte. Tôi sử dụng nó nhiều lần trong ngày và chắc chắn rằng cuối cùng nó rất tuyệt vời khi sắp xếp văn bản lành mạnh.


2
Tại sao bạn lại gọi một tập lệnh Perl để làm điều gì đó mà có các thư viện Python cho?
Lennart Regebro

2
Bởi vì tôi không biết có một thư viện Python, đó là lý do tại sao!
tchrist

@Lennart: Tôi thực sự thích các thư viện gốc hoặc nhiều nhất là các thư viện được liên kết với API C và được tải động (đôi khi bạn cần). Tôi không thấy các giải pháp PyPerl và Inline :: Perl khác nhau rất thuyết phục, mạnh mẽ hoặc linh hoạt. Hay gì đó. Họ cảm thấy không ổn vì một số lý do. Lần cuối cùng tôi đã thử điều này khi tôi cần phát hiện bộ ký tự tốt (mà tôi chưa bao giờ có được, than ôi).
tchrist

4
Sử dụng Perl bên trong Python chỉ là nghiện.
Utku Zihnioglu

1
Chà. Đúng vậy - trông giống như Perl đối với tôi, trên thực tế, chúng tôi thấy rằng bây giờ có nhiều hơn hai cách để thực hiện mọi thứ :) Nhưng việc gọi C từ Python nói chung không ngụ ý các loại phụ thuộc bổ sung và các vấn đề hỗ trợ thực tế mà việc gọi Perl sẽ làm, vì vậy thật khó để thấy nhiều lời kêu gọi làm theo cách này.
nealmcb

0

Nó còn xa mới là một giải pháp hoàn chỉnh cho trường hợp sử dụng của bạn, nhưng bạn có thể có một cái nhìn tại unaccent.py kịch bản từ effbot.org. Về cơ bản, nó làm là xóa tất cả các dấu trọng âm khỏi văn bản. Bạn có thể sử dụng văn bản 'được làm sạch' đó để sắp xếp theo thứ tự bảng chữ cái. (Đối với một mô tả tốt hơn thấy này trang.)


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.