文字列内のすべての文字を反復処理する場合、これを行う方法を知っていますが、よりエレガントな方法を探しています。
回答:
正規表現は、ごくわずかなコードでうまくいきます。
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
\w
インクルード\d
と_
、したがってisvalid = re.match(r'[\w-]+$', astr)
またはisinvalid = re.search(r'[^\w-]', astr)
。locale.setlocale
文字列またはUnicode文字列が存在する可能性があるため、追加の考慮が必要です。
isvalid = re.match(r'[\w-]*$', astr)
-空の文字列は有効です。
[編集]まだ言及されていない別の解決策があり、ほとんどの場合、これまでに与えられた他の解決策を上回っているようです。
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とセットの差は同等であり、データに関係なく、文字列の長さに一貫して比例します。
すべての有効な文字を照合する正規表現メソッドと無効な文字を検索する正規表現メソッドの間にも同様の違いがあります。長いが完全に有効な文字列をチェックする場合、マッチングのパフォーマンスは少し向上しますが、文字列の終わり近くの無効な文字の場合はパフォーマンスが低下します。
string.ascii_letters
の代わりに、string.letters
あなたはそうあなたが偽陽性の結果を得る可能性があります(正規表現のためのre.LOCALEフラグを使用しない場合check_trans()
。string.maketrans()
とは限らないの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バージョンよりもパフォーマンスが低下します。
この目標を達成するにはさまざまな方法がありますが、いくつかは他よりも明確です。私の例のそれぞれについて、「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)
string.ascii_letters
'[a-zA-Z]'正規表現を使用する場合に使用します。
正規表現は非常に柔軟です。
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
:のみ [a-zA-Z0-9_]
したがって、-
ハイフン文字を正当化するために文字を追加する必要があります。
+
:前の文字の1つ以上の繰り返しに一致します。私はあなたが空白の入力を受け入れないと思います。ただし、そうする場合は、に変更してください*
。
^
:文字列の先頭に一致します。
$
:文字列の終わりに一致します。
次の場合を回避する必要があるため、これら2つの特殊文字が必要です。このような不要な文字&
は、一致したパターンの間に表示される場合があります。
&&&PATTERN&&PATTERN
さて、あなたは正規表現の助けを求めることができます、ここで素晴らしいです:)
コード:
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
お役に立てれば :)
いつでもリスト内包表記を使用して結果をすべてで確認できます。正規表現を使用するよりもリソースを少し消費しません。 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
、一致する文字が見つかるか最後に到達するまで、文字列内の各文字を繰り返す必要があると思います。ジョエル・スポルスキーを引用すると、これは画家のアルゴリズムのシュレミエルのようなものです。
ただし、セット内の存在をテストする方が効率的であるか、少なくとも許可される文字数にあまり依存しない必要があります。確かに、このアプローチは私のマシンでは少し高速です。それは明らかであり、ほとんどの場合に十分に機能すると思います(私の遅いマシンでは、数万の短い文字列をほんの一瞬で検証できます)。私はそれが好きです。
実際、私のマシンでは、正規表現は数倍速く動作し、これと同じくらい簡単です(おそらくもっと簡単です)。だから、それはおそらく前進するための最良の方法です。
正規表現を使用して、一致するかどうかを確認してください。
([a-z][A-Z][0-9]\_\-)*