94

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

4

11 に答える 11

134

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

import re

...

if re.match("^[A-Za-z0-9_-]*$", my_little_string):
    # do something here
于 2008-09-18T04:08:19.550 に答える
25

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

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

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

all(x in allowed_set for x in s) を使用すると、早期に回避できればうまく機能しますが、すべての文字を反復処理する必要がある場合はうまくいかない可能性があります。isSubSet と set の違いは比較可能であり、データに関係なく一貫して文字列の長さに比例します。

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

于 2008-09-18T12:19:48.103 に答える
15

この目標を達成するにはさまざまな方法がありますが、いくつかの方法は他の方法よりも明確です。それぞれの例で、'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)
于 2008-09-18T04:18:40.337 に答える
13

ダッシュとアンダースコアがない場合、最も簡単な解決策は

my_little_string.isalnum()

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

于 2008-09-18T10:49:54.920 に答える
4

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

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True
于 2008-09-18T10:47:49.917 に答える
3
 pat = re.compile ('[^\w-]')

 def onlyallowed(s):
    return not pat.search (s)
于 2008-09-18T04:12:00.273 に答える
-1

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

于 2008-09-18T04:12:12.660 に答える
-1

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

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

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

文字列の場合、一致が見つかるか最後に到達するまで、文字列内の各文字を反復処理する必要がALLOWEDあると思います。c in ALLOWEDJoel Spolsky の言葉を引用すると、これはShlemiel the Painter アルゴリズムのようなものです。

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

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

于 2012-11-30T16:50:13.597 に答える
-4

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

([a-z][A-Z][0-9]\_\-)*
于 2008-09-18T04:06:30.113 に答える