如何validation一个string只包含字母,数字,下划线和破折号?

我知道如何做到这一点,如果我遍历string中的所有字符,但我正在寻找一个更优雅的方法。

一个正则expression式只需要很less的代码就可以实现:

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

还有另外一个没有提到的解决scheme,在大多数情况下,它似乎比迄今为止给出的其他解决scheme都要好。

使用string.translate来replacestring中的所有有效字符,并查看是否有剩余的无效字符。 这是非常快的,因为它使用底层的C函数来完成工作,只涉及很less的python字节码。

很显然,性能并不是最重要的 – 考虑到最具可读性的解决scheme,如果不是性能至关重要的代码path,可能是最好的方法,只是看看解决scheme如何叠加起来,以下是迄今为止提出的所有方法的性能比较。 check_trans是使用string.translate方法的。

testing代码:

 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 

在大多数情况下,翻译方法似乎是最好的,对于长的有效string来说,效果是非常好的,但是被test_long_invalid中的正则expression式打败了(大概是因为正则expression式可以立即释放,但翻译总是要扫描整个string)。 设置的方法通常是最糟糕的,仅在空string情况下才能跳过正则expression式。

使用all(x中的allowed_set代替s中的x)如果提前释放,则执行得很好,但如果必须迭代每个字符,则可能会很糟糕。 isSubSet和set的差别是可比的,并且不pipe数据如何,都与string的长度保持一致。

匹配所有有效字符的正则expression式与search无效字符之间也有类似的区别。 在检查一个长而完全有效的string时,匹配会更好一些,但对于string末尾的无效字符来说,会更糟糕。

有很多方法可以达到这个目标,有些比另外一些更清楚。 对于我的每个例子,“True”意味着传递的string是有效的,“False”意味着它包含无效字符。

首先,有一个天真的方法:

 import string allowed = string.letters + string.digits + '_' + '-' def check_naive(mystring): return all(c in allowed for c in mystring) 

然后使用正则expression式,你可以用re.match()来做到这一点。 请注意,' – '必须位于[]的末尾,否则将用作'范围'分隔符。 还要注意$这意味着“string的结尾”。 在这个问题中提到的其他答案使用了一个特殊的字符类'\ w',我总是更喜欢用[]使用明确的字符类范围,因为它更容易理解,而不必查找快速参考指南,案件。

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

另一个解决scheme指出,你可以做一个与正则expression式相反的匹配,我已经在这里包括了。 请注意,[^ …]反转了字符类,因为使用了^:

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

你也可以用'set'对象做一些棘手的事情。 看看这个例子,它从原始string中删除了所有允许的字符,给我们留下了一个包含a)nothing或者b)string中有问题的字符的集合:

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

如果不是破折号和下划线,最简单的解决scheme是

 my_little_string.isalnum() 

(Python库参考的第3.6.1节)

作为使用正则expression式的替代方法,你可以在集合中做到这一点:

 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) 

那么你可以问正则expression式的帮助,这里的伟大:)

码:

 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 

希望这可以帮助 :)

你总是可以使用列表理解,并检查所有的结果,这将是一个比使用正则expression式less一点资源密集: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])

这里有一些基于耶鲁的“天真的方法”(天真是他的话,不是我的!):

 import string ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-') def check(mystring): return all(c in ALLOWED for c in mystring) 

如果ALLOWED是一个string,那么我认为c in ALLOWED将涉及遍历string中的每个字符,直到find匹配或达到结束。 引用乔尔· 斯波斯基( Joel Spolsky),这是一个什莱姆尔画家algorithm 。

但是在集合中testing存在应该更有效率,或者至less更less依赖于允许的字符数量。 当然这种方法在我的机器上快一点。 很明显,我认为它在大多数情况下performance得足够好(在我慢的机器上,我可以在几分之一秒内validation成千上万个短小的string)。 我喜欢。

实际上,在我的机器上,正则expression式的工作速度要快几倍,而且就像这样简单(可以说更简单)。 所以这可能是最好的前进方向。

使用正则expression式,看它是否匹配!

 ([az][AZ][0-9]\_\-)*