【问题标题】:Sorting list of string with specific locale in python在python中对具有特定语言环境的字符串列表进行排序
【发布时间】:2012-06-22 17:05:51
【问题描述】:

我正在开发一个使用不同语言文本的应用程序,因此,出于查看或报告目的,一些文本(字符串)需要以特定语言排序。

目前我有一个解决方法会弄乱全局区域设置,这很糟糕,我不想将它投入生产:

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

【问题讨论】:

    标签: python locale ctypes glibc


    【解决方案1】:

    您可以使用 PyICU's 整理器来避免更改全局设置:

    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 library;行为与locale.strcoll 略有不同。


    我不知道如何在不全局更改的情况下获取给定语言环境名称的locale.strxfrm 函数。作为一个hack,你可以在不同的子进程中运行你的函数

    pool = multiprocessing.Pool()
    # ...
    pool.apply(locale_aware_sort, [strings, loc])
    

    缺点:可能很慢,资源匮乏


    使用普通的threading.Lock 将不起作用,除非您可以控制可以从多个线程调用区域设置感知功能(它们不限于locale 模块,例如re)的每个位置。


    您可以使用 Cython 编译您的函数,以使用 GIL 同步访问。 GIL 将确保在您的函数运行时不会执行其他 Python 代码。

    缺点:不是纯 Python

    【讨论】:

      【解决方案2】:

      ctypes 解决方案很好,但如果将来有人只想修改您的原始解决方案,这里有一种方法:

      使用上下文管理器可以安全地完成全局设置的临时更改。

      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
      

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

      【讨论】:

        【解决方案3】:

        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* 函数),并自动导入包含文件定义或其他东西

        【讨论】:

          猜你喜欢
          • 2010-12-04
          • 2012-09-06
          • 2011-09-25
          • 1970-01-01
          • 2022-01-28
          • 2017-09-15
          • 2016-11-18
          • 2019-09-20
          相关资源
          最近更新 更多