Python'da unicode dizelerini alfabetik olarak nasıl sıralayabilirim?


99

Python varsayılan olarak bayt değerine göre sıralar, yani é, z ve diğer eşit derecede komik şeylerden sonra gelir. Python'da alfabetik olarak sıralamanın en iyi yolu nedir?

Bunun için bir kütüphane var mı? Hiçbir şey bulamadım. Tercihen sıralama dil desteğine sahip olmalıdır, böylece åäö'nun İsveççe'de z'den sonra sıralanması gerektiğini, ancak ü'nin u'ya göre sıralanması gerektiğini anlar. Unicode desteği bu nedenle hemen hemen bir gerekliliktir.

Kitaplık yoksa, bunu yapmanın en iyi yolu nedir? Sadece harften tamsayı değerine bir eşleme yapın ve dizeyi bununla bir tamsayı listesine eşleyin.


11
Bunun yerel ayara daha da bağlı olduğuna dikkat edin: İsveççe'de (belirttiğiniz gibi) "Ä", "Z" den sonra gelir, ancak Almanca'da "Ä" genellikle "AE" olarak sıralanır.
balpha

@Georg: Bunun üzerine bir ödül açmanızın bir nedeni var mıydı? locale.strcollCevap Unicode kullanıcının yerel kullanılarak sıralama gerektiğinde doğru olduğunu ve bundan daha (birden fazla yerel kullanılarak harmanlama) gerektiğinde istediğini Yoğun bakım cevap. Çoğu zaman istiyorsun locale.strcoll.
Glenn Maynard

@Glenn: Ben ne kadar iyi bilmek istedim locale.strcollçalışır ve özellikle neyi yoğun bakımda Python fonksiyonu daha iyi yapar. Temelde soruya biraz daha dikkat.
Georg Schölly

1
@Georg: Cevabımdan da görebileceğiniz gibi, son zamanlarda Unicode Harmanlama Algoritması ile çok oynuyorum. Örneğin, --locale=de__phonebookihtiyaç duyduğunuzda sıralayabilmek gerçekten mükemmeldir . Perl modülü, UCA test paketini geçer ve sağladığım komut dosyası , yalnızca komut satırından tüm UCA ve yerel ayarlar dahil tüm seçenekleriyle oynamayı çok daha kolaylaştırır . Cevaplamayabilir soru, ama yine de son derece ilginç olmalı. İsviçre'deyseniz, esnekliği kullanabileceğinizden eminim. :)
tchrist

Yanıtlar:


75

IBM'in ICU kitaplığı bunu (ve çok daha fazlasını) yapar. Python bağlamalarına sahiptir: PyICU .

Güncelleme : Yoğun bakımda arasındaki sıralama içinde çekirdek farkı ve locale.strcollyoğun bakımda tam kullanmasıdır Unicode Harmanlama Algoritması ise strcollkullanımları ISO 14651 .

Bu iki algoritma arasındaki farklar burada kısaca özetlenmiştir: http://unicode.org/faq/collation.html#13 . Bunlar, pratikte nadiren önemli olması gereken oldukça egzotik özel durumlardır.

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

Bu, Python 2 ve Python 3 için aynı mı çalışır? Kullandığım locale.strxfrmu0b34a0f6ae tarafından cevaptan ve işin görünüyor ve çok daha zarif ve herhangi bir ek yazılım gerektirmez.
sup

Benim sudo pip3 install PyICUiçin Python3 ile çalışmıyor, yüklenemiyor ve Python2 için de çalışıyor.
imrek

PyICU'nun Pip'ten derlemesi ve kurması için libicu-devel.x86_64'ü kurmam gerekiyordu. Son 'sıralı' komutun çıktısı: ['a', '\ xc3 \ xa4', 'b', 'c'] olmasına rağmen çalışır
Mike Stoddart

54

Bunu cevaplarda göremiyorum. Uygulamam, python'un standart kitaplığını kullanarak yerel ayara göre sıralar. Oldukça kolaydır.

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

Lennart ve diğer cevaplayıcılara soru: 'Yerel' bilen yok mu yoksa bu göreve bağlı değil mi?


Bu arada 1) locale.strxfrm'nin UTF-8 kodlu `str 'için bozuk olduğunu düşünmüyorum; Uygulama tarafından kıyaslandım ve unicode nesnelerde cmp = strcoll kullanmanın, hepsini UTF-8'e
kodlamaktan

6
Bu arada 2) Yerel ayar modülü herhangi bir rastgele yerel ayar ile değil, yalnızca sizin oluşturduğunuz yerel ayarlarla (bir Linux kutusu için) çalışacaktır. "locale -a" size hangisini söyleyecektir
u0b34a0f6ae

6
@Georg: Yerel ayarın yalnızca basit bir alt dize-> harmanlama_elgesi eşlemesini desteklediğine inanıyorum. Genişletmeler (æ "ae" olarak sıralanır), Fransız aksanı sıralama (harfler soldan sağa sıralanır, ancak sağdan sola aksanlar), yeniden düzenleme ve muhtemelen birkaç tane daha. Ayrıntılar burada (tam UCA özellik seti): unicode.org/reports/tr10 ve burada (yerel ayar harmanlaması): chm.tu-dresden.de/edv/manuals/aix/files/aixfiles/LC_COLLATE.htm
Rafał Dowgird

3
Açıkça soruya cevap vermek için: Evet ise göreve hazır. Görünüşe göre, Unicode Harmanlama Algoritmasının tamamının daha iyi işlediği bazı özel durumlar var, ancak şansın farkına varmayacağınızı zaten bilmiyorsanız.
Lennart Regebro

1
Buradaki en büyük sorun şudur: tüm uygulama için yerel ayarı global olarak ayarlamanız gerekir. - Karşılaştırma için elinizde tutamazsınız.
Robert Siemer

9

James Tauber'ın Python Unicode Harmanlama Algoritmasını deneyin . Tam olarak istediğiniz gibi yapmayabilir, ancak bir göz atmaya değer görünüyor. Sorunlar hakkında biraz daha fazla bilgi için Christopher Lenz tarafından yazılan bu gönderiye bakın.


Bu en azından genel sorunu düzeltir. Sanırım harmanlama listesinin dile duyarlı sürümleri de oluşturulabilir.
Lennart Regebro

Bu, yerel ayarı belirlemenize izin vermez ve referans yapılandırma dosyası bir ValueError'a neden olur.
thebjorn

8

Pyuca ile de ilgilenebilirsiniz :

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

Kesinlikle en kesin yol olmasa da, en azından biraz doğru yapmanın çok basit bir yolu. Ayrıca, yerel ayarlar iş parçacığı açısından güvenli olmadığından bir web uygulamasında yerel ayarı yener ve süreç boyunca dil ayarlarını belirler. Ayrıca harici bir C kitaplığına dayanan PyICU'dan daha kolay kurulum.

Komut dosyasını github'a yükledim, çünkü bu yazı yazılırken orijinali kapalıydı ve onu almak için web önbelleklerine başvurmak zorunda kaldım:

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

Bir plone modülünde Almanca / Fransızca / İtalyanca metinleri makul bir şekilde sıralamak için bu komut dosyasını başarıyla kullandım.


Pyuca için +1. Oldukça hızlıdır (28000 kelimeyi sıralamak için 3 saniye), saf python ve bağımlılık gerektirmez.
michaelmeyer

7

Özet ve genişletilmiş yanıt:

locale.strcollPython 2 altında, ve locale.strxfrmaslında sorunu çözecek ve söz konusu yerel ayarın kurulu olduğunu varsayarak iyi bir iş çıkaracaktır. Yerel adların kafa karıştırıcı bir şekilde farklı olduğu Windows altında da test ettim, ancak diğer yandan varsayılan olarak desteklenen tüm yerel ayarlara sahip görünüyor.

ICUbunu pratikte daha iyi yapmaz, ancak çok daha fazlasını yapar . En önemlisi, farklı dillerdeki metinleri kelimelere ayırabilen ayırıcılar için desteğe sahiptir. Bu, kelime ayırıcıları olmayan diller için çok kullanışlıdır. Bölme için bir temel olarak kullanmak için bir kelime topluluğuna ihtiyacınız olacak, çünkü bu dahil değil.

Ayrıca, yerel ayarlar için uzun adlara sahiptir, böylece yerel ayarlar için güzel görünen adlar elde edebilirsiniz, Gregoryen'den başka takvimler için destek (Python arayüzünün bunu desteklediğinden emin değilim) ve tonlarca diğer az veya çok belirsiz yerel ayarı destekler .

Sonuç olarak: Alfabetik ve yerel ayara bağlı olarak sıralamak localeistiyorsanız, özel gereksinimleriniz yoksa veya sözcük ayırıcı gibi yerel ayara bağlı daha fazla işlevselliğe ihtiyacınız yoksa modülü kullanabilirsiniz .


6

Cevapların zaten mükemmel bir iş çıkardığını görüyorum, sadece İnsan Sıralamasında bir kodlama verimsizliğine işaret etmek istedim . Seçmeli bir karakter karakter çevirisini bir unicode dizgesine uygulamak için şu kodu kullanır:

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

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

Python, bu yardımcı görevi gerçekleştirmek için çok daha iyi, daha hızlı ve daha özlü bir yola sahiptir (Unicode dizelerinde - bayt dizeleri için benzer yöntem, farklı ve biraz daha az yararlı bir özelliğe sahiptir! -):

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

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

Eğer geçmek dict translateyöntemiyle orijinal odun kömüründe to-char olduğunu yeniden inşa adımı neden ihtiyaç olan anahtarları olarak Unicode sıra sayılarını (değil dizeleri) sahiptir spec_dict. (Çeviriye ilettiğiniz komuttaki değerler [sıra sayıları olması gereken anahtarların aksine] Unicode sıra değerleri, rastgele Unicode dizeleri veya Çevirinin bir parçası olarak karşılık gelen karakteri kaldırmak için Hiçbiri olabilir, bu nedenle belirtmek kolaydır " sıralama amaçları için belirli karakterler "," sıralama amaçları için ae ile eşleme "ve benzeri).

Python 3'te "yeniden oluşturma" adımını daha basit bir şekilde alabilirsiniz, örneğin:

spec_dict = ''.maketrans(spec_dict)

Python 3'te bu statik yöntemi kullanmanın diğer yolları için belgelere bakın maketrans.


Bu yöntem güzel ancak az ve b arasına yerleştirmenize izin vermiyor
Barney Szabolcs


1

Son zamanlarda bu görev için zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) kullanıyorum . Örneğin, Almanca ß sıralamak:

>>> 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 ayrıca Yoğun Bakım Ünitesini de kapsar, bu nedenle PyICU'ya bir alternatif olur.


1

Eksiksiz bir UCA Çözümü

Bunu yapmanın en basit, en kolay ve en basit yolu , standart Unicode :: Collate modülünün bir alt sınıfı olan Perl kitaplık modülü olan Unicode :: Collate :: Locale'ye bir açıklama yapmaktır . Yapmanız gereken tek şey, kurucuya İsveç için bir yerel değer iletmek . "xv"

(İsveççe metin için bunu takdir etmeyebilirsiniz, ancak Perl soyut karakterler kullandığı için, istediğiniz herhangi bir Unicode kod noktasını kullanabilirsiniz - platform veya yapı ne olursa olsun! Çok az dil böyle bir kolaylık sunar. Son zamanlarda bu çıldırtıcı problem yüzünden Java ile savaşı çok kaybetti.)

Sorun şu ki, Python'dan bir Perl modülüne nasıl erişeceğimi bilmiyorum - yani, kabuk belirtme çizgisi veya iki taraflı boru kullanmak dışında. Bu amaçla, tam olarak istediğiniz şeyi mükemmel bir kolaylıkla yapmak için arayabileceğiniz, ucsort adında eksiksiz bir çalışma betiği sağladım .

Bu komut dosyası, tüm özelleştirme seçeneklerinin desteklendiği tam Unicode Harmanlama Algoritması ile% 100 uyumludur !! Ve isteğe bağlı bir modül taktıysanız veya Perl 5.13 veya daha üstünü çalıştırıyorsanız, kullanımı kolay CLDR yerel ayarlarına tam erişiminiz olur. Aşağıya bakınız.

Gösteri

Bu şekilde sıralanmış bir girdi kümesi düşünün:

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

Kod noktasına göre varsayılan bir sıralama şunu verir:

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 ä å ö

bu herkesin kitabına göre yanlıştır. Unicode Harmanlama Algoritmasını kullanan betiğimi kullanarak şu sırayı alırsınız:

% 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

Bu, varsayılan UCA sıralamasıdır. İsveç yerel ayarını almak için ucsort'u şu şekilde arayın :

% 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 å ä ö

İşte daha iyi bir girdi demosu. İlk olarak, giriş kümesi:

% 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

Kod noktasına göre, bu şu şekilde sıralanır:

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

Ancak varsayılan UCA'yı kullanmak, bu şekilde sıralamayı sağlar:

% 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

Ancak İsveç dilinde şu şekilde:

% 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

Büyük harfin küçük harften önce sıralanmasını tercih ediyorsanız, şunu yapın:

% 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

Özelleştirilmiş Türler

Ucsort ile daha birçok şey yapabilirsiniz . Örneğin, İngilizce başlıkları şu şekilde sıralayabilirsiniz:

% 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
Foundation’s 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

Komut dosyasını genel olarak çalıştırmak için Perl 5.10.1 veya daha iyisine ihtiyacınız olacak. Yerel ayar desteği için isteğe bağlı CPAN modülünü kurmanız gerekir Unicode::Collate::Locale. Alternatif olarak, bu modülü standart olarak içeren Perl, 5.13+ geliştirme sürümlerini kurabilirsiniz.

Çağrı Kuralları

Bu hızlı bir prototiptir, bu nedenle ucsort çoğunlukla belgelenmemiştir. Ancak bu, komut satırında kabul ettiği anahtarların / seçeneklerin SYNOPSIS'idir:

    # 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

Evet, tamam: bu gerçekten arama için kullandığım argüman listesi Getopt::Long, ama sen anladın. :)

Bir Perl betiği çağırmadan doğrudan Python'dan Perl kütüphane modüllerini nasıl çağıracağınızı çözebiliyorsanız, kesinlikle yapın. Sadece kendimi nasıl bilemiyorum. Nasıl olduğunu öğrenmek isterim.

Bu arada, bu senaryonun tüm özelliğiyle ihtiyacınız olan şeyi ve daha fazlasını yapacağına inanıyorum ! Şimdi bunu tüm metin sıralama için kullanıyorum. Bu nihayet ben uzun, çok uzun zamandır ihtiyaç duyulan kadarıyla yok.

Tek dezavantajı, --localeargümanın performansın düşük olmasına neden olmasıdır, ancak normal, yerel olmayan ancak yine de% 100 UCA uyumlu sıralama için yeterince hızlıdır . Her şeyi belleğe yüklediği için, muhtemelen bunu gigabaytlık belgelerde kullanmak istemezsiniz. Günde birçok kez kullanıyorum ve sonunda aklı başında metin sıralaması yapmak harika.


2
Neden Python kitaplıklarının olduğu bir şeyi yapmak için bir Perl betiği çağırıyorsunuz?
Lennart Regebro

2
Orada bilmiyordum Çünkü idi bir Python kütüphanesi, bu yüzden!
tchrist

@Lennart: Yerel kitaplıkları veya en çok bir C API ile bağlantılı ve dinamik olarak yüklenmiş (bazen ihtiyaç duyduğunuz) kütüphaneleri tercih ederim. Çeşitli PyPerl ve Inline :: Perl çözümlerini çok ikna edici, sağlam veya esnek bulamadım. Ya da başka birşey. Sadece bazı nedenlerden dolayı doğru hissetmiyorlar. Bunu en son iyi karakter tespitine ihtiyacım olduğunda denedim (ne yazık ki hiç anlamadım).
tchrist

4
Python içinde Perl kullanmak sadece bağımlılıktır.
Utku Zihnioglu

1
Vay. Evet - bana Perl'e benziyor, aslında şimdi bir şeyler yapmanın ikiden fazla yolu olduğunu görüyoruz :) Ancak Python'dan C'yi çağırmak genellikle Perl'i çağırmanın yapacağı türden ek bağımlılıklar ve pratik destek sorunları anlamına gelmez. bu şekilde yapmak için çok fazla çağrı görmek çok zor.
nealmcb

0

Bu sizin kullanım durumu için komple bir çözüm uzaktır, ancak bir göz sürebilir unaccent.py effbot.org gelen komut. Temelde yaptığı şey, bir metindeki tüm aksanları kaldırmaktır. Alfabetik olarak sıralamak için bu 'sterilize edilmiş' metni kullanabilirsiniz. (Daha iyi bir açıklama için bu sayfaya bakın.)


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.