12

私はさまざまな言語のテキストを使用するアプリケーションで作業しているため、表示またはレポートの目的で、一部のテキスト (文字列) を特定の言語でソートする必要があります。

現在、グローバルロケール設定をいじる回避策がありますが、これは悪いことであり、本番環境には入れたくありません。

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 ロケール ドキュメントでは、保存と復元は悪い考えであると明示的に述べられていますが、提案はありません

4

3 に答える 3

7

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グローバルに変更せずにロケール名を指定して関数を取得する方法がわかりません。ハックとして、関数を別の子プロセスで実行できます。

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

短所:遅くなる可能性があり、リソースを大量に消費する


ロケール対応関数 (モジュールに限定されない) を複数のスレッドから呼び出すことがthreading.Lockできるすべての場所を制御できない限り、通常の使用は機能しません。localere


Cythonを使用して関数をコンパイルし、 GIL を使用してアクセスを同期することができます。GIL は、関数の実行中に他の Python コードが実行されないようにします。

短所:純粋な Python ではない

于 2012-06-20T17:09:31.153 に答える
5

解決策は問題ありませんが、将来誰かが元の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

これにより、スレッド化を使用しない限り、元のロケールのクリーンな復元が保証されます。

于 2012-06-21T13:56:04.257 に答える
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* 関数を使用すると思います)、インクルード ファイル定義などを自動的にインポートします。

于 2012-06-21T13:47:48.317 に答える