2012-06-20 13 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) == ['а', 'б', 'в', 'ј', 'ќ', 'џ', 'ш'] 

何をするために残ってすることは、すべてのロケール関数を実装(私は推測するwchar *機能付き)PythonのUnicode文字列をサポートし、自動的にインクルードファイルの定義をインポートしていますか何か

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モジュールに限定されていない)を複数のスレッドから呼び出すことができるすべての場所を制御できます。


あなたはGILを使用してアクセスを同期するためにCythonを使用して機能をコンパイルすることができます。 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 

これにより、スレッドを使用しない限り、元のロケールを完全に復元できます。

関連する問題