忽略Python字符串中的大小写

Pau*_*ter 51 python string case-insensitive

在Python中比较字符串的最简单方法是什么,忽略大小写?

当然可以做(str1.lower()<= str2.lower())等,但是这创建了两个额外的临时字符串(具有明显的alloc/gc开销).

我想我正在寻找相当于C的stricmp().

[请求更多上下文,所以我将用一个简单的例子来证明:]

假设您要对looong字符串列表进行排序.你只需要做List.sort().这是O(n*log(n))字符串比较,没有内存管理(因为所有字符串和列表元素都是某种智能指针).你很快乐.

现在,您想要做同样的事情,但忽略这种情况(让我们简化并说所有字符串都是ascii,因此可以忽略区域设置问题).您可以执行List.sort(key = lambda s:s.lower()),但是每次比较会导致两个新的分配,加上垃圾收集器与重复(降低)字符串的负担.每个这样的存储器管理噪声比简单的字符串比较慢几个数量级.

现在,使用就地stricmp()函数,你可以:theList.sort(cmp = stricmp),它与theList.sort()一样快和内存友好.你又开心了.

问题是任何基于Python的不区分大小写的比较涉及隐式字符串重复,所以我期望找到基于C的比较(可能在模块字符串中).

找不到那样的东西,因此问题就在这里.(希望这澄清了这个问题).

小智 75

这是一个基准测试,显示使用str.lower比接受的答案的建议方法(libc.strcasecmp)更快:

#!/usr/bin/env python2.7
import random
import timeit

from ctypes import *
libc = CDLL('libc.dylib') # change to 'libc.so.6' on linux

with open('/usr/share/dict/words', 'r') as wordlist:
    words = wordlist.read().splitlines()
random.shuffle(words)
print '%i words in list' % len(words)

setup = 'from __main__ import words, libc; gc.enable()'
stmts = [
    ('simple sort', 'sorted(words)'),
    ('sort with key=str.lower', 'sorted(words, key=str.lower)'),
    ('sort with cmp=libc.strcasecmp', 'sorted(words, cmp=libc.strcasecmp)'),
]

for (comment, stmt) in stmts:
    t = timeit.Timer(stmt=stmt, setup=setup)
    print '%s: %.2f msec/pass' % (comment, (1000*t.timeit(10)/10))
Run Code Online (Sandbox Code Playgroud)

我机器上的典型时间:

235886 words in list
simple sort: 483.59 msec/pass
sort with key=str.lower: 1064.70 msec/pass
sort with cmp=libc.strcasecmp: 5487.86 msec/pass
Run Code Online (Sandbox Code Playgroud)

因此,该版本str.lower不仅是迄今为止最快的版本,而且也是所有提议的解决方案中最便携和pythonic.我没有介绍内存使用情况,但原始海报仍然没有给出令人信服的理由担心它.另外,谁说调用libc模块不会复制任何字符串?

注意:lower()字符串方法还具有依赖于语言环境的优点.在编写自己的"优化"解决方案时,您可能无法做到正确的事情.即便如此,由于Python中的错误和缺少的功能,这种比较可能会在unicode上下文中给出错误的结果.

  • 最好避免将别人的答案称为"愚蠢". (6认同)
  • 比.lower方法快4-5倍意味着它比简单排序情况快2倍.怎么可能?!? (4认同)
  • 这也是错误的,因为如果你不使用Unicode casefolds,你会得到各种错误的答案. (3认同)
  • 当然,内存是一个问题,因为.lower()时间的99.9%以上是内存分配.此外,在我检查的(windows)机器上,key = _stricmp方法的速度提高了4-5倍,并且没有内存pnalty. (2认同)
  • @hop:检查[bugs.python.org](http://bugs.python.org/issue?%40columns=id%2Cactivity%2Ctitle%2Ccreator%2Cassignee%2Cstatus%2Ctype&%40sort=-activity&%40filter=status&%对于Unicode错误,40action = searchid&ignore = file%3Acontent&%40search_text = unicode&submit = search&status = -1%2C1%2C2%2C3).我刚刚提出了一堆测试用例,通过不使用casefolding来显示Python的混乱局面.如果我必须在快速和正确之间做出选择,我知道每次都会选择哪一个. (2认同)

Eli*_*ght 7

您是否在高性能敏感应用程序的频繁执行路径中使用此比较?或者,您是在大小为兆字节的字符串上运行吗?如果没有,那么你不应该担心性能,只需使用.lower()方法.

下面的代码演示了通过在两个字符串上调用.lower()来进行不区分大小写的比较,这两个字符串的大小几乎都是几兆字节,在我的1.8GHz桌面计算机上大约需要0.009秒:

from timeit import Timer

s1 = "1234567890" * 100000 + "a"
s2 = "1234567890" * 100000 + "B"

code = "s1.lower() < s2.lower()"
time = Timer(code, "from __main__ import s1, s2").timeit(1000)
print time / 1000   # 0.00920499992371 on my machine
Run Code Online (Sandbox Code Playgroud)

如果确实这是一个非常重要的,性能关键的代码部分,那么我建议在C中编写一个函数并从Python代码中调用它,因为这样可以让您进行真正有效的不区分大小写的搜索.有关编写C扩展模块的详细信息,请访问:https://docs.python.org/extending/extending.html

  • 这是完全错误的.它未能检测到*ΣΤΙΓΜΑΣ*和*στιγμας*在情况下是相同的.您不能使用casemapping来比较Unicode中的大小写.您必须使用casefolding.这些是不同的东西.*Σ,σ,ζ*都是相同的,就像*S,s,s*(无论如何它与s是什么?:)和*Μ,μ,μ*都是.有许多其他类似的情况,比如*weiß,WEIẞ,weiss,WEISS*也是如此,或者*效率高,效率高.*你必须使用casefolds,**因为casemaps不起作用. (5认同)
  • 所以这就是你把东西传递给Timer类的方法.谢谢你解决了我的一个非常不同的痒:) (3认同)

tzo*_*zot 7

您的问题意味着您不需要Unicode.请尝试以下代码段; 如果它适合你,你就完成了:

Python 2.5.2 (r252:60911, Aug 22 2008, 02:34:17)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, "en_US")
'en_US'
>>> sorted("ABCabc", key=locale.strxfrm)
['a', 'A', 'b', 'B', 'c', 'C']
>>> sorted("ABCabc", cmp=locale.strcoll)
['a', 'A', 'b', 'B', 'c', 'C']
Run Code Online (Sandbox Code Playgroud)

澄清:如果乍一看并不明显,locale.strcoll似乎是你需要的函数,避免str.lower或locale.strxfrm"重复"字符串.

  • locale.setlocale()的全局设置显然是一种矫枉过正(过于全局化). (4认同)
  • 您不能使用POSIX语言环境和strcoll,因为它跨平台不可靠.您必须使用Unicode casefolds,它保证在任何地方都能正常工作. (3认同)

Dou*_*der 5

我找不到任何其他内置的不区分大小写的比较方法:python cook-book食谱使用lower().

但是,由于土耳其语I问题,因此在使用lower进行比较时必须小心.不幸的是,Python对土耳其语的处理并不好.ı转换为我,但我没有转换为ı.©转换为i,但我没有转换为İ.

  • 正如您所见,Python不能非常强大地处理Unicode.案例图不注意这些事情.很伤心. (4认同)

pat*_*ung -11

针对您的澄清...

您可以使用ctypes来执行 c 函数“strcasecmp”。Ctypes 包含在 Python 2.5 中。它提供了调用 dll 和共享库(例如 libc)的能力。下面是一个简单示例(Linux 上的 Python;请参阅 Win32 帮助链接):

from ctypes import *
libc = CDLL("libc.so.6")  // see link above for Win32 help
libc.strcasecmp("THIS", "this") // returns 0
libc.strcasecmp("THIS", "THAT") // returns 8
Run Code Online (Sandbox Code Playgroud)

可能还想参考strcasecmp 文档

不太确定这是更快还是更慢(尚未测试),但这是使用 C 函数进行不区分大小写的字符串比较的一种方法。

~~~~~~~~~~~~~~

ActiveState 代码 - 配方 194371:不区分大小写的字符串 是创建不区分大小写的字符串类的配方。对于快速的东西来说,这可能有点过头了,但如果您打算经常使用它们,它可以为您提供处理不区分大小写的字符串的通用方法。

  • 不,这都是错误的。唯一正确的解决方案是比较它们的 Unicode 文件夹。否则你会搞砸的。 (6认同)
  • 我相信这对于其中包含空值的字符串给出了错误的答案。 (3认同)
  • 这要慢得多。看我的回答! (2认同)