关于python:如何验证字符串仅包含字母,数字,下划线和破折号?

关于python:如何验证字符串仅包含字母,数字,下划线和破折号?

How do I verify that a string only contains letters, numbers, underscores and dashes?

如果我遍历字符串中的所有字符,我知道该怎么做,但是我正在寻找一种更优雅的方法。


正则表达式只需很少的代码即可完成操作:

1
2
3
4
5
6
import re

...

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

[编辑]还有一个尚未提及的解决方案,在大多数情况下,它似乎胜过迄今为止给出的其他解决方案。

使用string.translate替换字符串中的所有有效字符,然后查看是否还有剩余的无效字符。这非常快,因为它使用基础C函数来完成工作,并且只涉及很少的python字节码。

显然性能不是全部-在性能不关键的代码路径中,采用可读性最高的解决方案可能是最好的方法,而只是为了查看解决方案的堆叠方式,这里是所有方法的性能比较到目前为止提出。 check_trans是使用string.translate方法的那个。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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()

我的系统上的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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,则在allow_set中使用x在allowed_set中)表现良好,但是如果必须遍历每个字符,则可能会很糟糕。 isSubSet和set差异是可比较的,并且与字符串的长度成正比,而与数据无关。

匹配所有有效字符和搜索无效字符的regex方法之间存在相似的区别。在检查一个长而完全有效的字符串时,匹配的性能要好一些,但在字符串末尾附近的无效字符时,匹配性能会更差。


有多种方法可以实现此目标,有些方法比其他方法更清晰。对于我的每个示例,\\'True \\'表示传递的字符串是有效的,\\'False \\'表示它包含无效的字符。

首先,有一种幼稚的方法:

1
2
3
4
5
import string
allowed = string.letters + string.digits + '_' + '-'

def check_naive(mystring):
    return all(c in allowed for c in mystring)

然后使用正则表达式,您可以使用re.match()进行此操作。请注意,\\'-\\'必须位于[]的末尾,否则它将用作\\'range \\'分隔符。还要注意$,它表示\\'字符串的结尾\\'。这个问题中提到的其他答案使用特殊的字符类\\'\\\\\\\\ w \\',我总是更喜欢使用[]作为显式的字符类范围,因为它更容易理解,而无需查找快速参考指南,而且更容易出现特殊情况。

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

另一种解决方案指出,您可以对正则表达式进行逆向匹配,我现在将其包括在此处。请注意,[^ ...]会反转字符类,因为使用了^:

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

您也可以使用\\'set \\'对象做一些棘手的事情。看一下这个示例,该示例从原始字符串中删除了所有允许的字符,从而为我们提供了一个包含以下内容的集合:a)无,或b)字符串中令人反感的字符:

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

如果不使用破折号和下划线,则最简单的解决方法是

1
my_little_string.isalnum()

(Python库参考的3.6.1节)


作为使用正则表达式的替代方法,您可以在Sets中进行:

1
2
3
4
5
6
7
from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True

1
2
3
4
 pat = re.compile ('[^\\w-]')

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

正则表达式可以非常灵活。

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

\\w:仅[a-zA-Z0-9_]

因此您需要添加- char以证明连字符char。

+:匹配前一个字符的一个或多个重复。我猜您不接受空白输入。但是,如果这样做,请更改为*

^:匹配字符串的开头。

$:匹配字符串的结尾。

您需要使用这两个特殊字符,因为您需要避免以下情况。像&这样的多余字符可能会出现在匹配的模式之间。

&&&PATTERN&&PATTERN


好吧,您可以向regex寻求帮助,这里很棒:)

代码:

1
2
3
4
5
6
7
8
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'

输出:

1
yes

希望这会有所帮助:)


这是基于Jerub "天真的方法"(天真是他的话,不是我的话!)的内容:

1
2
3
4
5
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的Painter算法。

但是测试集中是否存在应该更有效,或者至少更少地依赖于允许的字符数。当然,这种方法在我的机器上要快一些。很明显,我认为在大多数情况下它的性能都足够好(在我的慢速机器上,我可以在不到一秒钟的时间内验证成千上万个短字符串)。我喜欢。

实际上,在我的计算机上,正则表达式的运行速度快了好几倍,并且就这么简单(可以说更简单)。因此,这可能是最好的方法。


您始终可以使用列表推导并全部检查结果,这比使用正则表达式要少一些资源:all( for c in mystring])


使用正则表达式,看看是否匹配!

1
([a-z][A-Z][0-9]\\_\\-)*

推荐阅读

    linux输出字符串命令?

    linux输出字符串命令?,标准,基础,字符串,资料,简介,商业,数字,系统,命令,汉

    linux命令替换的字符?

    linux命令替换的字符?,命令,数据,系统,商业,字符串,文件,内容,方法,批量,字

    linux命令删除字符串?

    linux命令删除字符串?,软件,系统,代码,名称,通用,连续,字符,字符串,命令,空

    linux命令行字符隐藏?

    linux命令行字符隐藏?,系统,标准,信息,名称,代码,工具,文件夹,命令,文件,语

    linux转换字符集命令?

    linux转换字符集命令?,系统,名称,时间,位置,服务,文件,字符集,命令,格式,以

    linux字符动画命令?

    linux字符动画命令?,工作,系统,地址,信息,命令,目录,工具,时间,标准,基础,Lin

    linux命令匹配数字?

    linux命令匹配数字?,数字,档案,位置,环境,名字,较大,系统,权限,命令,用户,Lin

    linux权限数字命令?

    linux权限数字命令?,数字,系统,地址,权限,命令,标准,情况,管理,基础,文件,lin

    字符串查找命令linux?

    字符串查找命令linux?,系统,字符串,工具,信息,文件,命令,字符,选项,文本,范

    linux命令替换字符串?

    linux命令替换字符串?,字符串,文件,批量,首次,数据,命令,内容,方法,用字,结

    linux命令中转义字符?

    linux命令中转义字符?,标准,本行,密码,字符,电脑,系统,环境,数据,命令,终端,l

    linux命令行字符颜色?

    linux命令行字符颜色?,系统,地址,代码,信息,数字,软件,通用,电脑,颜色,命令,l

    linux彩色字符命令?

    linux彩色字符命令?,数字,颜色,命令,字符,文字,终端,控制台,环境变量,白色,

    linux中替换字符命令?

    linux中替换字符命令?,工作,地址,系统,命令,资料,数据,信息,商业,管理,目录,L

    linux拼接字符串命令?

    linux拼接字符串命令?,系统,工作,代码,工具,名称,信息,地址,时间,数据,命令,l

    linux图形转字符命令?

    linux图形转字符命令?,系统,电脑,密码,界面,情况,地方,工具,图形界面,字符,

    linux隐藏字符的命令?

    linux隐藏字符的命令?,工作,地址,系统,发行,信息,标准,管理,命令,目录,文件,

    linux命令行最大字符?

    linux命令行最大字符?,系统,工作,数字,地址,等级,设备,软件,信息,标准,设计,l

    添加字符串命令linux?

    添加字符串命令linux?,情况,名称,文件,位置,名字,地方,连续,信息,命令,内容,L

    linux命令行大字符?

    linux命令行大字符?,工作,地址,系统,信息,管理,第一,发行,在线,最新,标准,lin