大文字と小文字を区別しない文字列比較を行うにはどうすればよいですか?


573

Pythonで大文字と小文字を区別しない文字列比較を行うにはどうすればよいですか?

通常の文字列とリポジトリ文字列の比較を非常にシンプルでPython的な方法でカプセル化したいと思います。また、通常のpython文字列を使用して文字列によってハッシュされたdictの値を検索する機能も必要です。

回答:


595

ASCII文字列を想定:

string1 = 'Hello'
string2 = 'hello'

if string1.lower() == string2.lower():
    print("The strings are the same (case insensitive)")
else:
    print("The strings are NOT the same (case insensitive)")

71
それは常にうまくいくとは限りません。例として、ギリシャ語のシグマが2つあり、1つは最後にのみ使用されることを考慮してください。文字列Σίσυφος(「Sísyphos」、またはより適切な「Síſyphos」)には3つすべてがあります。前が大文字、最後が小文字の最終、3番目が小文字の非最終です。2つの文字列がΣίσυφοςandのΣΊΣΥΦΟΣ場合、これらの大文字と小文字は区別されないため、アプローチは失敗します。
tchrist '19 / 07/19

52
@最後の2人のコメンター:両方の文字列がASCII文字列であると仮定するのは公平だと思います。あなたがもう少しエキサイティングな何かへの答えを探しているなら、私はそれがそこにあると確信しています(またはあなたはそれを尋ねることができます)。
ハーレーホルコム

16
問題:'ß'.lower() == 'SS'.lower()偽です。
kennytm 2013

11
ギリシャ文字だけが特別な場合ではありません!米国英語では、文字「i」(\ u0069)は文字「I」(\ u0049)の小文字バージョンです。ただし、トルコ語(「tr-TR」)のアルファベットには「I with a dot」文字「İ」(\ u0130)が含まれています。これは「i」の大文字バージョンであり、「I」は「i without」の大文字バージョンですドット」文字、「ı」(\ u0131)。
Gqqnbig

20
@HarleyHolcombe文字列がASCIIであると想定するのはどのように安全(または公正)ですか?質問には明記されておらず、文字列がユーザーによって入力された、またはユーザーに表示された任意の時点にある場合は、国際化をサポートする必要があります。とにかく、新しいプログラマーはこれを読んでいるので、彼らに真に正しい答えを与えるべきです。
Ethan Reesor 16

529

大文字と小文字を区別しない方法で文字列を比較することは簡単なように見えますが、そうではありません。ここではPython 2の開発が進んでいないため、Python 3を使用します。

最初に注意すべきことは、Unicodeでの大文字と小文字を区別しない変換は簡単ではないということです。次のtext.lower() != text.upper().lower()ようなテキストがあります"ß"

"ß".lower()
#>>> 'ß'

"ß".upper().lower()
#>>> 'ss'

しかし、あなたがcaselessly比較したいとしましょう"BUSSE""Buße"。ヘック、あなたはおそらく比較"BUSSE"して"BUẞE"同等にしたいと思うでしょう-それは新しい資本形態です。推奨される方法は使用することcasefoldです:

str。ケースフォールド()

文字列を大文字に変換したコピーを返します。大文字と小文字を区別する文字列は、大文字と小文字を区別しないマッチングに使用できます。

大文字と小文字の区別は小文字に似ていますが、文字列内のすべての大文字と小文字の区別を取り除くことを目的としているため、より積極的です。[...]

使用しないでくださいlowercasefoldが利用できない場合は、それを行うと.upper().lower()役立ちます(ただし、多少は役立ちます)。

次に、アクセントを考慮する必要があります。フォントレンダラーが優れている場合は、おそらく考えます"ê" == "ê"が、そうではありません。

"ê" == "ê"
#>>> False

後者のアクセントは結合文字だからです。

import unicodedata

[unicodedata.name(char) for char in "ê"]
#>>> ['LATIN SMALL LETTER E WITH CIRCUMFLEX']

[unicodedata.name(char) for char in "ê"]
#>>> ['LATIN SMALL LETTER E', 'COMBINING CIRCUMFLEX ACCENT']

これに対処する最も簡単な方法はunicodedata.normalizeです。おそらくNFKD正規化を使用したいと思うかもしれませんが、ドキュメントを確認してください。その後、

unicodedata.normalize("NFKD", "ê") == unicodedata.normalize("NFKD", "ê")
#>>> True

最後に、これは関数で表されます。

import unicodedata

def normalize_caseless(text):
    return unicodedata.normalize("NFKD", text.casefold())

def caseless_equal(left, right):
    return normalize_caseless(left) == normalize_caseless(right)

8
より良い解決策は、すべての文字列を摂取時に正規化することです。そうすればx.casefold() == y.casefold()、大文字と小文字を区別しない比較を行うことができます(さらに重要なのはx == y、大文字と小文字を区別することです)。
-abarnert

3
@abarnert確かに、状況によっては、ソースをそのままにしておく方がよい場合もありますが、前もって正規化することで、後のコードをはるかに簡単にすることもできます。
Veedrac、2015年

3
@Veedrac:その通りです、それが常に適切であるとは限りません。元のソースを変更せずに出力できるようにする必要がある場合(たとえば、Linuxでファイル名を処理しているため、NKFCとNKFDの両方が許可され、明示的に異なることが想定されている場合)、入力で変換できないことは明らかです…
abarnert

7
Unicode標準セクション3.13には、大文字と小文字を区別しない比較に関する他の2つの定義があります。両側の(D146、正規)NFD(toCasefold(NFD(str)))と両側の(D147、互換性)NFKD(toCasefold(NFKD(toCasefold(NFD(X)))))です。インナーNFDは特定のギリシャ語のアクセント文字を処理するためだけのものであると述べています。エッジケースがすべてだと思います。

2
そして、casefold()が大文字になるチェロキーアルファベットのちょっとした楽しみ:>>> "ᏚᎢᎵᎬᎢᎬᏒ"。upper() 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ"。lower() 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ" .casefold() 'ᏚᎢᎵᎬᎢᎬᏒ' >>>
bortzmeyer

60

Python 2を使用して.lower()、各文字列またはUnicodeオブジェクトを呼び出す...

string1.lower() == string2.lower()

...ほとんどの場合は機能しますが、@ tchristが説明した状況では実際には機能しません。

unicode.txt2つの文字列Σίσυφοςとを含むというファイルがあるとしますΣΊΣΥΦΟΣ。Python 2の場合:

>>> utf8_bytes = open("unicode.txt", 'r').read()
>>> print repr(utf8_bytes)
'\xce\xa3\xce\xaf\xcf\x83\xcf\x85\xcf\x86\xce\xbf\xcf\x82\n\xce\xa3\xce\x8a\xce\xa3\xce\xa5\xce\xa6\xce\x9f\xce\xa3\n'
>>> u = utf8_bytes.decode('utf8')
>>> print u
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = u.splitlines()
>>> print first.lower()
σίσυφος
>>> print second.lower()
σίσυφοσ
>>> first.lower() == second.lower()
False
>>> first.upper() == second.upper()
True

Σ文字には、ςとσの2つの小文字形式があり、大文字と小文字を.lower()区別せずに比較できません。

ただし、Python 3以降、3つの形式はすべてtoに解決され、両方の文字列でlower()を呼び出すと正しく機能します。

>>> s = open('unicode.txt', encoding='utf8').read()
>>> print(s)
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = s.splitlines()
>>> print(first.lower())
σίσυφος
>>> print(second.lower())
σίσυφος
>>> first.lower() == second.lower()
True
>>> first.upper() == second.upper()
True

したがって、ギリシャ語の3つのシグマのようなエッジケースを気にする場合は、Python 3を使用してください。

(参考のために、Python 2.7.3とPython 3.3.0b1は上記のインタープリターの印刷出力に示されています。)


20
比較をさらに堅牢にするために、Python 3.3以降ではcasefoldを使用できます(たとえば、first.casefold()== second.casefold())。Python 2の場合は、PyICUを使用できます(以下も参照してください:icu-project.org/apiref/icu4c/…
kgriffs

42

Unicode標準のセクション3.13を区別しないマッチングのアルゴリズムを定義しています。

X.casefold() == Y.casefold() Python 3では、「デフォルトの大文字と小文字を区別しないマッチング」(D144)を実装しています。

ケースフォールディングでは、すべてのインスタンスで文字列の正規化が保持されないため、正規化を実行する必要があります('å'vs. 'å')。D145は「正規の大文字と小文字を区別しないマッチング」を導入します。

import unicodedata

def NFD(text):
    return unicodedata.normalize('NFD', text)

def canonical_caseless(text):
    return NFD(NFD(text).casefold())

NFD() U + 0345文字を含む非常にまれなエッジケースでは、2回呼び出されます。

例:

>>> 'å'.casefold() == 'å'.casefold()
False
>>> canonical_caseless('å') == canonical_caseless('å')
True

'㎒'(U + 3392)などのケースの互換性のあるケースレスマッチング(D146)と、識別子のケースレスマッチングを簡素化および最適化する「識別子のケースレスマッチング」もあります


3
Python 3はUnicode文字列を使用し、Unicode標準が大文字と小文字を区別しない文字列マッチングを定義する方法を説明するため、これがPython 3の最良の回答です。
セルギコレスニコフ2016

残念ながら、Python 3.6の時点では、このcasefold()関数は大文字と小文字の折りたたみプロパティで説明されているように、大文字のIとドット付きの大文字のIの特別なケース処理を実装していません。したがって、それらの文字を含むトルコ語の単語の比較は失敗する可能性があります。たとえば、canonical_caseless('LİMANI') == canonical_caseless('limanı')はを返す必要Trueがありますが、を返しますFalse。現在、Pythonでこれに対処する唯一の方法は、casefoldラッパーを作成するか、PyICUなどの外部Unicodeライブラリを使用することです。
セルギコレスニコフ2016

@SergiyKolesnikov .casefold()は、私の知る限り、正常に動作します。標準から:「デフォルトの大文字と小文字の操作は、特定の言語と環境に合わせた調整がない場合の使用を目的としています」。トルコのドット付き大文字Iとドットなしの小文字iの大文字と小文字の規則は、SpecialCasing.txtにあります。「非トルコ語では、このマッピングは通常使用されません。」Unicode FAQから: Q:トルコ語のロケールに依存しない大文字小文字をサポートするためにエンコードされた余分な文字がないのはなぜですか?
jfs 16

1
@ jf-sebastian casefold()が正しく動作しないとは言いませんでした。大文字と点線の大文字Iの特別な処理を可能にするオプションのパラメーターを実装した場合、それは実用的です。たとえば、ICUライブラリのfoldCase()が行う方法: "ケースの折りたたみはロケールに依存せず、コンテキストに依存しません-sensitive、ただしCaseFolding.txtで「T」とマークされているドット付きIとドットなしiのマッピングを含めるか除外するかを選択するオプションがあります。
セルギコレスニコフ2016

6

ここでは正規表現を使用してこの解決策を見ました。

import re
if re.search('mandy', 'Mandy Pande', re.IGNORECASE):
# is True

それはアクセントでうまく機能します

In [42]: if re.search("ê","ê", re.IGNORECASE):
....:        print(1)
....:
1

ただし、大文字と小文字を区別しないUnicode文字では機能しません。@Rhymoidに指摘していただきありがとうございます。私の理解では、ケースが真実であるためには正確なシンボルが必要であることを理解していたためです。出力は次のとおりです。

In [36]: "ß".lower()
Out[36]: 'ß'
In [37]: "ß".upper()
Out[37]: 'SS'
In [38]: "ß".upper().lower()
Out[38]: 'ss'
In [39]: if re.search("ß","ßß", re.IGNORECASE):
....:        print(1)
....:
1
In [40]: if re.search("SS","ßß", re.IGNORECASE):
....:        print(1)
....:
In [41]: if re.search("ß","SS", re.IGNORECASE):
....:        print(1)
....:

4
大文字と小文字を区別しない検索では見つからßないという事実は、Unicode文字はまったく機能しないSSことを示してます

3

通常のアプローチでは、検索と比較のために文字列を大文字にするか小文字にします。例えば:

>>> "hello".upper() == "HELLO".upper()
True
>>> 

2

最初に小文字に変換してみませんか?使用できますstring.lower()


4
あなたは彼らの小文字をマップし比較することはできません。ΣίσυφοςΣΊΣΥΦΟΣ同等のテストが、すべきではないでしょう。
tchrist '19 / 07/19

-2
def insenStringCompare(s1, s2):
    """ Method that takes two strings and returns True or False, based
        on if they are equal, regardless of case."""
    try:
        return s1.lower() == s2.lower()
    except AttributeError:
        print "Please only pass strings into this method."
        print "You passed a %s and %s" % (s1.__class__, s2.__class__)

3
例外をstdoutに出力されるメッセージで置き換え、Noneを返します。これはFalseです。これは実際にはあまり役に立ちません。
gerrit 2017年

-2

2つの文字列を小文字に変換し(すべての文字が小文字になる)、それらを比較するだけです(文字列がASCII文字列であると想定)。

例えば:

string1 = "Hello World"
string2 = "hello WorlD"

if string1.lower() == string2.lower():
    print("The two strings are the same.")
else:
    print("The two strings are not the same.")

この回答は新しい情報を追加しません。また、受け入れられた回答とほぼ同じです。
ジョージー

-3

これは、先週私が愛/憎むことを学んだもう1つの正規表現です。したがって、通常は(この場合ははい)インポートして、いかにimの感情を反映したものにしますか。通常の関数を作成します。...入力を求め、次に.... something = re.compile(r'foo * | spam * '、yes.I)...... re.I(yes.Iを使用します。以下)はIGNORECASEと同じですが、多くの間違いを犯すことはできません!

次に、正規表現を使用してメッセージを検索しますが、正直に言うと、それ自体が数ページになるはずですが、要点は、fooまたはspamがパイプでつながれ、大文字と小文字は無視されるということです。次に、どちらかが見つかった場合、lost_n_foundはそれらの1つを表示します。どちらもない場合、lost_n_foundはNoneに等しくなります。noneと等しくない場合、「return lost_n_found.lower()」を使用して小文字でuser_inputを返します

これにより、大文字と小文字を区別するあらゆるものをより簡単に一致させることができます。最後に(NCS)は「誰も真剣に気にしない...!」の略です。大文字と小文字を区別しない...

誰か質問があれば私にこれを教えてください。

    import re as yes

    def bar_or_spam():

        message = raw_input("\nEnter FoO for BaR or SpaM for EgGs (NCS): ") 

        message_in_coconut = yes.compile(r'foo*|spam*',  yes.I)

        lost_n_found = message_in_coconut.search(message).group()

        if lost_n_found != None:
            return lost_n_found.lower()
        else:
            print ("Make tea not love")
            return

    whatz_for_breakfast = bar_or_spam()

    if whatz_for_breakfast == foo:
        print ("BaR")

    elif whatz_for_breakfast == spam:
        print ("EgGs")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.