52

大文字と小文字を区別せずにPythonで文字列を比較する最も簡単な方法は何ですか?

もちろん、(str1.lower() <= str2.lower()) などを行うこともできますが、これにより 2 つの追加の一時文字列が作成されます (明らかな alloc/gc オーバーヘッドを伴います)。

C の stricmp() に相当するものを探していると思います。

[さらにいくつかのコンテキストが要求されたので、簡単な例で説明します:]

文字列の長いリストをソートしたいとします。theList.sort() を実行するだけです。これは O(n * log(n)) 文字列比較であり、メモリ管理はありません (すべての文字列とリスト要素はある種のスマート ポインターであるため)。あなたは幸せです。

ここで、同じことをしたいと思いますが、大文字と小文字は無視します (単純化して、すべての文字列が ASCII であるとしましょう。したがって、ロケールの問題は無視できます)。theList.sort(key=lambda s: s.lower()) を実行できますが、比較ごとに 2 つの新しい割り当てが発生し、ガベージ コレクターに重複した (縮小された) 文字列の負担がかかります。このようなメモリ管理の各ノイズは、単純な文字列比較よりも桁違いに遅くなります。

ここで、stricmp() のようなインプレース関数を使用して、次のようにします。あなたは再び幸せです。

問題は、Python ベースの大文字と小文字を区別しない比較には暗黙的な文字列の重複が含まれるため、C ベースの比較 (おそらくモジュール文字列) を見つけることを期待していました。

そのようなものは見つからなかったので、ここで質問します。(これで質問が明確になることを願っています)。

4

16 に答える 16

74

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()string メソッドには、ロケールに依存するという利点もあります。独自の「最適化された」ソリューションを作成するときに、おそらく正しくないことがあります。それでも、Python のバグや欠落している機能により、この種の比較は Unicode コンテキストで間違った結果をもたらす可能性があります。

于 2008-09-23T14:32:08.267 に答える
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']

明確化:一見して明らかでない場合は、locale.strcollが必要な関数のようであり、str.lowerまたはlocale.strxfrmの「重複した」文字列を避けます。

于 2008-09-15T22:20:06.347 に答える
7

パフォーマンスが非常に重要なアプリケーションの非常に頻繁に実行されるパスでこの比較を使用していますか? または、サイズがメガバイトの文字列でこれを実行していますか? そうでない場合は、パフォーマンスについて心配する必要はなく、単に .lower() メソッドを使用してください。

次のコードは、1.8 GHz のデスクトップ コンピューターで、サイズがほぼ 1 メガバイトの 2 つの文字列に対して .lower() を呼び出して大文字と小文字を区別しない比較を行うと、約 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 を参照してください。

于 2008-09-15T13:52:43.053 に答える
6

大文字と小文字を区別しない比較を行う組み込みの方法が他に見つかりません。Python のクックブックのレシピでは、lower() を使用しています。

ただし、トルコ語の I 問題があるため、比較に low を使用する場合は注意が必要です。残念ながら、トルコ語 Is に対する Python の処理は適切ではありません。ı は I に変換されますが、I には変換されません。İ は i に変換されますが、i は İ に変換されません。

于 2008-09-15T15:15:10.017 に答える
3

必要な機能に相当する機能は組み込まれていません。

両方の文字列が重複しないように、一度に各文字を.lower()に変換する独自の関数を作成できますが、CPUを大量に消費し、非常に非効率になると確信しています。

非常に長い文字列(複製するとメモリの問題を引き起こす可能性がある長さ)を使用している場合を除いて、私はそれをシンプルに保ち、使用します

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

あなたは大丈夫でしょう

于 2008-09-15T13:46:39.420 に答える
2

この質問は2つの非常に異なることを求めています:

  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文字列のみを扱います。これがユニコードでどのように動作するかはわかりません。

于 2011-08-30T05:45:39.957 に答える
2

標準ライブラリでうまくサポートされていないものがある場合、私は常に 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

検索の代わりに一致を使用したため、正規表現にキャレット (^) を追加する必要はありませんでした。

注:これは等価性のみをチェックします。これは必要な場合もあります。私も好きとまでは言いません。

于 2010-04-26T03:41:19.423 に答える
1

これは、reで行う方法です:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')
于 2008-09-15T13:05:22.403 に答える
1

計算コストの高いキーを使用して値のリストをソートするための推奨イディオムは、いわゆる「装飾パターン」です。元のリストから(キー、値)タプルのリストを作成し、そのリストをソートするだけです。次に、キーを削除して、並べ替えられた値のリストを取得するのは簡単です。

>>> 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() を呼び出すコストが本当に心配な場合は、(lower 文字列、元の文字列) のタプルをどこにでも格納できます。タプルは Python で最も安価な種類のコンテナーであり、ハッシュ可能であるため、辞書のキー、セット メンバーなどとして使用できます。

于 2008-09-15T21:54:03.723 に答える
0

時折または繰り返し比較する場合でも、コアコードの最も内側のループで発生しないか、パフォーマンスへの影響に実際に気付くのに十分なデータがない限り、いくつかの余分な文字列オブジェクトは問題になりません。あなたがそうするかどうか見てください:あなたがそれをより少なくするならば、「愚かな」方法で物事をすることははるかに愚かではありません。

たくさんのテキストを大文字と小文字を区別せずに比較し続けたい場合は、文字列の小文字バージョンを手元に置いて、ファイナライズと再作成を回避するか、データセット全体を小文字に正規化することができます。もちろん、これはデータセットのサイズによって異なります。針が比較的少なく、干し草の山が大きい場合は、針をコンパイル済みの正規表現オブジェクトに置き換えることが1つの解決策です。具体的な例を見ずに言うのは難しいなら。

于 2008-09-15T13:43:59.720 に答える
0

.lower() を使用するか、正規表現を使用する必要があると確信しています。組み込みの大文字と小文字を区別しない文字列比較関数については知りません。

于 2008-09-15T12:59:43.477 に答える
0

str().lower()高性能が重要でない限り、メソッドをそのまま使用してください。その場合、そのソートメソッドを C 拡張として記述してください。

「Python 拡張機能の書き方」はまともなイントロのように思えます..

さらに興味深いことに、このガイドでは、ctypes ライブラリの使用と外部 C モジュールの作成を比較しています (ctype は C 拡張機能よりも大幅に低速です)。

于 2008-10-11T08:44:14.977 に答える
0

各文字列を一度だけ小文字に変換できます --- 必要な場合にのみ遅延して、または文字列のコレクション全体を並べ替えることがわかっている場合は、並べ替えの前処理として。この比較キーを並べ替える実際のデータに関連付ける方法はいくつかありますが、これらの手法については別の問題で対処する必要があります。

この手法は、大文字/小文字の問題を処理するためだけでなく、ロケール固有の並べ替えや、先頭の記事を無視してデータを正規化してから並べ替える「図書館スタイル」のタイトル並べ替えなど、他の種類の並べ替えにも使用できることに注意してください。

于 2008-09-15T19:13:20.430 に答える
0
import re
if re.match('tEXT', 'text', re.IGNORECASE):
    # is True
于 2014-03-07T11:37:53.967 に答える
-1

You could subclass str and create your own case-insenstive string class but IMHO that would be extremely unwise and create far more trouble than it's worth.

于 2008-09-15T13:06:08.657 に答える
-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

strcasecmp のドキュメントを参照することもできます

これが速いか遅いかはよくわかりませんが (テストしていません)、C 関数を使用して大文字と小文字を区別しない文字列比較を行う方法です。

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

ActiveState コード - レシピ 194371: 大文字と小文字を区別しない文字列は、大文字と小文字を区別しない文字列クラスを作成するためのレシピです。簡単なことをするには少しやり過ぎかもしれませんが、頻繁に使用する予定がある場合は、大文字と小文字を区別しない文字列を処理する一般的な方法を提供できます。

于 2008-09-15T20:20:53.387 に答える