文字列に文字、数字、アンダースコア、ダッシュのみが含まれていることを確認するにはどうすればよいですか?


86

文字列内のすべての文字を反復処理する場合、これを行う方法を知っていますが、よりエレガントな方法を探しています。


5
アスキー、ロケール固有、またはユニコード文字について話しているのですか?
jfs 2008年

回答:


122

正規表現は、ごくわずかなコードでうまくいきます。

import re

...

if re.match("^[A-Za-z0-9_-]*$", my_little_string):
    # do something here

25
これを次のように簡略化できます。^ [\ w \ d _-] * $
Prestaul 2008

13
このソリューションは、長さがゼロの文字列に一致します。*の代わりに+を使用して、1文字以上の文字列と一致させます。
Jerub 2008

10
@Prestaul:\wインクルード\d_、したがってisvalid = re.match(r'[\w-]+$', astr)またはisinvalid = re.search(r'[^\w-]', astr)locale.setlocale文字列またはUnicode文字列が存在する可能性があるため、追加の考慮が必要です。
jfs 2008年

1
修正:isvalid = re.match(r'[\w-]*$', astr)-空の文字列は有効です。
jfs 2008年

その正規表現でピリオド/ドット(。)を許可するにはどうすればよいですか?編集、方法は次のとおりです:^ [a-zA-Z0-9 -_ \ s \。] + $
fredrik 2014

24

[編集]まだ言及されていない別の解決策があり、ほとんどの場合、これまでに与えられた他の解決策を上回っているようです。

string.translateを使用して、文字列内のすべての有効な文字を置き換え、無効な​​文字が残っていないかどうかを確認します。これは、基盤となるC関数を使用して作業を行うため、Pythonバイトコードがほとんど含まれないため、非常に高速です。

明らかに、パフォーマンスがすべてではありません。パフォーマンスが重要なコードパスにない場合は、最も読みやすいソリューションを選択するのがおそらく最善のアプローチですが、ソリューションがどのように積み重なるかを確認するために、これまでに提案されたすべての方法のパフォーマンス比較を示します。check_transは、string.translateメソッドを使用するものです。

テストコード:

import string, re, timeit

pat = re.compile('[\w-]*$')
pat_inv = re.compile ('[^\w-]')
allowed_chars=string.ascii_letters + string.digits + '_-'
allowed_set = set(allowed_chars)
trans_table = string.maketrans('','')

def check_set_diff(s):
    return not set(s) - allowed_set

def check_set_all(s):
    return all(x in allowed_set for x in s)

def check_set_subset(s):
    return set(s).issubset(allowed_set)

def check_re_match(s):
    return pat.match(s)

def check_re_inverse(s): # Search for non-matching character.
    return not pat_inv.search(s)

def check_trans(s):
    return not s.translate(trans_table,allowed_chars)

test_long_almost_valid='a_very_long_string_that_is_mostly_valid_except_for_last_char'*99 + '!'
test_long_valid='a_very_long_string_that_is_completely_valid_' * 99
test_short_valid='short_valid_string'
test_short_invalid='/$%$%&'
test_long_invalid='/$%$%&' * 99
test_empty=''

def main():
    funcs = sorted(f for f in globals() if f.startswith('check_'))
    tests = sorted(f for f in globals() if f.startswith('test_'))
    for test in tests:
        print "Test %-15s (length = %d):" % (test, len(globals()[test]))
        for func in funcs:
            print "  %-20s : %.3f" % (func, 
                   timeit.Timer('%s(%s)' % (func, test), 'from __main__ import pat,allowed_set,%s' % ','.join(funcs+tests)).timeit(10000))
        print

if __name__=='__main__': main()

私のシステムでの結果は次のとおりです。

Test test_empty      (length = 0):
  check_re_inverse     : 0.042
  check_re_match       : 0.030
  check_set_all        : 0.027
  check_set_diff       : 0.029
  check_set_subset     : 0.029
  check_trans          : 0.014

Test test_long_almost_valid (length = 5941):
  check_re_inverse     : 2.690
  check_re_match       : 3.037
  check_set_all        : 18.860
  check_set_diff       : 2.905
  check_set_subset     : 2.903
  check_trans          : 0.182

Test test_long_invalid (length = 594):
  check_re_inverse     : 0.017
  check_re_match       : 0.015
  check_set_all        : 0.044
  check_set_diff       : 0.311
  check_set_subset     : 0.308
  check_trans          : 0.034

Test test_long_valid (length = 4356):
  check_re_inverse     : 1.890
  check_re_match       : 1.010
  check_set_all        : 14.411
  check_set_diff       : 2.101
  check_set_subset     : 2.333
  check_trans          : 0.140

Test test_short_invalid (length = 6):
  check_re_inverse     : 0.017
  check_re_match       : 0.019
  check_set_all        : 0.044
  check_set_diff       : 0.032
  check_set_subset     : 0.037
  check_trans          : 0.015

Test test_short_valid (length = 18):
  check_re_inverse     : 0.125
  check_re_match       : 0.066
  check_set_all        : 0.104
  check_set_diff       : 0.051
  check_set_subset     : 0.046
  check_trans          : 0.017

変換アプローチは、ほとんどの場合、劇的に長い有効な文字列の場合に最適であるように見えますが、test_long_invalidの正規表現によって打ち負かされます(おそらく、正規表現はすぐに救済できますが、変換は常に文字列全体をスキャンする必要があるためです)。設定されたアプローチは通常最悪であり、空の文字列の場合にのみ正規表現を打ち負かします。

all(x in allowed_set for x in s)を使用すると、早期に救済された場合はうまく機能しますが、すべての文字を反復処理する必要がある場合はうまくいかない可能性があります。isSubSetとセットの差は同等であり、データに関係なく、文字列の長さに一貫して比例します。

すべての有効な文字を照合する正規表現メソッドと無効な文字を検索する正規表現メソッドの間にも同様の違いがあります。長いが完全に有効な文字列をチェックする場合、マッチングのパフォーマンスは少し向上しますが、文字列の終わり近くの無効な文字の場合はパフォーマンスが低下します。


1
使用string.ascii_lettersの代わりに、string.lettersあなたはそうあなたが偽陽性の結果を得る可能性があります(正規表現のためのre.LOCALEフラグを使用しない場合check_trans()string.maketrans()とは限らないのUnicode文字列の作業。
JFS

Python 3 / Unicode / from __future__ import unicode_literals)の場合はtrans_table3 = dict((ord(char), '') for char in allowed_chars)、とdefを使用しcheck_trans(s): return not s.translate(trans_table3)ます。ただし、一般的に、REバージョンよりもパフォーマンスが低下します。
ヒューゴ

14

この目標を達成するにはさまざまな方法がありますが、いくつかは他よりも明確です。私の例のそれぞれについて、「True」は渡された文字列が有効であることを意味し、「False」は無効な文字が含まれていることを意味します。

まず第一に、素朴なアプローチがあります:

import string
allowed = string.letters + string.digits + '_' + '-'

def check_naive(mystring):
    return all(c in allowed for c in mystring)

次に、正規表現を使用します。これはre.match()で実行できます。'-'は[]の末尾にある必要があることに注意してください。そうでない場合は、 '範囲'区切り文字として使用されます。'文字列の終わり'を意味する$にも注意してください。この質問に記載されている他の回答では、特殊文字クラス '\ w'を使用しています。クイックリファレンスガイドを検索しなくても理解しやすく、特殊文字クラスの範囲を使用する方が簡単なので、[]を使用して明示的な文字クラス範囲を使用することを常に好みます。場合。

import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
    return CHECK_RE.match(mystring)

別の解決策は、正規表現を使用して逆一致を実行できることを示しています。これをここに含めました。[^ ...]は、^が使用されているため、文字クラスを反転することに注意してください。

CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
   return not CHECK_INV_RE.search(mystring)

'set'オブジェクトを使用してトリッキーなことを行うこともできます。この例を見てください。元の文字列から許可されているすべての文字が削除され、a)何も含まれていないか、b)文字列から問題のある文字が含まれているセットが残ります。

def check_set(mystring):
    return not set(mystring) - set(allowed)

最初の正規表現テストでは、「[a-zA-Z0-9 _-] + $」を「[a-zA-Z0-9_-] * $」にしないでください。空の文字列はおそらく一致すると見なされます。
ブライアン

string.ascii_letters'[a-zA-Z]'正規表現を使用する場合に使用します。
jfs 2008年

12

ダッシュとアンダースコアがない場合、最も簡単な解決策は次のとおりです。

my_little_string.isalnum()

(Pythonライブラリリファレンスのセクション3.6.1


残念ながら、リンクは機能しなくなりましたが、関連するセクションPython»3.3.6ドキュメント»Python標準ライブラリ»4.7.1があります。文字列メソッド。@Berに感謝します。これはまさに私が必要としていたものです。
サノス2016年

4

正規表現を使用する代わりに、セットでそれを行うことができます。

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True


1

正規表現は非常に柔軟です。

import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4

\w:のみ [a-zA-Z0-9_]

したがって、-ハイフン文字を正当化するために文字を追加する必要があります。

+:前の文字の1つ以上の繰り返しに一致します。私はあなたが空白の入力を受け入れないと思います。ただし、そうする場合は、に変更してください*

^:文字列の先頭に一致します。

$:文字列の終わりに一致します。

次の場合を回避する必要があるため、これら2つの特殊文字が必要です。このような不要な文字&は、一致したパターンの間に表示される場合があります。

&&&PATTERN&&PATTERN


0

さて、あなたは正規表現の助けを求めることができます、ここで素晴らしいです:)

コード:

import re

string = 'adsfg34wrtwe4r2_()' #your string that needs to be matched.
regex = r'^[\w\d_()]*$' # you can also add a space in regex if u want to allow it in the string  
if re.match(regex,string):
    print 'yes'
else: 
    print 'false'

出力:

yes  

お役に立てれば :)


-1

いつでもリスト内包表記を使用して結果をすべてで確認できます。正規表現を使用するよりもリソースを少し消費しません。 all([c in string.letters + string.digits + ["_", "-"] for c in mystring])


投稿する前にコードをテストしてください。実行される壊れた答えに基づく解決策は次のとおりです。all(string.lettersのc + string.digits + mystringのcの "_")
Jerub 2008

2
これは、正規表現よりもはるかに多くのリソースを消費します。すべての文字に対して線形スキャンを実行し(事前にセットを作成することをお勧めします)、ジェネレーターの理解がより軽量になる場合は、不必要にリストを作成します。
ブライアン

-1

これは、Jerubの「ナイーブなアプローチ」に基づいたものです(ナイーブは彼の言葉であり、私の言葉ではありません!):

import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')

def check(mystring):
    return all(c in ALLOWED for c in mystring)

ALLOWED文字列の場合c in ALLOWED、一致する文字が見つかるか最後に到達するまで、文字列内の各文字を繰り返す必要があると思います。ジョエル・スポルスキーを引用すると、これは画家のアルゴリズムのシュレミエルのようなものです

ただし、セット内の存在をテストする方が効率的であるか、少なくとも許可される文字数にあまり依存しない必要があります。確かに、このアプローチは私のマシンでは少し高速です。それは明らかであり、ほとんどの場合に十分に機能すると思います(私の遅いマシンでは、数万の短い文字列をほんの一瞬で検証できます)。私はそれが好きです。

実際、私のマシンでは、正規表現は数倍速く動作し、これと同じくらい簡単です(おそらくもっと簡単です)。だから、それはおそらく前進するための最良の方法です。


-4

正規表現を使用して、一致するかどうかを確認してください。

([a-z][A-Z][0-9]\_\-)*

1
これらの文字はすべて1つのクラスに含まれている必要があります。そうでない場合、偽陰性が発生します。また、文字列の始まりと文字列の終わりのマーカーを含めるのを忘れました...このように、有効な文字が1つ存在する限り、常に一致します。
トーマス

1
これは、有効な文字がなくても実際に一致します。長さがゼロの一致。また、Pythonではありません。
Jerub 2008
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.