web-dev-qa-db-ja.com

Python文字列の大文字と小文字を区別しない

大文字小文字を無視して、Pythonで文字列を比較する最も簡単な方法は何ですか?

もちろん(str1.lower()<= str2.lower())などもできますが、これにより、2つの追加の一時文字列が作成されました(明らかなalloc/g-cオーバーヘッド付き)。

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

[より多くのコンテキストが要求されたので、ささいな例で説明します:]

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

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

現在、インプレース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のバグと欠落している機能のために、この種の比較はユニコードコンテキストで間違った結果を与える可能性があります。

75
user3850

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

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

あなたの質問は、ユニコードを必要としないことを暗示しています。次のコードスニペットを試してください。それがあなたのために働くなら、あなたは終わっています:

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を使用する場合は注意する必要があります。残念ながら、トルコ語のIsに対するPythonの処理は良くありません。 ıは私に変換されますが、私はıに変換されません。 İはiに変換されますが、iはİに変換されません。

5
Douglas Leeder

必要な機能に相当する組み込み機能はありません。

一度に各文字を.lower()に変換する独自の関数を記述して、両方の文字列の重複を避けることができますが、CPUを集中的に使用し、非常に非効率になると確信しています。

非常に長い文字列(重複するとメモリの問題が発生する可能性が非常に長い)を使用している場合を除き、単純に使用します

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

大丈夫だよ

3
Ricardo Reyes

この質問は、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

多くの場合、小文字のテクニックが優れているため、この関数が意味をなす場合にのみ使用してください。

私はアスキー文字列でのみ動作しますが、これがユニコードでどのように動作するのか分かりません。

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

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

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

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