web-dev-qa-db-ja.com

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

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

79
Ethan Post

正規表現は、非常に少ないコードでトリックを実行します。

import re

...

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

[編集]まだ言及されていない別の解決策があり、ほとんどの場合、これまでに挙げた他の解決策よりも優れているようです。

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 s for x in s)を使用すると、早期に終了する場合は適切に機能しますが、すべての文字を反復処理する必要がある場合は不適切な場合があります。 isSubSetとsetの差は同等であり、データに関係なく文字列の長さに一貫して比例します。

すべての有効な文字に一致する正規表現メソッドと、無効な文字を検索する正規表現メソッドには、同様の違いがあります。マッチングは、長いが完全に有効な文字列をチェックするときは少し良くなりますが、文字列の終わり近くの無効な文字では悪くなります。

22
Brian

この目標を達成するにはさまざまな方法があり、他の方法よりも明確な方法もあります。それぞれの例で、「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)
14
Jerub

ダッシュとアンダースコアがなければ、最も簡単な解決策は

my_little_string.isalnum()

(セクション .6.1 of Python Library Reference)

10
Ber

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

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True
4
Ber
 pat = re.compile ('[^\w-]')

 def onlyallowed(s):
    return not pat.search (s)
3
Javier

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

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

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

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

+:前の文字の1回以上の繰り返しに一致します。空白の入力は受け付けないでしょう。しかし、もしそうなら、*に変更してください。

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

$:文字列の末尾に一致します。

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

&&&PATTERN&&PATTERN

1
Alston

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

0
William Keller

これは、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は、一致が見つかるか最後に達するまで、文字列の各文字を繰り返し処理します。 Joel Spolskyを引用すると、これは Shlemiel the Painterアルゴリズム のようなものです。

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

[〜#〜]実際に[〜#〜]正規表現は数倍速く動作し、これと同じくらい簡単です(ほぼ単純です) )。それがおそらく最善の方法です。

0
MB.

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

コード:

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  

お役に立てれば :)

0