在Python中使用特定语言环境对字符串列表进行排序

17

我正在开发一个应用程序,使用来自不同语言的文本,因此,为了查看或报告目的,某些文本(字符串)需要按照特定语言进行排序。

目前,我的解决方法是对全局区域设置进行操作,这是不好的,我不想在生产环境中采用这种方法:

default_locale = locale.getlocale(locale.LC_COLLATE)

def sort_strings(strings, locale_=None):
    if locale_ is None:
        return sorted(strings)

    locale.setlocale(locale.LC_COLLATE, locale_)
    sorted_strings = sorted(strings, cmp=locale.strcoll)
    locale.setlocale(locale.LC_COLLATE, default_locale)

    return sorted_strings

官方Python本地化文档明确表示保存和恢复是一个不好的想法,但没有给出任何建议:http://docs.python.org/library/locale.html#background-details-hints-tips-and-caveats

3个回答

9
你可以使用PyICU的排序器来避免改变全局设置。
import icu # PyICU

def sorted_strings(strings, locale=None):
    if locale is None:
       return sorted(strings)
    collator = icu.Collator.createInstance(icu.Locale(locale))
    return sorted(strings, key=collator.getSortKey)

例子:

>>> L = [u'sandwiches', u'angel delight', u'custard', u'éclairs', u'glühwein']
>>> sorted_strings(L)
['angel delight', 'custard', 'glühwein', 'sandwiches', 'éclairs']
>>> sorted_strings(L, 'en_US')
['angel delight', 'custard', 'éclairs', 'glühwein', 'sandwiches']

缺点:依赖于PyICU库;行为与locale.strcoll略有不同。
我不知道如何在不全局更改的情况下获取locale.strxfrm函数给定一个区域名称。作为一个“hack”,你可以在一个不同的子进程中运行你的函数。
pool = multiprocessing.Pool()
# ...
pool.apply(locale_aware_sort, [strings, loc])

缺点:可能会很慢,占用资源较多


使用普通的threading.Lock是不起作用的,除非你能够控制每个可能从多个线程调用的区域感知函数的地方(它们不仅限于locale模块,例如re)。

6
< p > < code > ctypes < /code > 解决方案不错,但如果将来有人想修改您的原始解决方案,以下是一种方法:< /p> < p > 使用上下文管理器可以安全地暂时更改全局设置。< /p>
from contextlib import contextmanager
import locale

@contextmanager
def changedlocale(newone):
    old_locale = locale.getlocale(locale.LC_COLLATE)
    try:
        locale.setlocale(locale.LC_COLLATE, newone)
        yield locale.strcoll
    finally:
        locale.setlocale(locale.LC_COLLATE, old_locale)

def sort_strings(strings, locale_=None):
    if locale_ is None:
        return sorted(strings)

    with changedlocale(locale_) as strcoll:
        return sorted(strings, cmp=strcoll)

    return sorted_strings

这可以确保原始语言环境的干净恢复 - 只要您不使用线程。

4

Glibc支持具有显式状态的区域设置API。这是一个使用ctypes快速包装该API的示例。

# -*- coding: utf-8
import ctypes


class Locale(object):
    def __init__(self, locale):
        LC_ALL_MASK = 8127
        # LC_COLLATE_MASK = 8
        self.libc = ctypes.CDLL("libc.so.6")
        self.ctx = self.libc.newlocale(LC_ALL_MASK, locale, 0)



    def strxfrm(self, src, iteration=1):
        size = 3 * iteration * len(src)
        dest =  ctypes.create_string_buffer('\000' * size)
        n = self.libc.strxfrm_l(dest, src, size,  self.ctx)
        if n < size:
            return dest.value
        elif iteration<=4:
            return self.strxfrm(src, iteration+1)
        else:
            raise Exception('max number of iterations trying to increase dest reached')


    def __del__(self):
        self.libc.freelocale(self.ctx)

以及一个简短的测试

locale1 = Locale('C')
locale2 = Locale('mk_MK.UTF-8')

a_list = ['а', 'б', 'в', 'ј', 'ќ', 'џ', 'ш']
import random
random.shuffle(a_list)

assert sorted(a_list, key=locale1.strxfrm) == ['а', 'б', 'в', 'ш', 'ј', 'ќ', 'џ']
assert sorted(a_list, key=locale2.strxfrm) == ['а', 'б', 'в', 'ј', 'ќ', 'џ', 'ш']

需要完成的工作是实现所有区域设置功能,支持Python Unicode字符串(使用wchar*函数),并自动导入包含文件定义或类似内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接