it-swarm.cn

忽略Python字符串中的大小写

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

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

我想我正在寻找相当于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的比较(可能在模块字符串中)。

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

51
Paul Oyster

这是一个基准测试,显示使用 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))

我机器上的典型时间:

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

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

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

74
user3850

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

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

7
Eli Courtwright

您的问题意味着您不需要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']

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

7
tzot

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

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

5
Douglas Leeder

没有与您想要的功能相当的内置功能。

你可以编写自己的函数,一次转换为.lower()每个字符,以避免重复两个字符串,但我确信它会非常cpu密集,效率极低。

除非你使用非常长的字符串(如果重复可能会导致内存问题),那么我会保持简单和使用

str1.lower() == str2.lower()

你会好的

3
Ricardo Reyes

这个问题要问两个截然不同的事情:

  1. 在Python中比较字符串的最简单方法是什么,忽略大小写?
  2. 我想我正在寻找相当于C的stricmp()。

由于已经很好地回答了#1(即:str1.lower()<str2.lower()),我将回答#2。

def strincmp(str1, str2, numchars=None):
    result = 0
    len1 = len(str1)
    len2 = len(str2)
    if numchars is not None:
        minlen = min(len1,len2,numchars)
    else:
        minlen = min(len1,len2)
    #end if
    orda = ord('a')
    ordz = ord('z')

    i = 0
    while i < minlen and 0 == result:
        ord1 = ord(str1[i])
        ord2 = ord(str2[i])
        if ord1 >= orda and ord1 <= ordz:
            ord1 = ord1-32
        #end if
        if ord2 >= orda and ord2 <= ordz:
            ord2 = ord2-32
        #end if
        result = cmp(ord1, ord2)
        i += 1
    #end while

    if 0 == result and minlen != numchars:
        if len1 < len2:
            result = -1
        Elif len2 < len1:
            result = 1
        #end if
    #end if

    return result
#end def

只有在有意义时才使用此功能,因为在许多情况下,小写技术会更优越。

我只使用ascii字符串,我不知道这将如何与unicode。

2
trevorcroft

当标准库中不支持某些东西时,我总是寻找一个PyPI包。随着虚拟化和现代Linux发行版的无处不在,我不再避免使用Python扩展。 PyICU似乎符合条例草案: https://stackoverflow.com/a/1098160/3461

现在还有一个纯python选项。经过充分测试: https://github.com/jtauber/pyuca


老答案: /

我喜欢正则表达式解决方案。这是一个可以复制并粘贴到任何函数中的函数,这要归功于python的块结构支持。

def equals_ignore_case(str1, str2):
    import re
    return re.match(re.escape(str1) + r'\Z', str2, re.I) is not None

由于我使用匹配而不是搜索,因此我不需要在正则表达式中添加插入符号(^)。

注意: 这只检查相等性,有时需要它。我也不会说我喜欢它。

2
Benjamin Atkin

使用昂贵的计算密钥对值列表进行排序的推荐习惯是所谓的“装饰模式”。它只包含从原始列表构建(键,值)元组列表,并对该列表进行排序。然后删除键并获取排序值列表是微不足道的:

>>> original_list = ['a', 'b', 'A', 'B']
>>> decorated = [(s.lower(), s) for s in original_list]
>>> decorated.sort()
>>> sorted_list = [s[1] for s in decorated]
>>> sorted_list
['A', 'a', 'B', 'b']

或者如果你喜欢单行:

>>> sorted_list = [s[1] for s in sorted((s.lower(), s) for s in original_list)]
>>> sorted_list
['A', 'a', 'B', 'b']

如果你真的担心调用lower()的成本,你可以在任何地方存储(降低字符串,原始字符串)的元组。元组是Python中最便宜的容器,它们也可以清洗,因此可以用作字典键,设置成员等。

1
Antoine P.

这是你用re做的方式:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')
1
Moses Ting

对于偶尔甚至重复的比较,一些额外的字符串对象应该无关紧要,只要这不会发生在核心代码的最内层循环中,或者您没有足够的数据来实际注意到性能影响。看看你是否这样做:如果你做得少,那么以“愚蠢”的方式做事就不那么愚蠢了。

如果你真的想要不断地比较大量和大量的文本不区分大小写你可以保留字符串的小写版本,以避免最终确定和重新创建,或者将整个数据集规范化为小写。这当然取决于数据集的大小。如果有相对较少的针头和大型草堆,用编译的正则表达式对象替换针头是一种解决方案。如果没有看到一个具体的例子就很难说。

0
yason

您可以将每个字符串翻译为小写一次 - 仅在您需要时将其翻译为懒惰,或者如果您知道将对整个字符串集合进行排序,则将其作为排序的前导。有几种方法可以将此比较密钥附加到正在排序的实际数据,但这些技术应在单独的问题中解决。

请注意,此技术不仅可用于处理大小写问题,还可用于其他类型的排序,例如特定于语言环境的排序,或“库样式”标题排序,它们会忽略主要文章,并在排序之前对数据进行标准化。

0
Dale Wilson

只需使用str().lower()方法,除非高性能很重要 - 在这种情况下,将排序方法写为C扩展名。

“如何编写Python扩展” 似乎是一个体面的介绍..

更有趣的是, 本指南 使用ctypes库与编写外部C模块进行比较(ctype比C扩展慢得多)。

0
dbr
import re
if re.match('tEXT', 'text', re.IGNORECASE):
    # is True
0
Venkatesh Bachu

我很确定你要么使用.lower()还是使用正则表达式。我不知道内置的不区分大小写的字符串比较函数。

0
Mark Biek