python中具有特定区域设置的字符串排序列表
我在一个使用不同语言文本的应用程序上工作,因此,出于查看或报告目的,一些文本(字符串)需要用特定语言排序 目前,我有一个解决方案,它会干扰全局语言环境设置,这很糟糕,我不想将其投入生产:python中具有特定区域设置的字符串排序列表,python,locale,ctypes,glibc,Python,Locale,Ctypes,Glibc,我在一个使用不同语言文本的应用程序上工作,因此,出于查看或报告目的,一些文本(字符串)需要用特定语言排序 目前,我有一个解决方案,它会干扰全局语言环境设置,这很糟糕,我不想将其投入生产: default_locale = locale.getlocale(locale.LC_COLLATE) def sort_strings(strings, locale_=None): if locale_ is None: return sorted(strings) l
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语言环境文档明确指出保存和恢复是一个坏主意,但没有给出任何建议:您可以使用的collator来避免更改全局设置:
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']
缺点:依赖;该行为与locale.strcoll
略有不同
我不知道如何在不全局更改的情况下获得给定区域名称的
locale.strxfrm
函数。作为黑客,您可以在不同的子进程中运行您的函数:
缺点:可能速度慢,资源匮乏
使用普通的
线程。除非您可以控制每个可以从多个线程调用区域设置感知函数(它们不限于locale
模块,例如re
)的位置,否则锁定将无法工作
您可以使用Cython编译函数,以使用GIL同步访问。GIL将确保在函数运行时不能执行其他Python代码
缺点:Glibc不是纯Python,它支持显式状态的localeapi。这里有一个用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)
剩下要做的就是实现所有的区域设置函数,支持python unicode字符串(我猜是使用wchar*函数),并自动导入包含文件定义或其他内容,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
只要不使用线程,就可以确保干净地恢复原始区域设置
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) == ['а', 'б', 'в', 'ј', 'ќ', 'џ', 'ш']
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