¿Cómo ordeno alfabéticamente cadenas unicode en Python?


Python ordena por valor de byte por defecto, lo que significa que é viene después de z y 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. Preferrably sorting debe tener soporte de idioma para que entienda que åäö debe ser ordenada después de z en sueco, pero que ü debe ser ordenada por u, etc. Por lo tanto, el soporte Unicode es prácticamente un requisito.

Si no hay biblioteca para ello, cuál es la mejor manera para hacer esto? Simplemente haga una asignación de letra a un valor entero y mapee la cadena a una lista de enteros con eso?

Author: Georg Schölly, 2009-07-08

11 answers

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

Actualización: La diferencia principal en la clasificación entre ICU y locale.strcoll es que ICU utiliza el Algoritmo de Intercalación Unicode completo mientras que strcoll utiliza 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 rara vez 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']
 66
Author: Rafał Dowgird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-22 11:04:40

No veo esto en las respuestas. Mi aplicación ordena según la configuración regional usando 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 a otros respondedores: ¿Nadie sabe "local" o no está a la altura de esta tarea?

 48
Author: u0b34a0f6ae,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-23 14:41:14

Pruebe el Algoritmo de Intercalación Python Unicode de James Tauber . Puede que no haga exactamente lo que quieres, pero parece que vale la pena echarle un vistazo. Para un poco más de información sobre los problemas, ver este post por Christopher Lenz.

 9
Author: Vinay Sajip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 13:08:24

También te puede interesar pyuca :

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

Aunque ciertamente no es la forma más exacta, es una forma muy simple de al menos hacerlo bien. También es mejor que la configuración regional en una aplicación web, ya que la configuración regional no es segura para threadsafe y establece la configuración de idioma en todo el proceso. También es más fácil de configurar que PyICU, que depende de una biblioteca C externa.

Subí el script a github como el original estaba abajo en el momento de escribir esto y tuve que recurrir a cachés web para conseguirlo:

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

He utilizado con éxito este script para ordenar correctamente el texto alemán/Francés/italiano en un módulo plone.

 8
Author: href_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-16 13:54:45

Una respuesta resumida y extendida:

locale.strcoll bajo Python 2, y locale.strxfrm de hecho resolverá el problema, y hace un buen trabajo, suponiendo que tenga instalada la configuración regional en cuestión. Lo probé bajo Windows también, donde los nombres de la configuración regional confusamente son diferentes, pero por otro lado parece tener todas las configuraciones regionales que son compatibles instaladas por defecto.

ICU no necesariamente hace esto mejor en la práctica, sin embargo lo hace de manera 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. Necesitarás 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, por lo que puede obtener nombres para mostrar bonitos para la configuración regional, soporte para otros calendarios que no sean Gregorianos (aunque no estoy seguro de que la interfaz de Python lo soporte) y toneladas y toneladas de otros más o menos oscuros compatibilidad con la configuración regional.

Así que en general: Si desea ordenar alfabéticamente y depende de la configuración regional, puede usar el módulo locale, a menos que tenga requisitos especiales, o también necesite más funcionalidad dependiente de la configuración regional, como words splitter.

 7
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-17 07:16:48

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 s, utiliza 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 de realizar esta tarea auxiliar (en cadenas Unicode the 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 dictado se pasa a la translate el método tiene ordinales Unicode (no cadenas) como claves, por lo que necesitamos ese paso de reconstrucción desde el char-to-char spec_dict original. (Los valores en el diccionario que se pasa para traducir [a diferencia de las claves, que deben ser ordinales] pueden ser ordinales Unicode, cadenas Unicode arbitrarias o Ninguna 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 ordenación", "asignar ä a ae para fines de ordenación", y similares).

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

spec_dict = ''.maketrans(spec_dict)

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

 6
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 14:57:16

Una Solución Completa de UCA

La forma más simple, fácil y directa de hacer esto es hacer una llamada al módulo de biblioteca de Perl, Unicode::Collate::Locale, que es una subclase del módulo estándar Unicode::Collate. Todo lo que necesita hacer es pasar al constructor un valor de configuración regional de "xv" para Suecia.

(Puede que no aprecie necesariamente esto para el texto sueco, pero como Perl usa caracteres abstractos, puede usar cualquier punto de código Unicode por favor - no importa la plataforma o construir! Pocos idiomas ofrecen tal conveniencia. Lo menciono porque he librado una batalla perdida con Java mucho sobre este problema enloquecedor últimamente.)

El problema es que no sé cómo acceder a un módulo Perl desde Python - aparte, es decir, usando una llamada de shell o una tubería de dos lados. Con ese fin, Por lo tanto, le he proporcionado un script de trabajo completo llamado ucsort que puedes llamar para hacer exactamente lo que tienes pedido con perfecta facilidad.

Este script es 100% compatible con el Algoritmo de Intercalación Unicode completo , con todas las opciones de sastrería soportadas!! Y si tiene instalado un módulo opcional o ejecuta Perl 5.13 o superior, entonces tiene acceso completo a configuraciones regionales de CLDR fáciles de usar. Véase más adelante.

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 punto de código produce:

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 utiliza el Algoritmo de Intercalación Unicode, se obtiene 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

Esa es la clasificación UCA predeterminada. Para obtener la localización sueca, llame 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, que 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 por defecto 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 el local sueco, este camino:

% 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

Usted puede 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 el soporte de configuración regional, debe instalar el módulo opcional CPAN Unicode::Collate::Locale. Alternativamente, puede instalar una versión de desarrollo de Perl, 5.13+, que incluye ese módulo estándar.

Llamando a Convenciones

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

    # 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í, ok: esa es realmente la lista de argumentos que uso para la llamada a Getopt::Long, pero entiendes la idea. :)

Si puede averiguar cómo llamar a los módulos de la biblioteca Perl desde Python directamente sin llamar a un script Perl, por supuesto, hágalo tan. Yo no sé cómo. Me encantaría saber cómo.

Mientras tanto, creo que este script hará lo que necesitas hacer en todo su particular - y más! Ahora uso esto para toda la ordenación de texto. Finalmente hace lo que he necesitado durante mucho, mucho tiempo.

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

 2
Author: tchrist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 12:56:41

Para implementarlo tendrá que leer acerca de " Unicode collation algorithm" ver http://en.wikipedia.org/wiki/Unicode_collation_algorithm

Http://www.unicode.org/unicode/reports/tr10 /

Una implementación de ejemplo está aquí

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

 1
Author: Anurag Uniyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 13:13:38

Últimamente he estado usando zope.ucol (https://pypi.python.org/pypi/zope.ucol) para esta tarea. Por ejemplo, ordenando la ß alemana:

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

 1
Author: Brian Sutherland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-10 11:08:14

Jeff Atwood escribió un buen post en Natural Sort Order , en él enlazó a un script que hace más o menos lo que se pide .

No es un script trivial, de ninguna manera, pero hace el truco.

 0
Author: Simon Scarfe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-08 13:11:58

Está lejos de ser una solución completa para su caso de uso, pero podría echar un vistazo a la unaccent.py script desde effbot.org. Lo que básicamente hace es eliminar todos los acentos de un texto. Puedes usar ese texto 'desinfectado' para ordenarlo alfabéticamente. (Para una mejor descripción ver esta página.)

 0
Author: Mark van Lent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-05 13:17:19