- 正则表达式是对字符串操作的一种逻辑公式,就是用实现定义好的一些特定字符,及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑(可以用来做检索、截取或者替换操作)。
- 作用:
- 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”)
- 可以通过正则表达式从字符串中获取我们想要的特定部分
- 还可以对目标字符串进行替换操作
- 基本函数:
Python中通过标准库中的re模块支持正则表达式。re模块提供一个根据正则表达式进行查找、替换、分割字符串的函数。这些函数使用正则表达式作为第一个参数。
match函数的使用
re.match(pattern, string, flags=0)
re.match()尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就会返回None
re.search()则是全文搜索。
import re
pattern = "hello"
s = "hello world"
result = re.match(pattern, s)
ss = "world, hello"
result = re.search(pattern, ss)
print(result.group())
print(result.span())
re.match()是一个较为宽松的匹配机制,它只关心字符串的开头是否符合模式。只要开头匹配上,哪怕字符串后面跟着一堆乱七八糟的字符,其也会返回一个Match对象;但是re.fullmatch()匹配则更加严格,它要求整个字符串从第一个字符到最后一个字符,必须百分之百匹配正则表达式,多一个字符不行,少一个字符也不行。从本质上来讲,使用re.fullmatch()等同于在正则表达式强行加上了字符串结束锚点\Z(或$),即re.fullmatch(pattern, text) <=> re.match(pattern\Z, text)
import re
pattern = r'\d{3}'
text = "123abc123"
text = "123"
常用匹配符
| 符号 |
描述 |
| . |
匹配任意一个字符(除了\n) |
| [] |
匹配列表中的字符 |
| \w |
匹配字母、数字、下划线,即a-z,A-Z,0-9,_ |
| \W |
匹配不是字母、数字、下划线 |
| \s |
匹配空白字符,即空格(\n,\t) |
| \S |
匹配不是空白的字符 |
| \d |
匹配数字,即0-9 |
| \D |
匹配不是数字的字符 |
- 一个正则表达式是由字母、数字和特殊字符(括号、星号、问号等)组成。正则表达式中有许多特殊的字符,这些特殊的字符是构成正则表达式的要素。
import re
pattern = '.'
s = "hello world"
result = re.match(pattern, s)
s = '\n'
result = re.match(pattern, s)
pattern = r'\w\w\w\w'
s = "7l_Lchenxing"
result = re.match(pattern, s)
pattern = r'\W\W\W'
s = "s()..."
result = re.match(pattern, s)
s = "()[]"
result = re.match(pattern, s)
pattern = '[13579]'
print(re.match(pattern, s))
pattern = r'1[35789]\d\d\d\d\d\d\d\d\d'
限定符
- 从上面的示例中可以看到如果要匹配手机号码,需要形如
\d\d\d\d\d\d\d\d\d\d\d这样的正则表达式。其中表现了11次\d,表达式烦琐。正则表达式作为一门小型的语言,还提供了对表达式的一部分重复处理的功能。例如,*可以对正则表达式的某个部分重复匹配多次。这种匹配符号称为限定符。
| 符号 |
描述 |
符号 |
描述 |
| * |
匹配零次或多次 |
{m} |
重复m次 |
| + |
匹配一次或多次 |
{m,n} |
重复m到n次,其中n可以省略,表示m到任意次 |
| ? |
匹配一次或零次 |
{m,} |
至少m次 |
import re
pattern = r'\d*'
s = "123456abc"
s = "abc123456"
pattern = r'\d+'
s = "abc123456"
s = "123456abc"
pattern = r'\d?'
s = "a1"
s = "11a"
pattern = r'\d{4}'
s = "12345bc"
s = "123abc4"
pattern = r'\d{4,}'
s = "123456abc"
s = "123abc"
pattern = r'\d{2,5}'
s = "1abc"
s = "123abc"
s = "123456"
限定符使用示例
- 匹配出一个字符串首字母为大写字符,后面都是小写字符,这些小写字符可有可无
import re
pattern = r'[A-Z][a-z]*'
s = "Hello world"
s = "HEllo world."
import re
pattern = r'[A-Za-z_][A-Za-z0-9_]*'
s = "a_"
s = "0a"
s = "mm"
pattern = r'[A-Za-z_]\w*'
import re
pattern = r'[1-9][0-9]?'
s = "100"
s = "0"
- 匹配出一个随机密码8~20位以内(大写字母 小写字母 下划线 数字)
import re
pattern = r'\w{8,20}'
s = "chenxing0000"
s = "123456"
边界字符
| 字符 |
功能 |
| ^ |
匹配字符串开头 |
| $ |
匹配字符串结尾 |
| \b |
匹配一个字符的边界 |
| \B |
匹配非单词的边界 |
- 注意:
^与[^m]中的"“的含义并不相同,后者”"表示”除了…“的意思。
- 边界字符本身不匹配任何实际的字符,它们只匹配位置。
^匹配字符串的开头。如果开启了多行模式(re.MULTILINE),它会匹配每一行的开头。
$匹配字符串的结尾,在多行模式下,匹配每一行的结尾。
- 例如:你需要从一堆文本中严格校验并提取符合
flag{...}格式的字符串,且该字符串必须独占一行:
import re
text = """
flag{this_is_a_fake_flag}
trailing_garbage
flag{correct_crypto_fake_format}
prefix flag{another_fake_flag}
"""
pattern = r'flag\{.*?\}'
pattern = re.compile(r'^flag\{.*?\}$', re.MULTILINE)
- 单词边界:
\b和\B
\b:匹配一个单词的边界。也就是指单词和非单词(如空格、标点符号或字符串开头/结尾)之间的那个隐形位置。在正则表达式中,“单词”通常指字母、数字和下划线(\w)
\B:匹配非单词边界。即要求当前位置的前后要么都是单词字符,要么都是非单词字符。
- 假如你正在审计一段代码或分析日志系统,需要找出所有的独立单词
admin,但不能把administrator或superadmin给误报出来:
log_entry = "The admin user logged in. Superadmin failed. Need to contact the administrator."
pattern = r'admin'
pattern = r'\badmin\b'
pattern = r'\b\w*\Badmin\b|\badmin\B\w*\b'
- 详细解释一下这里的
\b与·\B,其实就是当边界符出现的时候,他会去判断此时非非边界符的正则表达式匹配到的子串的两边,例如\badmin会去判断admin的左边是不是单词边界(空格、\t等),如果使得话,这次才算是完全的匹配。拿\b\w*\Badmin\b举个例子,当正则表达式匹配到Superadmin时,admin的左边是Super,其中\B会去判定,r和admin之间是不是单词边界,对于\B来说答案是True,所以满足条件,之后\w*\Badmin就匹配成功了,而最外侧的两个\b则是要求Superadmin两侧都是单词边界,也就是类似于... Superadmin ...的清苦那个才算是我们此时的正则表达式的要求。
- 绝对字符串起止边界:
\A和\Z:
\A:永远只匹配整个字符串的绝对开头
\Z:永远只匹配整个字符串的绝对结尾
区别:无论是否开启re.MULTILINE模式,\A和\Z都不会受到换行符的影响,只看整个大文本的头和尾。
search函数的使用
search是在一个字符串中搜索满足文本模式的字符串,其格式如下:
re.search(pattern, string, flags=0)
- 函数参数与
match类似:
| 参数 |
描述 |
| pattern |
匹配的正则表达式 |
| string |
要匹配的字符串 |
| flags |
标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。如下表列出正则表达式修饰符-可选标志 |
re.match()只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search()匹配整个字符串,直到找到一个匹配。
import re
pattern = r'abc'
s = "abcdefabc"
result = re.search(pattern, s)
s = "defabc"
s = "defdef"
s = "deAbcf"
s = "Abcdefabc"
- 择一匹配(
|)的使用
search方法搜索一个字符串,要想搜索多个字符串,如搜索aa、bb、cc,最简单的方法是在文本模式字符串中使用择一匹配符号(|)。择一匹配符合和逻辑或类似,只要满足任何一个就算匹配成功。
pattern = r'aa|bb|cc'
s = "aa"
s = "bb"
s = "where is cc"
pattern = r'\d{1,2}|100$'
s = "56"
s = "0"
s = "101"
s = "100"
pattern = r'\d{1,2}$|100'
s = "101"
- 如果待匹配的字符串中,某些字符可以有多个选择,就可以使用字符集
[],也就是一对中括号括起来的字符串。例如,[xyz]表示x、y、z三个字符中取其中任何一个,相当于x|y|z,所以对单个字符使用或关系时,字符集和择一匹配的效果是一样的。
- 两者并不完全等效,其中
[]对于排列组合式的效果更好,而|对于特定或关系效果更好:
pattern = r'[ab][cd]'
pattern = r'ac|cd'
pattern = r'apple|banana'
分组
- 如果一个模式字符串中有用一对圆括号括起来的部分,那么这部分就会作为一个分组,可以通过
group方法的参数获取指定的组匹配的字符串。当然,如果模式字符串中没有任何用圆括号括起来的部分,那么就不会对待匹配的字符串进行分组。
| 字符 |
功能 |
| (ab) |
将括号中的字符作为一个分组 |
| \num |
引用分组num匹配到的字符串 |
| (?P<name>) |
分别起组名 |
| (?P=name) |
引用别名为name分组匹配到的字符串 |
import re
pattern = r'(\d+)-(\d{5,8}$)'
phonenumber = "010-66668888"
result = re.match(pattern, phonenumber)
import re
pattern = r'<.+><.+>.+</.+></.+>'
s = "<html><head>分组的使用</head></html>"
s = "<html><head>分组的使用</body></h1>"
pattern = r'<(.+)><(.+)>.+</\2></\1>'
s = "<html><head>分组的使用</head></html>"
s = "<html><head>分组的使用</body></h1>"
pattern = r'<(?P<k1>.+)><(?P<k2>.+)>.+</(?P=k2)></(?P=k1)>'
s = "<html><head>分组的使用</head></html>"
s = "<html><head>分组的使用</body></h1>"
- 使用分组的注意事项:
- 只有圆括号括起来的部分才算一组,如果模式字符串中既有圆括号括起来的部分,也有没有被圆括号括起来的部分,那么只会将圆括号括起来的部分算作一组,其他部分忽略。
- 用
group()方法获取指定分组时,组是从1开始,也就是说,group(1)获取第一组的值,group(2)获取的是第二组的值。
groups()方法获取所有分组的值,以元组的形式返回。除了使用group(1)获取第1组的值外,还可以用groups()[0]获取第一组的值,以此类推。
- 针对于
sub(pattern, repl, string)函数的使用过程中,如果要在repl中调用pattern中的分组,对于命名别名的方式与上述表格一致,但是在调用时则需要使用r'\g<k1>'这种引用规则了,亦或者直接使用r'\1'这样的\num形式。
re模块中其他常用的函数
sub和subn搜索与替换
sub与subn函数用于实现搜索和替换功能。这两个函数的功能几乎完全相同,都是将某个字符串中所有匹配正则表达式的部分替换成其他字符串。用来替换的部分可能是一个字符串,也可以是一个函数,该函数返回一个用来替换的字符串。sub函数返回替换后的结果,subn函数返回一个元组,元组的第1个元素是替换后的结果,第2个元素是替换的总数。语法如下:
re.sub(pattern, repl, string, count=0, flags=0)
| 参数 |
描述 |
| pattern |
匹配的正则表达式 |
| repl |
替换的字符串,也可为一个函数 |
| string |
要查找替换的字符串 |
| count |
模式匹配后替换的最大次数,默认0表示替换所有匹配 |
import re
phone = "2004-959-559 # 这是一个国外电话号码"
pattern = r'\s#.+'
result = re.sub(pattern, "", phone)
result = re.subn(pattern, "", phone)
compile函数
compile函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()和search()这两个函数使用。语法结构如下:
re.compile(pattern[, flags])
| 参数 |
描述 |
| pattern |
一个字符串形式的正则表达式 |
| flags |
可选,表示匹配模式,比如忽略大小写,多行模式等 |
import re
s = 'one1 two2 three3'
pattern = r'\w+'
regex = re.compile(pattern)
findall函数
- 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。语法如下:
re.findall(pattern, string, flags=0)
| 参数 |
描述 |
| pattern |
匹配的正则表达式 |
| string |
要匹配的字符串 |
| flags |
标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
import re
pattern = r'\w+'
s = "one1 two2 three3 _-=+ @#"
result = re.findall(pattern, s)
- 注意:
match()与search()都只是匹配一次,而findall()是匹配所有。
finditer函数
- 和
findall()类似,在字符串中找到正则表达式所匹配的所有子串,并将其作为一个迭代器(内部match()返回结果类型)返回。
import re
pattern = r'\w+'
s = "one1 two2 three3 _-=+ @#"
result = re.findall(pattern, s)
pattern = r'\w+'
s = "one1 two2 three3"
result = re.finditer(pattern, s)
print(result)
for i in result:
print(i)
"""
<re.Match object; span=(0, 4), match='one1'>
<re.Match object; span=(5, 9), match='two2'>
<re.Match object; span=(10, 16), match='three3'>
"""
split函数
split()函数用于根据正则表达式分隔字符串,也就是说,将字符串与模式匹配的子字符串都作为分隔符来分割这个字符串。split()函数返回一个list对象,每个元素都是被分隔之后的子字符串。语法如下:
re.split(pattern, string[, maxsplit=0, flags=0])
| 参数 |
描述 |
| pattern |
匹配的正则表达式 |
| string |
要进行分隔的字符串 |
| maxsplit |
分隔次数,maxsplit=1,分隔一次,默认为0,表示全部满足要求的子字符串都会作为分隔符 |
| flags |
标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
pattern = r'\d'
s = "one 1 two 2 three 3 "
result = re.split(pattern, s)
print(result)
result = re.split(pattern, s, maxsplit=2)
贪婪模式与非贪婪模式
- 贪婪模式是指Python里数量词默认是尝试匹配尽可能多的字符。非贪婪模式与贪婪模式相反,总是尝试匹配尽可能少的字符,可以用
*、?、+、{m,n}后面加上?,使得贪婪变成非贪婪模式。
import re
s = "This is my telephonenumber:132-3126-6688"
pattern = r'(.+)(\d+-\d+-\d+)'
result = re.match(pattern, s)
pattern = r'(.+?)(\d+-\d+-\d+)'
result = re.match(pattern, s)
pattern = r'abc\d+'
s = "abc123"
pattern = r'abc\d+?'
Task
2025年能源网络安全大赛 – 数据脱敏
为了抵抗黑客攻击导致数据拖库等问题,需要将敏感数据识别并脱敏存储成⼀个表。给定脱敏算法逻辑,要求选⼿⽣成脱敏后的数据表数据(所有数据均为随机⽣成,与现实世界⽆任何联系)。为了防⽌⼀些隐私数据泄漏,现需要对该数据表进⾏脱敏操作,请按照指定要求对各字段进⾏脱敏处理,并按照先行后列拼接字符串(不包含标题行),对此字符串进行md5计算,得到答案。
脱敏要求:
编号:⽆需脱敏。
姓名:⼆字姓名对最后⼀位字使⽤ * 进⾏替换,三字姓名对中间⼀位字使⽤ * 进⾏替换,四字姓名对中间两位字使⽤ * 进⾏替换。
手机号:请对中间五位信息使⽤ * 进⾏替换。
身份证号码:请对除了前6位信息使⽤ * 进⾏替换。
银⾏卡:请对前四位和后十位信息使⽤ * 进⾏替换。
Email:请对字符 @ 前除 . 外的字符使⽤ * 进⾏替换。
性别:替换成未知。
微信号:请对为字符的信息使用 * 进行替换。
- 这道题目解出之后发现与网上的WP中的
flag对不上,看了一下处理方式中,只有银行卡号的处理方式不同,因此上网查了一下,一般的银行卡脱敏方式都是[银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>,因此这里还是相信一下WP中的题目描述银⾏卡:请对除前四位和后十位的信息使⽤ * 进⾏替换。
import pandas as pd
import re
import hashlib
from tqdm import tqdm
df = pd.read_excel('data.xlsx', engine='openpyxl', dtype=str)
Number = df['编号'].tolist()
Name = df['姓名'].tolist()
Phone = df['手机号'].tolist()
Idnumber = df['身份证号'].tolist()
Banknumber = df['银行卡号'].tolist()
Email = df['Email'].tolist()
Gender = df['性别'].tolist()
Wechat = df['微信号'].tolist()
def solve_name(name:str):
if len(name) == 2:
return name[0] + '*'
elif len(name) == 3:
return name[0] + '*' + name[-1]
else:
return name[0] + '**' + name[-1]
def solve_phonenumber(phone:str):
assert len(phone) == 11
pattern = r'(?P<k1>\d{3})\d{5}(?P<k2>\d{3})'
repl = r'\g<k1>*****\g<k2>'
return re.sub(pattern, repl, phone)
def solve_id(id:str):
assert len(id) == 18
repl = r'\1************'
pattern = r'(\d{6}).+'
return re.sub(pattern, repl, id)
def solve_bank(bankid:str):
return bankid[:4] + '*' * (len(bankid) - 14) + bankid[-10:]
def solve_email(email:str):
local, domain = email.split('@', 1)
local = re.sub(r'[^.]', '*', local)
return f"{local}@{domain}"
def solve_gender():
return "未知"
def solve_wechat(wechatid:str):
return '*' * len(wechatid)
if __name__ == "__main__":
ans = []
length = len(Number)
for i in tqdm(range(length)):
temp = solve_name(Name[i]) + solve_phonenumber(Phone[i]) + solve_id(Idnumber[i]) + solve_bank(Banknumber[i]) + solve_email(Email[i]) + solve_gender() + solve_wechat(Wechat[i])
ans.append(temp)
flag = 'flag{' + hashlib.md5("".join(ans).encode()).hexdigest() + '}'
print(flag)
- 这里的脱敏方式尽量采取了正则表达式,主要是为了巩固一下正则表达式的学习效果,因此看起来并不是很美观,也可能看起来会显得有些繁琐,甚至你会感觉还不如直接对字符串进行分隔,替换操作,但是解题并非现在写这道题目的目的,不然AI模型对于此类题目的解题是绝大概率能完成的。
2025年能源网络安全大赛 – 结构化数据识别
请参赛选手对给定的结构化文件《data.xlsx》进行处理,文件中包括手机号、身份证号码、银行卡号、邮箱等四类敏感数据以及干扰的脏数据(数据不满足特征)。具体数据检测规则可参考下文。参赛选手需要对数据进行清洗,编写规则,识别其中正确的敏感数据,并输出识别结果。仅当一行中所有的数据命中数据类型的规则时,才认为该行命中某一数据类型。 请参赛选手编写程序对数据进行识别,计算全部都命中的行数,计算答案的 MD5 值(MD5 值英文字符全小写,长度 32 个字符;计算答案的 MD5 值时,用 UTF-8 字符编码),并将答案的 MD5 值提交至平台。
1. 手机号是指11 位纯数字的民用手机号, 具体规则为:a) 1-3 位—— 138——运营商网络识别号,运营商包括中国移动、中国联通、中国电信、中国广电b) 4-7 位——8888——地区编码或其他(无规律)c) 8-11 位——8888——用户编码
2. 身份证号码是指18 位二代身份证号码, 具体规则为:a) 1-6 位—— 140521——为行政区代码(不定年份有修订)b) 7-14 位—— 19701231——为出生年月日,出生年月日不早于 1930 年1 月 1 日, 不晚于当前日期c) 15-17 位——543——顺序码,任意 3 位数字,第 17 位奇数为男性,偶数位女性d) 18 位——2——校验位,取值范围为 0~9 或 X。身份证号校验算法参照国标GB11643-1999《公民身份号码》。
3. 银行卡号是指19 位纯数字的银行卡号, 具体规则为:银行卡号规则是银行卡号由发卡银行标识、账户标识、校验位等部分组成。发卡银行标识为开头的6 位数字,不同的银行有不同的BIN。银行卡号的中间部分用于标识持卡人的账户信息,包括账户类型、账户分行等,通常包含12 位数字。银行卡号的最后一位通常是校验位,用于检查卡号是否合法,计算方法按照一定的算法生成。银行卡号校验位采用Luhn 算法。
4. 邮箱由用户名@域名组成,不限制长度,例如:[123456789@163.com](mailto:123456789@163.com) 具体规则为:用户名@域名,用户名可由字母、数字、下划线构成,不以下划线开始;域名:至少包含一个".",以最后一个"."分割为两个部分,前半部分由字母、数字或"."构成,"."不能在最前面;后半部分由字母或数字构成。
正确答案示例:2323行,md5后答案提交为:flag{149815eb972b3c370dee3b89d645ae14}
- 这里有两个问题,第一个是我的正确的谷歌邮箱都没办法通过上述描述的邮箱校验准则,但是对于题目给出的数据而言,不论用户名部分是否出现了
.最终邮箱校验的数目并不会改变,因此这里还是按照题目的说法进行脚本编写。
- 另外一个就是,经过不断地审查纠错发现在赛事方给出的数据中,其中身份证号是存在可以校验成功的身份证号的,但是银行卡号均没有通过校验,但是我新建的数据表格中,如果均采用我真实的正确数据的话,是可以完成校验的,因此这里认为脚本的正则匹配没有问题。最终的结果
ans = 0
import re
import pandas as pd
import gb2260
import hashlib
from tqdm import tqdm
from datetime import datetime
df = pd.read_excel('data.xlsx', engine='openpyxl', dtype=str, sheet_name='Sheet1')
IDnumber = df['col1'].tolist()
Phone = df['col2'].tolist()
Email = df['col3'].tolist()
Bank = df['col4'].tolist()
def check_idhead(idhead:int):
try:
ans = gb2260.get(idhead)
return True
except:
return False
def check_birth(birth:str):
try:
birth = datetime.strptime(birth, '%Y%m%d')
start = datetime(1930, 1, 1)
end = datetime(2025, 4, 17)
return start <= birth <= end
except:
return False
W = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]
def check_last(idnumber:str):
a = list(idnumber)
if a[-1].upper() == 'X':
ans = 10
else:
ans = int(a[-1])
for i in range(17):
ans = (ans + int(a[i]) * W[i]) % 11
return ans == 1
def check_id(idnumber:str):
if re.match(r'\d{17}[\dxX]$', idnumber):
if check_idhead(int(idnumber[:6])):
if check_birth(idnumber[6:14]):
if check_last(idnumber):
return True
return False
phonehead = [
'134', '135', '136', '137', '138', '139', '147', '148', '150', '151', '152', '157', '158', '159', '172', '178', '182', '183', '184', '187', '188', '198',
'130', '131', '132', '145', '146', '155', '156', '166', '171', '175', '176', '185', '186', '196',
'133', '149', '153', '173', '174', '177', '180', '181', '189', '190', '191', '193', '199',
'192'
]
def check_phone(phone:str):
if phone[:3] in phonehead:
if re.fullmatch(r'\d{11}', phone):
return True
return False
bankhead = [
'955880', '955881', '955882', '955888', '621225', '621226', '621227',
'95599', '622848', '622849', '623052',
'601382', '621661', '621660', '456351',
'621700', '621081', '621466', '436742',
'601428', '622260', '521899',
'621483', '621485', '621486', '410062',
'621098', '622150', '622151', '955100',
'621768', '621771', '442730',
'621489', '621492', '303781',
'621691', '621692', '472067',
'621792', '621793', '622177',
'623058', '627066', '623058',
'622909', '622908', '486861',
'621462', '621468', '406365'
]
def checkLuhn(purportedCC=''):
sum_ = 0
parity = len(purportedCC) % 2
for i, digit in enumerate([int(x) for x in purportedCC]):
if i % 2 == parity:
digit *= 2
if digit > 9:
digit -= 9
sum_ += digit
return sum_ % 10 == 0
def check_bank(banknumber:str):
if re.match(r'\d{19}$', banknumber):
if banknumber[:6] in bankhead or banknumber[:5] in bankhead:
return checkLuhn(banknumber)
return False
def check_email(email:str):
try:
local, domain = email.split('@', 1)
if re.fullmatch(r'[A-Za-z0-9]\w*', local):
try:
frond, back = domain.rsplit('.', 1)
if re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9.]*', frond):
if re.fullmatch(r'[A-Za-z0-9]+', back):
return True
except:
return False
else:
return False
except:
return False
if __name__ == "__main__":
length = min([len(Bank), len(IDnumber), len(Email), len(Phone)])
ans = 0
for i in tqdm(range(length)):
if check_bank(Bank[i]) and check_email(Email[i]) and check_id(IDnumber[i]) and check_phone(Phone[i]):
ans += 1
print(ans)
print('flag{' + hashlib.md5(str(ans).encode()).hexdigest() + '}')
所有评论(0)