2012-06-20 62 views
7

我使用的是使用不同語言文本的應用程序,因此,爲了查看或報告目的,需要使用特定語言對某些文本(字符串)進行排序。在Python中對特定區域設置的字符串列表排序

目前,我有一個解決辦法與全球區域設置,這是壞搞亂了,我不想把它製作:

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

的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 *功能我猜),並自動導入包括文件定義什麼

2

你可以使用一個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 library依賴;該行爲與locale.strcoll稍有不同。


我不知道如何在給定一個區域名稱locale.strxfrm功能,無需全局改變它。作爲破解你可以在不同的子進程運行功能:

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

缺點:可能很慢,耗資源


使用普通threading.Lock不會工作,除非您可以控制每個可以從多個線程調用區域感知功能的地方(它們不限於locale模塊,例如,re)。


你可以使用用Cython使用GIL同步訪問編譯功能。當你的函數運行時,GIL將確保沒有其他Python代碼可以被執行。

缺點:不是純Python

2

的​​解決方案是好的,但如果有人在將來想只修改原來的解決方案,在這裏是一種怎麼做:

使用上下文管理器可以安全地完成全局設置的臨時更改。

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 

這確保了原始語言環境的乾淨恢復 - 只要您不使用線程。

相關問題