Python的sort函数和Linux的LC_ALL=C sort函数是相同的吗?

9
我正在将一个Bash脚本移植到Python。该脚本设置LC_ALL=C,并使用Linux sort命令来确保本地字节顺序,而不是特定于语言环境的排序顺序(https://dev59.com/h3VD5IYBdhLWcg3wR5ko)。
在Python中,我想使用Python的列表sort()sorted()函数(不使用key=选项)。那么,我是否总会得到与LC_ALL=C和Linux sort相同的结果呢?

2
Python 的哪个版本以及需要排序的项目是什么? - Roman Susi
5个回答

10

如果你将locale.strcoll作为cmp参数传递给list.sort()sorted(),那么排序行为应该符合你的预期:

import locale
locale.setlocale(locale.LC_ALL, "C")
yourList.sort(cmp=locale.strcoll)

但是在Python 3中(来自这个回答):

import locale
from functools import cmp_to_key
locale.setlocale(locale.LC_ALL, "C")
yourList.sort(key=cmp_to_key(locale.strcoll))

谢谢大家。我的数据都是Unicode格式的,我说“不使用key=选项”是因为我用它来做其他事情。这个解决方案非常好。@nabucosound,你的解决方案很有趣,但安装PyICU对我的目的来说有点重。再次感谢。 - tahoar
在我的情况下,我需要使用不同的区域设置选项:locale.setlocale(locale.LC_ALL, ('en_US', 'UTF-8')) - jtpereyda

1

考虑到可以添加一个比较函数,您可以确保排序的等效为LC_ALL=C。但从文档上看,如果所有字符都是7位,则默认以此方式进行排序,否则使用特定于区域设置的排序。

如果您有8位或Unicode字符,那么使用特定于区域设置的排序就是有意义的。


1
Python版本低于3的非Unicode字符串实际上是字节。sort函数和方法不会执行任何强制地区设置的操作(需要locale模块函数来显式地促进基于地区设置的排序)。
Python 3.x中的所有字符串都不再是字节,而是Unicode字符串。在Python 3中仍存在“bytes”类型。

1

我一直在使用国际Unicode组件,以及PyICU绑定,使用自己的语言环境(例如我的情况是加泰罗尼亚语)来排序sorted()。例如,按名称属性对用户配置文件列表进行排序:

collator = PyICU.Collator.createInstance(PyICU.Locale('ca_ES.UTF-8'))
sorted(user_profiles, key=lambda x: x.name, cmp=collator.compare)

0

是的!针对您的具体问题

我是否总会得到与Linux sort和LC_ALL=C相同的结果?

是的!Python默认使用C语言环境,因此您可以期望与Linux LC_ALL=C sort相同的行为。

您可以通过自己设置并使用strxfrm进行排序来更明确地说明这种行为:

locale.setlocale(locale.LC_ALL, 'C')  # same as you do in linux
locale.setlocale(locale.LC_COLLATE, 'C')  # specific to sorting

mylist.sort(key=locale.strxfrm)
# To incorporate locale sorting with other uses of key=,
# wrap locale.strxfrm() around whatever else you're doing:
mylist.sort(key=lambda i: locale.strxfrm( mysortfunc(i) ))

文档

来自https://docs.python.org/3/library/locale.html​

初始情况下,当程序启动时,区域设置是“C”区域设置,无论用户的首选区域设置是什么。... 程序必须明确表示它希望使用用户首选的其他类别的区域设置,通过调用setlocale(LC_ALL, '')

根据POSIX标准,如果一个程序没有调用setlocale(LC_ALL, ''),则使用可移植的“C”区域设置。调用setlocale(LC_ALL, '')可以让程序使用由LANG变量定义的默认区域设置。由于我们不想干扰当前的区域设置,因此我们以上述方式模拟了这种行为。

示例

# What are the settings when Python first starts?
>>> import locale
>>> locale.setlocale(locale.LC_ALL, None)  # If locale is omitted or None, the current setting for category is returned.
'LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C'
                                           # ^^^^^^^^^^^^
>>> locale.getlocale(locale.LC_COLLATE)  # The 'C' setting is equivalent to:
(None, None)

# Set LC_COLLATE & use strcoll/strxfrm to sort according to user's locale
# (like linux sort(1) does by default):
>>> locale.setlocale(locale.LC_COLLATE, '')  # An empty string specifies the user’s default settings.
'en_US.UTF-8'
>>> locale.setlocale(locale.LC_ALL, None)
'LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=en_US.UTF-8;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C'
                                           # ^^^^^^^^^^^^^^^^^^^^^^
>>> mylist.sort(key=locale.strxfrm)
>>> mylist.sort(key=lambda i: locale.strxfrm( mysortfunc(i) ))

# Set LC_ALL (everything) to user's locale (common practice):
>>> locale.setlocale(locale.LC_ALL, '')
'en_US.UTF-8'
>>> locale.setlocale(locale.LC_ALL, None)
'en_US.UTF-8'
>>> locale.getlocale(locale.LC_COLLATE)
('en_US', 'UTF-8')

# Use portable/C locale, including byte-order sorting:
>>> locale.setlocale(locale.LC_ALL, 'C')
'C'
>>> locale.setlocale(locale.LC_ALL, None)
'C'
# The LC_ALL setting overrode our previous LC_COLLATE setting:
>>> locale.setlocale(locale.LC_COLLATE, None)
'C'
>>> locale.getlocale(locale.LC_COLLATE)
(None, None)

非常感谢Frédéric Hamidi的回答,它让我朝着正确的方向理解了这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接