python sorting unicode internationalization collation

¿Cómo clasifico las cadenas de unicode alfabéticamente en Python?



sorting internationalization (11)

Python ordena por valor de byte de manera predeterminada, lo que significa que viene después de zy otras cosas igualmente divertidas. ¿Cuál es la mejor manera de ordenar alfabéticamente en Python?

¿Hay una biblioteca para esto? No pude encontrar nada. Preferiblemente, la ordenación debe tener soporte de idioma, por lo que entiende que åäö debe clasificarse después de z en sueco, pero que ü debe ser ordenado por u, etc. El soporte de Unicode es, por lo tanto, casi un requisito.

Si no hay una biblioteca para él, ¿cuál es la mejor manera de hacer esto? Simplemente haga un mapeo de una letra a un valor entero y asocie la cadena a una lista de enteros con eso.


Una solución completa de UCA

La forma más sencilla, sencilla y directa de hacerlo es hacer una llamada al módulo de biblioteca de Perl, Unicode::Collate::Locale , que es una subclase del módulo Unicode::Collate estándar. Todo lo que necesita hacer es pasarle al constructor un valor regional de "xv" para Suecia.

(Es posible que no necesariamente aprecie esto para el texto sueco, pero dado que Perl usa caracteres abstractos, puede usar cualquier punto de código Unicode que desee, sin importar la plataforma o la compilación. Pocos idiomas ofrecen tal conveniencia. Lo menciono porque he luchado contra un perdiendo batalla con Java mucho sobre este enloquecedor problema últimamente).

El problema es que no sé cómo acceder a un módulo de Perl desde Python, aparte de usar un shell call o un canal de dos lados. Con ese fin, por lo tanto , le he proporcionado un script de trabajo completo llamado ucsort al que puede llamar para hacer exactamente lo que ha pedido con gran facilidad.

¡Esta secuencia de comandos es 100% compatible con el algoritmo de intercalación Unicode completo , con todas las opciones de adaptación compatibles! Y si tiene instalado un módulo opcional o ejecuta Perl 5.13 o superior, entonces tendrá acceso completo a las configuraciones regionales de CLDR fáciles de usar. Vea abajo.

Demostración

Imagine un conjunto de entrada ordenado de esta manera:

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

Una clasificación predeterminada por rendimiento de punto de código:

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

que es incorrecto por el libro de todos. Usando mi script, que usa el Algoritmo de intercalación Unicode, obtienes este orden:

% 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

Ese es el tipo de UCA predeterminado. Para obtener la configuración regional sueca, llame a ucsort de esta manera:

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

Aquí hay una mejor demostración de entrada. Primero, el conjunto de entrada:

% 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

Por punto de código, eso se ordena de esta manera:

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

Pero usar el UCA predeterminado lo hace ordenar de esta manera:

% 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

Pero en la localidad sueca, de esta manera:

% 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

Si prefiere mayúsculas para ordenar antes de minúsculas, haga esto:

% 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

Clases personalizadas

Puedes hacer muchas otras cosas con ucsort . Por ejemplo, aquí está cómo ordenar títulos en inglés:

% 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

Necesitará Perl 5.10.1 o superior para ejecutar el script en general. Para soporte de localización, debe instalar el módulo de CPAN opcional Unicode::Collate::Locale . Alternativamente, puede instalar versiones de desarrollo de Perl, 5.13+, que incluyen ese módulo de manera estándar.

Convenciones de llamadas

Este es un prototipo rápido, por lo que ucsort está mayoritariamente (der) documentado. Pero esta es su SINOPSIS de qué conmutadores / opciones acepta en la línea de comando:

# 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

Sí, está bien: esa es realmente la lista de argumentos que utilizo para la llamada a Getopt::Long , pero entiendes la idea. :)

Si puede averiguar cómo llamar directamente a los módulos de la biblioteca de Perl desde Python sin llamar a un script de Perl, hágalo de todos modos. Simplemente no sé cómo yo mismo. Me encantaría aprender cómo.

Mientras tanto, creo que este script hará lo que necesites en todos sus aspectos, ¡ y más! Ahora uso esto para todas las clasificaciones de texto. Finalmente hace lo que he necesitado durante mucho, mucho tiempo.

El único inconveniente es que el argumento de --locale hace que el rendimiento baje, aunque es lo suficientemente rápido para la ordenación normal, no local pero aún 100% compatible con UCA . Como carga todo en la memoria, probablemente no desee utilizar esto en documentos de gigabyte. Lo uso muchas veces al día, y seguro que es genial tener una ordenada ordenación del texto por fin.


Últimamente he estado usando zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) para esta tarea. Por ejemplo, clasificando el alemán ß:

>>> 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 también envuelve ICU, por lo que sería una alternativa a PyICU.


Está lejos de ser una solución completa para su caso de uso, pero podría echarle un vistazo al script unaccent.py de effbot.org. Lo que básicamente hace es eliminar todos los acentos de un texto. Puede usar ese texto "desinfectado" para ordenar alfabéticamente. (Para una mejor descripción, vea this página).



La biblioteca ICU de IBM hace eso (y mucho más). Tiene enlaces de Python: PyICU .

Actualización : La diferencia principal en la clasificación entre ICU y locale.strcoll es que ICU usa el Algoritmo de strcoll Unicode completo, mientras que strcoll usa ISO 14651 .

Las diferencias entre esos dos algoritmos se resumen brevemente aquí: http://unicode.org/faq/collation.html#13 . Estos son casos especiales bastante exóticos, que raramente deberían importar en la práctica.

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


No veo esto en las respuestas. Mi aplicación ordena de acuerdo con la configuración regional utilizando la biblioteca estándar de Python. Es bastante fácil.

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

Pregunta a Lennart y otros que responden: ¿Alguien sabe ''locale'' o no está a la altura de esta tarea?




Puede que también te interese pyuca :

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

Aunque ciertamente no es la forma más exacta, es una manera muy simple de al menos hacerlo bien. También supera a la configuración regional en una aplicación web, ya que la configuración regional no es segura en cuanto a los hilos y establece las configuraciones de idioma en todo el proceso. También es más fácil de configurar que la PyICU que se basa en una biblioteca de C externa.

Cargué el script en github ya que el original estaba inactivo en el momento de escribir este documento y tuve que recurrir a cachés web para obtenerlo:

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

Utilicé con éxito esta secuencia de comandos para ordenar sanamente el texto alemán / francés / italiano en un módulo plone.


Un resumen y una respuesta extendida:

locale.strcoll en Python 2, y locale.strxfrm de hecho resolverá el problema, y ​​hace un buen trabajo, suponiendo que tiene instalada la localidad en cuestión. También lo probé en Windows, donde los nombres de la configuración regional son confusos y diferentes, pero por otro lado parece que tiene todas las configuraciones regionales admitidas instaladas por defecto.

ICU no necesariamente lo hace mejor en la práctica, sin embargo lo hace mucho más . En particular, tiene soporte para divisores que pueden dividir textos en diferentes idiomas en palabras. Esto es muy útil para idiomas que no tienen separadores de palabras. Tendrá que tener un corpus de palabras para usar como base para la división, porque eso no está incluido, sin embargo.

También tiene nombres largos para las configuraciones regionales para que puedas obtener nombres geniales para la configuración regional, soporte para otros calendarios además de Gregorian (aunque no estoy seguro de que la interfaz Python lo admita) y toneladas y toneladas de otros soportes locales más o menos oscuros .

Entonces todo: si desea ordenar alfabéticamente y dependiente de la configuración regional, puede usar el módulo de locale , a menos que tenga requisitos especiales o también necesite más funciones dependientes de la configuración regional, como el separador de palabras.


Veo que las respuestas ya han hecho un excelente trabajo, solo quería señalar una ineficiencia de codificación en Human Sort . Para aplicar una traducción selectiva char-by-char a una cadena unicode, usa el código:

spec_dict = {''Å'':''A'', ''Ä'':''A''} def spec_order(s): return ''''.join([spec_dict.get(ch, ch) for ch in s])

Python tiene una forma mucho mejor, más rápida y más concisa para realizar esta tarea auxiliar (en cadenas Unicode - ¡el método análogo para cadenas de bytes tiene una especificación diferente y algo menos útil! -):

spec_dict = dict((ord(k), spec_dict[k]) for k in spec_dict) def spec_order(s): return s.translate(spec_dict)

El dict que pasas al método de translate tiene ordinales Unicode (no cadenas) como claves, por lo que necesitamos ese paso de reconstrucción del spec_dict char-to-char spec_dict . (Los valores en el diccionario que pasa para traducir [en oposición a las claves, que deben ser ordinales] pueden ser ordinales Unicode, cadenas Unicode arbitrarias o Ninguno para eliminar el carácter correspondiente como parte de la traducción, por lo que es fácil especificar "ignorar un cierto carácter para fines de clasificación "," mapa ä a ae para fines de clasificación ", y similares).

En Python 3, puede obtener el paso de "reconstrucción" más simple, por ejemplo:

spec_dict = ''''.maketrans(spec_dict)

Consulte los documentos de otras formas en que puede usar este método estático maketrans en Python 3.