文字列内のすべての文字を反復処理する場合、これを行う方法は知っていますが、よりエレガントな方法を探しています。
正規表現は、非常に少ないコードでトリックを実行します。
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
[編集]まだ言及されていない別の解決策があり、ほとんどの場合、これまでに挙げた他の解決策よりも優れているようです。
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の差は同等であり、データに関係なく文字列の長さに一貫して比例します。
すべての有効な文字に一致する正規表現メソッドと、無効な文字を検索する正規表現メソッドには、同様の違いがあります。マッチングは、長いが完全に有効な文字列をチェックするときは少し良くなりますが、文字列の終わり近くの無効な文字では悪くなります。
この目標を達成するにはさまざまな方法があり、他の方法よりも明確な方法もあります。それぞれの例で、「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)
正規表現を使用する代わりに、Setでそれを行うことができます。
from sets import Set
allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')
if Set(my_little_sting).issubset(allowed_chars):
# your action
print True
pat = re.compile ('[^\w-]')
def onlyallowed(s):
return not pat.search (s)
正規表現は非常に柔軟です。
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
常にリストの内包表記を使用し、すべての結果を確認できます。正規表現を使用するよりもリソースの消費が少し少なくなります:all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
これは、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アルゴリズム のようなものです。
ただし、セット内に存在するかどうかをテストする方が効率的であるか、少なくとも許可される文字の数に依存している必要はありません。確かに、このアプローチは私のマシンでは少し高速です。それは明らかであり、ほとんどの場合に十分なパフォーマンスを発揮すると思います(私の遅いマシンでは、数秒で数万の短い文字列を検証できます)。私はそれが好きです。
[〜#〜]実際に[〜#〜]正規表現は数倍速く動作し、これと同じくらい簡単です(ほぼ単純です) )。それがおそらく最善の方法です。
さて、ここで素晴らしい、正規表現の助けを求めることができます:)
コード:
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
お役に立てれば :)