当前位置: 首页 > article >正文

【python基础语法八】正则表达式

正则表达式

1. 定义和作用

  • 正则表达式是什么?
    • 它是约束字符串匹配某种形式的规则
  • 正则表达式有什么用?
    1. 检测某个字符串是否符合规则.比如:判断手机号,身份证号是否合法
    2. 提取网页字符串中想要的数据.比如:爬虫中,提取网站天气,信息,股票代码,星座运势等具体关键字
  • 在线测试工具 http://tool.chinaz.com/regex/

2. 正则表达式所包含的元素种类

  • 正则表达式由一些 [普通字符] 和一些 [元字符] 组成:
    • (1)普通字符包括大小写字母和数字
    • (2)元字符具有特殊含义,大体种类分为如下:
      1. 预定义字符集,字符组
      2. 量词
      3. 边界符
      4. 分组

(一) 匹配单个字符 => [元字符] 预定义字符集

预定义字符集匹配内容
.匹配任意字符,除了换行符\n
\d匹配数字
\D匹配非数字
\w匹配字母或数字或下划线 (正则函数中,支持中文的匹配)
\W匹配非字母或数字或下划线
\s匹配任意的空白符
\S匹配任意非空白符
\n匹配一个换行符
\t匹配一个制表符
[]匹配中括号内列举的字符
字符组格式说明 [默认必须从字符组中选一个]
[...]匹配字符组中的字符
[^...]匹配除了字符组内所有内容,之外的所有字符
字符组内容待匹配字符匹配结果说明
[0123456789]8True字符组里枚举的各种字符,必须满足一个,否则返回假,不匹配
[abcdefg]9False由于字符组中没有"9"字符,所以不匹配
[0-9]7True可用 - 表示范围,[0-9][0123456789]是一个意思
[a-z]sTrue[a-z]匹配所有的小写字母
[A-Z]BTrue[A-Z]就表示所有的大写字母
[0-9a-fA-F]eTrue可以匹配数字,大小写形式的a-f. 该正则可验证十六进制
import re
"""lst = re.findall(正则表达式,字符串)"""

# (1) 预定义字符集
# \d 匹配数字
strvar = "sdjfklj234&*(&1"
lst = re.findall("\d",strvar)
print(lst) # ['2', '3', '4', '1']

# \D 匹配非数字
strvar = "YWERsdf78_&"
lst = re.findall("\D",strvar)
print(lst) # ['Y', 'W', 'E', 'R', 's', 'd', 'f', '_', '&']

# \w 匹配字母或数字或下划线     (正则函数中,支持中文的匹配)
strvar = "sadf234_^&*%$^$%你好"
lst = re.findall("\w",strvar)
print(lst) # ['s', 'a', 'd', 'f', '2', '3', '4', '_', '你', '好']

# \W 匹配非字母或数字或下划线
strvar = "sadf234_^&*%$^$%你好"
lst = re.findall("\W",strvar)
print(lst) # ['^', '&', '*', '%', '$', '^', '$', '%']

# \s 匹配任意的空白符 ( " "  \t  \n \r )
strvar = " 		 \r "
lst = re.findall("\s",strvar)
print(lst) # [' ', '\t', '\t', ' ', '\r', ' ']

# \S 匹配任意非空白符
strvar = " 		 \r  123_*("
lst = re.findall("\S",strvar)
print(lst) # ['1', '2', '3', '_', '*', '(']

# \n 匹配一个换行符
strvar = """
今天国庆假期结束了,兄弟们满载	而归,玩的	很困,尽	快调	整.
"""
lst = re.findall(r"\n",strvar)
print(lst) # ['\n', '\n']

# \t 匹配一个制表符
lst = re.findall(r"\t",strvar)
print(lst) # ['\t', '\t', '\t', '\t']

# (2) 字符组 [] 匹配出字符组当中列举的字符(默认选一个)
# lst = re.findall("[123]","a1b2c3d4")
# print(lst) # ['1', '2', '3']

print(re.findall('a[abc]b','aab abb acb adb')) # ['aab', 'abb', 'acb']
print(re.findall('a[0123456789]b','a1b a2b a3b acb ayb')) # ['a1b', 'a2b', 'a3b']
# 优化写法 0123456789  => 0-9
print(re.findall('a[0-9]b','a1b a2b a3b acb ayb')) 

print(re.findall('a[abcdefg]b','a1b a2b a3b acb ayb adb')) # ['acb', 'adb']
# 优化写法 abcdefg => a-g  表达所有的小写字母 a-z
print(re.findall('a[a-g]b','a1b a2b a3b acb ayb adb'))
print(re.findall('a[a-z]b','a1b a2b a3b acb ayb adb')) # ['acb', 'ayb', 'adb']

print(re.findall('a[ABCDEFG]b','a1b a2b a3b  aAb aDb aYb')) # ['aAb', 'aDb']
# 优化写法 ABCDEFG => A-G  表达所有的大写字母 A-Z
print(re.findall('a[A-G]b','a1b a2b a3b  aAb aDb aYb'))
print(re.findall('a[A-Z]b','a1b a2b a3b  aAb aDb aYb')) # ['aAb', 'aDb', 'aYb']

# 注意点: 不能直接写A-z  中间ascii编码中包含了特殊的符号
print(re.findall('a[A-z]b','a1b a2b a3b acb ayb adb a[b')) # ['acb', 'ayb', 'adb', 'a[b']
# 匹配所有的字母和数字 *** 
print(re.findall('a[a-zA-Z0-9]b','a1b a2b a3b acb ayb adb a[b')) # ['a1b', 'a2b', 'a3b', 'acb', 'ayb', 'adb']


print(re.findall('a[0-9a-zA-Z]b','a-b aab aAb aWb aqba1b')) # ['aab', 'aAb', 'aWb', 'aqb', 'a1b']
print(re.findall('a[0-9][*#/]b','a1/b a2b a29b a56b a456b')) # ['a1/b']

# ^ 在字符组开头的位置出现代表 除了...的意思
print(re.findall('a[^-+*/]b',"a%b ccaa*bda&bd")) # ['a%b', 'a&b']

# 匹配^-\等特殊字符时 ,需要前面加上\进行转义
lst = re.findall(r"a[\^\-\\]c","a^c a-c a\c")
print(lst) # ['a^c', 'a-c', 'a\\c']
print(lst[-1]) # a\c


# 注意点:为了防止转义,在正则表达式中或者要匹配的字符串中,无脑加r实现匹配。 \b 也有特殊含义
strvar = r"a\b"
lst = re.findall(r"a\\b",strvar)
print(lst[0])

(二) 匹配多个字符 => [元字符] 量词符号

量词用法说明
?重复0次或1次
+重复1次或多次 (至少1次)
*重复0次或多次 (任意次)
{n}重复n次
{n,}重复n次或更多次 (至少n次)
{n,m}重复n到m次
.* .+贪婪模式匹配
.*? .+?非贪婪模式匹配
  • 贪婪匹配: 默认向更多次数匹配 (底层用的是回溯算法)
  • 非贪婪匹配: 默认向更少次数匹配 (量词的后面加?号)
    • (1)量词( * ? + {} )加上问号?表示非贪婪 惰性匹配
    • (2)例:.*?w 表示匹配任意长度任意字符遇到一个w就立即停止
# (1) 量词
import re
'''1) ? 匹配0个或者1个a '''
print(re.findall('a?b','abbzab abb aab')) # ['ab', 'b', 'ab', 'ab', 'b', 'ab']

'''2) + 匹配1个或者多个a '''
print(re.findall('a+b','b ab aaaaaab abb')) # ['ab', 'aaaaaab', 'ab']

'''3) * 匹配0个或者多个a '''
print(re.findall('a*b','b ab aaaaaab abbbbbbb')) # ['b', 'ab', 'aaaaaab', 'ab', 'b', 'b', 'b', 'b', 'b', 'b']

'''4) {m,n} 匹配m个至n个a '''
# 1 <= a <= 3 
print(re.findall('a{1,3}b','aaab ab aab abbb aaz aabb')) # ['aaab', 'ab', 'aab', 'ab', 'aab']
# {2}  代表必须匹配2个a
print(re.findall('a{2}b','aaab ab aab abbb aaz aabb')) # ['aab', 'aab', 'aab']
# {2,} 代表至少匹配2个a
print(re.findall('a{2,}b','aaab ab aab abbb aaz aabb')) # ['aaab', 'aab', 'aab']


# (2) 贪婪匹配 和 非贪婪匹配
"""
贪婪匹配  : 默认向更多次匹配  (回溯算法)
非贪婪匹配: 默认向更少次匹配  (配合?号使用)    

回溯算法 : 从左向右进行匹配,直到找到最后一个,再也没有了,回头,返回上一个找到的内容
. 除了\n,匹配所有字符

非贪婪写法: 量词 + ? 

"""

# 贪婪匹配(模式)
strvar = "刘能和刘老根和刘罗锅111子222子"
lst = re.findall("刘.",strvar)
print(lst) # ['刘能', '刘老', '刘罗']

lst = re.findall("刘.?",strvar)
print(lst) # ['刘能', '刘老', '刘罗']

lst = re.findall("刘.+",strvar) # 至少一个,默认就匹配多个(贪婪模式)
print(lst) # ['刘能和刘老根和刘罗锅111子222子']

lst = re.findall("刘.*",strvar)
print(lst) # ['刘能和刘老根和刘罗锅111子222子']

lst = re.findall("刘.{1,20}",strvar)
print(lst) # ['刘能和刘老根和刘罗锅111子222子']

lst = re.findall("刘.*子",strvar)
print(lst) # ['刘能和刘老根和刘罗锅111子222子']

# 非贪婪匹配(模式)
lst = re.findall("刘.??",strvar)
print(lst) # ['刘', '刘', '刘']

lst = re.findall("刘.+?",strvar)
print(lst) # ['刘能', '刘老', '刘罗']

lst = re.findall("刘.*?",strvar)
print(lst) # ['刘', '刘', '刘']

lst = re.findall("刘.{1,20}?",strvar)
print(lst) # ['刘能', '刘老', '刘罗']

lst = re.findall("刘.*?子",strvar)
print(lst) # ['刘能和刘老根和刘罗锅111子']

(三) 匹配开头结尾 => [元字符] 边界符号

边界符说明
\b匹配一个字符的边界
^匹配字符串的开始
$匹配字符串的结尾
# (3) 边界符
"""
\b 本身是转义字符 退格,退到光标上一位
\b 在正则中还有边界符的意思

"word"
卡主左边界:\bw
卡主右边界:d\b
"""
strvar = "word old fuck"
# 右边界
lst = re.findall(r"d\b",strvar) # ['d', 'd']
lst = re.findall(r".*d\b",strvar) # ['word old']
lst = re.findall(r".*?d\b",strvar) # ['word', ' old']
print(lst) 

# 左边界
lst = re.findall(r"\bw",strvar) # ['w']
lst = re.findall(r"\bw.*",strvar) # ["word old fuck"]
lst = re.findall(r"\bw.*?",strvar) # ['w']
lst = re.findall(r"\bw.*? ",strvar) # ['word '] 空格在正则表达式中,不能随意加,是参与匹配的.
lst = re.findall(r"\bw\S*",strvar) # ['word']

# (4) ^ $的使用
"""
^ 写在在字符串的开头,表达必须以某个字符开头
$ 写在在字符串的结尾,表达必须以某个字符结尾
当使用了^ $ 代表要把该字符串看成一个整体
"""

strvar = "大哥大嫂大爷"
print(re.findall('大.',strvar))  # ['大哥', '大嫂', '大爷']
print(re.findall('^大.',strvar)) # ['大哥']
print(re.findall('大.$',strvar)) # ['大爷']
print(re.findall('^大.$',strvar))# []
print(re.findall('^大.*?$',strvar))   # ['大哥大嫂大爷']
print(re.findall('^大.*?大$',strvar)) # []
print(re.findall('^大.*?爷$',strvar)) # ['大哥大嫂大爷']


print(re.findall('^g.*? ' , 'giveme 1gfive gay')) # ['giveme']
print(re.findall('five$' , 'aassfive')) # [five]
print(re.findall('^giveme$' , 'giveme')) # ['giveme']
print(re.findall('^giveme$' , 'giveme giveme')) # []
print(re.findall('giveme' , 'giveme giveme')) # ['giveme', 'giveme']
print(re.findall("^g.*e",'giveme 1gfive gay')) # ['giveme 1gfive']

(四) 匹配分组 => [元字符] 分组符号

分组用法说明
a|b匹配字符a 或 字符b (如果两个当中有重合部分,把更长的那个放前面)
(ab)匹配括号内的表达式 ,将()作为一个分组
\num引用分组num匹配到的字符串
(?P<name>)给分组命名
(?P=name)引用别名: 把name分组所匹配到的内容引用到这里
  • 分组
    1. 正常分组()
      a. 正常情况下用()圆括号进行分组 可以用 \1 反向引用第一个圆括号匹配的内容。
      b. (?:正则表达式) 表示取消优先显示的功能
      (正则表达式) 代表分组 findall 把所有分组的内容显示到列表里
      (?:正则表达式) 代表分组 findall 把所有分组的内容不显示到列表里

    2. 命名分组
      c. (?P<组名>正则表达式) 给这个组起一个名字
      d. (?P=组名) 引用之前组的名字,把该组名匹配到的内容放到当前位置

# 匹配分组 ()表达整体
import re
# (1)分组
print(re.findall('.*?_good','wusir_good alex_good secret男_good')) # ['wusir_good', ' alex_good', ' secret男_good']
print(re.findall('(.*?)_good','wusir_good alex_good secret男_good')) # ['wusir', ' alex', ' secret男']

# (?:) 代表不优先显示分组里面的内容,只是显示正常匹配到的内容
print(re.findall('(?:.*?)_good','wusir_good alex_good secret男_good')) # ['wusir_good', ' alex_good', ' secret男_good']


# (2) | 代表或 , a|b 匹配字符a 或者 匹配字符b . 
strvar = "abceab"
lst = re.findall("a|b",strvar)
print(lst) # ['a', 'b', 'a', 'b']

# 注意点:把不容易匹配到的内容放到前面,把容易匹配到的内容放到后面
strvar = "abcdeabc234f"
lst = re.findall("abcd|abc",strvar)
# re.findall("abc|abcd",strvar) # ['abc', 'abc']
print(lst) # ['abcd', 'abc']

# (3) 练习
"""
.  可以匹配任意的字符,除了\n
\. 对.进行转义,表达.这个字符本身.
"""
# 匹配小数 
strvar = "3....  ....4  .3 ...3   1.3  9.89  10"
lst = re.findall(r"\d+\.\d+",strvar)
print(lst) # ['1.3', '9.89']

# 匹配小数和整数 
lst = re.findall(r"\d+\.\d+|\d+",strvar)
print(lst) # ['3', '4', '3', '3', '1.3', '9.89', '10']

# 使用分组改造
'''findall优先显示括号里的内容,需要加上?:取消优先显示,按照匹配到的内容显示'''
lst = re.findall(r"\d+(?:\.\d+)?",strvar)
print(lst) # ['3', '4', '3', '3', '1.3', '9.89', '10']


# 匹配135或171的手机号 
strvar = "13566668888 17366669999 17135178392"
lst = re.findall(r"(?:135|171)\d{8}",strvar)
print(lst) # ['13566668888', '17135178392']

# 优化,只能匹配出一个手机号
strvar = "13566668888"
lst = re.findall(r"^(?:135|171)\d{8}$",strvar)
print(lst) # ['13566668888']
obj = re.search(r"^(135|171)\d{8}$",strvar)
print(obj) # <_sre.SRE_Mathc object; span=(0,11), match="13566668888">
print(obj.group()) # 13566668888
print(obj.groups()) # ('135',)


# 匹配www.baidu.com 或者 www.google.com
"""
findall : 从左到右,匹配出所有的内容,返回到列表
		  问题是: 匹配到的字符串和分组的内容不能同时显示;

search  : 从左到右,匹配到一组内容就直接返回,返回的是对象
		  优点是: 可以让匹配到的内容和分组里的内容同时显示;
		  匹配不到内容时,返回的是None
		  
		obj.group() : 获取匹配到的内容
		obj.groups(): 获取分组里面的内容
"""
# findall
strvar = "www.baidu.com  www.google.com  www.taobao.com"
lst = re.findall(r"(?:www)\.(?:baidu|google)\.(?:com)",strvar)
print(lst) # ['www.baidu.com', 'www.google.com']

# search
strvar = "www.baidu.com  www.google.com  www.taobao.com"
obj = re.search(r"(www)\.(baidu|google)\.(com)",strvar)
print(obj) # 只匹配到一组内容就返回

# 获取匹配到的内容
print(obj.group()) # www.baidu.com
# 获取分组里面的内容 (推荐)
print(obj.groups()) # ('www', 'baidu', 'com')

# 方法二,可以直接通过下标1来获取分组里面的第一个内容;
print(obj.group(1)) # www
print(obj.group(2)) # baidu
print(obj.group(3)) # com

# search 练习 : 计算"5*6-7/3"结果  匹配 5*6 或者 7/3
strvar =  "5*6-7/3"
obj = re.search(r"\d+[*/]\d+",strvar)
res1 = obj.group()
print(res1 , type(res1)) # 5*6 <class 'str'>

# 计算结果
a,b = res1.split("*")
res2 = int(a) * int(b)
print(res2)

# 把30替换回原来的字符串中
strvar = strvar.replace(res1,str(res2))
print(strvar)

命名分组与反向引用

# ### 反向引用
import re
strvar = "<div>明天又要休息了</div>"
obj = re.search("<(.*?)>(.*?)<(.*?)>",strvar)
print(obj) # search object

# 获取匹配到的内容
res1 = obj.group()
print(res1) # <div>明天又要休息了</div>

# 获取分组里的内容
res2 = obj.groups()
print(res2) # ('div', '明天又要休息了', '/div')

# 反向引用的语法 \1 把第一个括号里面匹配到的内容在引用一次
obj = re.search(r"<(.*?)>(.*?)</\1>",strvar)
print(obj)
print(obj.group()) # <div>明天又要休息了</div>
print(obj.groups()) # ('div', '明天又要休息了')

strvar = " z3d4pzd a1b2cab "
obj = re.search(r"(.*?)\d(.*?)\d(.*?)\1\2",strvar)
print(obj)
print(obj.group()) # z3d4pzd
print(obj.groups()) # ('z', ''d, 'p')


# ### 命名分组
"""
(?P<组名>正则表达式) 给这个组起一个名字
(?P=组名) 引用之前组的名字,把该组名匹配到的内容放到当前位置
"""
# 写法一
strvar = " z3d4pzd a1b2cab "
obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)\1\2",strvar)
print(obj)
print(obj.group()) # z3d4pzd


# 写法二
strvar = " z3d4pzd a1b2cab "
obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)(?P=tag1)(?P=tag2)",strvar)
print(obj)
print(obj.group()) # z3d4pzd

(五) 正则表达式修饰符

compile 函数处使用

常用修饰符说明
re.I使匹配对大小写不敏感
re.M使每一行都能够单独匹配(多行匹配),影响 ^$
re.S使 . 匹配包括换行在内的所有字符

3. 正则函数

1. findall 匹配字符串中相应内容,返回列表

"""用法: findall("正则表达式","要匹配的字符串")"""

2. search 通过正则匹配出第一个对象返回,通过group取出对象中的值

import re

strvar = "3+4 6*4"
obj = re.search(r"(\d+[+*]\d+)",strvar)
print(obj) # search object
# 获取匹配到的内容
print(obj.group()) # 3+4
# 获取分组当中的内容 (返回元组)
print(obj.groups()) # ('3+4',)

3. match 验证用户输入内容 (了解)

"""search在正则表达式的前面加上^ 等价于 match ,其他用法上一模一样"""
strvar = "a17366668888"
strvar = "17366668888"
obj = re.search(r"\d+",strvar) # 17366668888
obj = re.match(r"\d+",strvar) # None
obj = re.search(r"^\d+",strvar) # None 与match等价
print(obj.group())

4. split 切割 ***

strvar = "alex|wusir_xboyww@risky"
lst = re.split("[|_@]",strvar) # 以多种分隔符分割
print(lst) # ['alex', 'wusir', 'xboyww', 'risky']

strvar = "alex2341273894wusir234234xboyww11111risky"
lst = re.split("\d+",strvar)
print(lst) # ['alex', 'wusir', 'xboyww', 'risky']

5. sub 替换 ***

strvar = "alex|wusir_xboyww@risky"
"""
strvar = strvar.replace("|","&").replace("_","&").replace("@","&")
print(strvar) # alex&wusir&xboyww&risky
"""
# sub(正则, 替换的字符, 原字符串 [,替换的次数])
res = re.sub("[|_@]","&",strvar) # alex&wusir&xboyww&risky
res = re.sub("[|_@]","&",strvar,1) # alex&wusir_xboyww@risky
print(res)

6. subn 替换 (用法上与sub相同,只是返回值不同)

res = re.subn("[|_@]","&",strvar) # ('alex&wusir&xboyww&risky', 3) # 替换了几次
res = re.subn("[|_@]","&",strvar,2) # ('alex&wusir&xboyww@risky', 2)
print(res)

7. finditer 匹配字符串中相应内容,返回迭代器

"""返回的是迭代器,迭代器中包含了对象 对象.group来获取匹配到的值"""
from collections import Iterator, Iterable

strvar = "sdf23647fdgdfg()*()*23423423"
it = re.finditer("\d+",strvar)
print(isinstance(it,Iterator)) # True

for obj in it: # 迭代器里每一个是 search 对象
	print(obj.group())

8. compile 指定一个统一的匹配规则

"""
正常情况下,正则表达式编译一次,执行一次
为了避免反复编译,节省时间空间,可以使用compile统一规则
编译一次,终身受益
"""
strvar = "asdfs234sdf234"
pattern = re.compile("\d+") # 正则对象

obj = pattern.search(strvar)
print(obj.group()) # 234

lst = pattern.findall(strvar)
print(lst) # ['234', '234']

使用修饰符

# re.I 使匹配对大小写不敏感
strvar = "<h1>大标题</H1>"
pattern = re.compile("<h1>(.*?)</h1>" , flags=re.I)
obj = pattern.search(strvar)
print(obj.group())


# re.M 使每一行都能够单独匹配(多行匹配),只影响 ^ 和 $
"""单行独立匹配,而不是整体匹配"""
strvar = """
<p>111</p>
<a>222</a>
<strong>333</strong>
"""

pattern = re.compile("^<.*?>(?:.*?)<.*?>$" , flags=re.M)
lst = pattern.findall(strvar)
print(lst) # ['<p>111</p>', '<a>222</a>', '<strong>333</strong>']

# re.S 使 . 匹配包括换行在内的所有字符
strvar = """
give
sdfsdfmefive
"""

# 多个修饰符一起使用通过|拼接
pattern = re.compile(".*?mefive" , flags = re.S|re.I|re.M )
obj = pattern.search(strvar)
print(obj.group())

http://www.kler.cn/a/16749.html

相关文章:

  • Spring Boot 1.x 版本可以集成 Spring Cloud Sleuth
  • Sigrity SPEED2000 Power Ground Noise Simulation模式如何查看PDS系统的自阻抗操作指导
  • mac终端使用pytest执行iOS UI自动化测试方法
  • 《MYSQL45讲》误删数据怎么办
  • MyBatis CRUD快速入门
  • 鸿蒙next版开发:相机开发-元数据(ArkTS)
  • MIT教授Tegmark:GPT-4敲响警钟,百年后人类何去何从丨智源大会嘉宾风采
  • 数据帧去掉VlanTag的代码(802.1Q)
  • go 语言环境安装(Windows 系统下安装)
  • ( 数组和矩阵) 566. 重塑矩阵 ——【Leetcode每日一题】
  • osg::Drawable类通过setDrawCallback函数设置回调函数的说明
  • 构建ChatGPT 镜像,并将其部署到 Docker 容器中。
  • 基于Matlab刻度盘识别角度计算
  • C++:计算机操作系统:多线程:高并发中的线程
  • ViveNAS - 一个基于LSM tree的文件存储实现 (一)
  • C++ srand()和rand()用法
  • hadoop伪分布式搭建教程
  • 【react从入门到精通】React JSX详解
  • pytorch学习率设置——optimizer.param_groups、对不同层设置学习率、动态调整学习率。
  • Java中几种常量池面试总结
  • OVS常用命令与使用总结
  • Python_PyStray结合Tkinter显示系统托盘图标
  • SpringMVC与SpringWebFlux
  • 【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?
  • MEET开发者 | 选择和努力一样重要,专访杭州三汇测试工程师齐雪莲
  • c++标准模板(STL)(std::array)(三)