正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。

通用字符匹配

语法 通配符匹配作用解析
. 匹配除换行符之外的任意一个字符
* 匹配前一个字符出现零次或任意多次
+ 匹配前一个字符出现1次或任意多次
? 匹配前一个字符出现1次或0次
^ 匹配以指定字符开头的数据
$ 匹配以指定字符结尾的数据
匹配前一个字符出现过m次的记录
匹配前一个字符,最少出现n次,最多出现m次

匹配任意一个字符(.) 默认匹配除\n之外的任意一个字符,若指定flag=DOTALL则匹配包括换行在内的字符.

>>> re.search("hel.o","hello lyshark,hello world").group()
'hello' >>> re.findall("hel.o","hello lyshark hello world")
['hello', 'hello']

匹配前一个字符出现0至任意多次(*) 匹配星号前面的字符出现0次,或任意多次.

>>> re.findall("ab*","abccba23acbcabb")
['ab', 'a', 'a', 'abb']

匹配前一个字符出现1次或任意多次(+) 匹配加号前面的字符出现过1次,或任意多次,至少出现一次.

>>> re.findall("ab+","abccba23acbcabb")
['ab', 'abb']

匹配前一个字符出现1次或0次(?) 匹配前一个字符出现过1次或0次,允许出现0次.

>>> re.findall("ab?","ab,abc,abb,abcd,a,acd,abc")
['ab', 'ab', 'ab', 'ab', 'a', 'a', 'ab'] >>> re.findall("ab?","ab,a,abc,abcde")
['ab', 'a', 'ab', 'ab']

**匹配开头与结尾(^\():** ^匹配指定字符开头的数据,\)匹配指定字符结尾的数字.

>>> re.search(r"^h","hello world").group()
'h'
>>> re.search(r"world$","hello\nworld").group()
'world'
>>> re.search(r"^a","\nabc\ndef",flags=re.MULTILINE).group()
'a'
>>> re.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()
'foo'

匹配前一个字符出现次数(x{m}) 匹配前一个字符x,出现过m次的行.

>>> re.search("hello{2}","hello,helloo,hellooo,helloooo").group()
'helloo' >>> re.search("hello{3}","hello,helloo,hellooo,helloooo").group()
'hellooo'

匹配前一个字符出现次数(x{n,m}) 匹配前一个字符x,最少出现过n次,最多出现过m次.

>>> re.search("hello{1,2}","hello,helloo,hellooo,helloooo").group()
'hello' >>> re.findall("hello{1,2}","hello,helloo,hellooo,helloooo")
['hello', 'helloo', 'helloo', 'helloo']

脱意字符的匹配(\) 转义字符,通常情况下使后一个字符改变原来的意思,也叫做脱意字符.

>>> re.search("..\\t","hello\t lyshark\n").group()
'lo\t'
>>> re.search("\\t","hello\t lyshark\n").group()
'\t'
>>> re.search("\t","hello\t lyshark\n").group()
'\t'
>>> re.search(r"\\","hello\\lyshark").group()
'\\'

匹配查找范围([]) 匹配查找指定的数据范围,通常使用[0-9] [a-z] [A-Z]这几个匹配格式.

>>> re.search("[0-9]","hello 1,2,3,4,5").group()   # 匹配第一次出现数字的行
'1'
>>> re.search("[0-9]","hello a12 b23 34a 45t").group()
'1'
>>> re.findall("[0-9]","hello 1,2,3,4,5") # 匹配所有出现数字的行
['1', '2', '3', '4', '5']
>>> re.findall("[0-9]","hello b23 34a 45t wan")
['2', '3', '3', '4', '4', '5']
>>> re.search("[^0-9]","hello 1,2,3,4,5").group() # 匹配开头不是0-9的单个字符
'h'
>>> re.search("[^0-9]*","hello 1,2,3,4,5").group() # 匹配开头不是0-9的单行行
'hello'
>>> re.search(r"[aeiou]","Hello LyShark").group()
'e'

匹配查找空白字符(s) 匹配空白字符

>>> re.search("\s+","ab\tc1\n3").group()
'\t'
>>> re.search("\s+","ab c1\n3").group()
' '

选择性匹配(|) 匹配选择竖线左边,或者右边的任意一种情况.

>>> re.search("abc|ABC","ABCBabcCD").group()
'ABC' >>> re.findall("abc|ABC","ABCBabcCD")
['ABC', 'abc']

实现分组匹配((?P...)) 匹配条件并自动分组,其中?P<..>是固定写法,后面紧跟正则规则.

>>> number = "371481199306143242"
>>> re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})",number).groupdict()
{'province': '3714', 'city': '81', 'birthday': '1993'} >>> re.search("(?P<name>[a-zA-Z]+)(?P<age>[0-9]+)","lyshark22").groupdict("temp")
{'name': 'lyshark', 'age': '22'}

针对IP地址与MAC地址的提取:

>>> re.search("^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$","192.168.1.1")
<re.Match object; span=(0, 11), match='192.168.1.1'> # 匹配IP地址 >>> re.match(r"^\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*$","192.168.1.100")
<re.Match object; span=(0, 13), match='192.168.1.100'> # 匹配IP地址 >>> string_ip = "is this 236.168.192.1 ip 12321"
>>> result = re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", string_ip)
>>> result
['236.168.192.1'] >>> string=re.compile(r'((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){3}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
>>> print(string.search('245.255.256.25asdsa10.11.244.10').group())
10.11.244.10 >>> string_IPv6="1050:0:0:0:5:600:300c:326b" # 匹配IPV6地址(大小写不敏感)
>>> re.match(r"^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$", string_IPv6, re.I)
<re.Match object; span=(0, 26), match='1050:0:0:0:5:600:300c:326b'> >>> re.findall(r"(?<![:.\w])(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}(?![:.\w])", string_IPv6, re.I)
['1050:0:0:0:5:600:300c:326b'] >>> re.match(r"^\s*([0-9a-fA-F]{2,2}:){5,5}[0-9a-fA-F]{2,2}\s*$","AB:1F:44:5B:3B:4A")
<re.Match object; span=(0, 17), match='AB:1F:44:5B:3B:4A'> # 匹配一个MAC地址

针对网址与端口的匹配:

>>> re.search(r"^(http|https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$","https://www.baidu.com")
<re.Match object; span=(0, 21), match='https://www.baidu.com'> # 匹配网址 >>> re.findall(r"([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])","hello 443")
['4', '4', '3'] # 匹配端口号 >>> re.search(r'^(http|https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?(:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?$',"http://www.baidu.com:80")
<re.Match object; span=(0, 23), match='http://www.baidu.com:80'> >>> re.search(r'^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])(:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?$',"192.168.1.100:443")
<re.Match object; span=(0, 17), match='192.168.1.100:443'>

针对时间格式的匹配:

>>> re.search(r"(\d{4}-\d{1,2}-\d{1,2})","2019-01-12")
<re.Match object; span=(0, 10), match='2019-01-12'> >>> re.findall(r"(\d{4}-\d{1,2}-\d{1,2})","2019-01-12,2010-12-11")
['2019-01-12', '2010-12-11'] >>> re.findall(r"\d{4}[-/]\d{2}[-/]\d{2}","2019-01-12,2010/12/11")
['2019-01-12', '2010/12/11'] >>> re.search(r"(\d{1,2}/(Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/\d{4})","2019-01-12,21/Nov/2019").group()
'21/Nov/2019' >>> re.findall(r"(\d{1,2}:\d{1,2})","2010-12-11 12:11")
['12:11'] >>> re.findall(r"(\d{1,2}:\d{1,2}:\d{1,2})","2010-12-11 12:11:22,09:25:30")
['12:11:22', '09:25:30'] >>> re.search(r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2})","2010-12-11 12:11")
<re.Match object; span=(0, 16), match='2010-12-11 12:11'> >>> re.findall(r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2})","2010-12-11 12:11")
['2010-12-11 12:11']

匹配邮箱/手机号/身份证:

>>> re.search("^1[3|4|5|8]\d{9}$","18264856987")
<re.Match object; span=(0, 11), match='18264856987'> # 匹配手机号 >>> re.search("[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+","182648@qq.com")
<re.Match object; span=(0, 13), match='182648@qq.com'> # 匹配一个邮箱 >>> re.findall(r'(^[1-8][0-7]{2}\d{3}([12]\d{3})(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}([0-9X])$)',"33070219630306041X")
[('33070219630306041X', '1963', '03', '06', 'X')] # 匹配身份证号

针对密码验证的匹配: 此处的匹配正则常用于用户名密码的过滤.

>>> re.findall("[\u4e00-\u9fa5]","你好")
['你', '好'] # 匹配中文字符 >>> re.findall("^[\u4e00-\u9fa5_a-zA-Z0-9]{4,10}$","1233")
['1233'] # 单纯限制字符的输入长度 >>> re.findall(r"^[a-zA-Z][a-zA-Z0-9_]{4,15}$","password")
['password'] # 允许输入最小5-15个字符的密码,允许使用下划线. >>> re.findall(r"^[a-zA-Z]\w{5,17}$","passw3")
['passw3'] # 以字母开头,长度在6~18之间,只能包含字母、数字和下划线 >>> re.findall("^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$","1233")
['1233'] # 限制不能以下划线开头和结尾

常用匹配函数

函数与方法名 通配符匹配作用解析
regex.match 从字符串开头位置匹配查找,如果0个或多个字符被匹配则返回相应的匹配对象,如果不匹配则返回None.
regex.search 扫描整个字符串,查找正则匹配到的字串中第一次出现的位置,并返回相应的匹配对象,如果匹配失败则返回None.
regex.findall 搜索字符串中与正则表达式匹配的所有子串,也就是查找字符串中所有的匹配结果,并且以列表的形式返回数据.
regex.sub 字符串的替换,简单来说就是替换字符串中与正则表达式匹配的指定数量的子串,最后返回替换修改后的字符串.
regex.split 以正则表达式匹配的字符串作为分隔符,对一个字符串进行分割,以列表形式返回分割后的各个字符串.
match.expand 通过得到的匹配对象来构造并返回一个新的字符串,未被匹配到的分组将被替换为一个空字符串.
match.group 返回一个或多个指定捕获组所匹配到的内容,如果只有1个参数则返回单独的字符串,多参数返回元组.
match.groups 返回一个包含所有分组所匹配内容的元组,如果某个分组没有匹配到内容,则取defalult所指定的值.
match.groupdict 返回一个包含所有命名分组名称及其所匹配内容的字典对象,如果某个分组没有匹配到内容则取默认值.

regex.match() 从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None.

match(pattern,string,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# falgs : 匹配模式
#------------------------------------------------
# 未分组情况下.
>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> ret = re.match("h\w+",origin)
>>> print(ret.group()) #获取匹配到的所有结果
>>> print(ret.groups()) #获取模型中匹配到的分组结果
>>> print(ret.groupdict()) #获取模型中匹配到的分组结果 # 有分组情况下. 提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
>>> ret = re.match("h(\w+).*(?P<name>\d)$",origin)
>>> print(r.group()) #获取匹配到的所有结果
>>> print(r.groups()) #获取模型中匹配到的分组结果
>>> print(r.groupdict()) #获取模型中匹配到的分组中所有执行了key的组

regex.search() 搜索整个字符串去匹配第一个符合条件的数据,未匹配成功返回None.

>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> re.search("^h\w+",origin).group() #匹配开头是h的后面是任意字符的
'hello'
>>> re.search("a\w+",origin).group() #匹配a开头后面是任意字符的
'alex'
>>> re.search("(?P<name>a\w+)",origin).groupdict()
{'name': 'alex'} #分组匹配并过滤出alex >>> re.search("(?P<姓名>[a-zA-Z]+)(?P<年龄>[0-9]+)","lyshark22").groupdict()
{'姓名': 'lyshark', '年龄': '22'} #匹配字符串,并分组打印出结果

regex.findall() 获取非重复的匹配列表,且每一个匹配均是字符串,空的匹配也会包含在结果中.

>>> origin = "hello alex bcd abcd lge acd 19"

>>> re.findall("al\w+",origin)
['alex'] #匹配到单个结果,则以单列表返回
>>> re.findall("a\w+",origin)
['alex', 'abcd', 'acd'] #匹配到多个结果,则以列表形式返回

regex.sub() 先匹配查找结果,然后进行字串的替换,也就是替换匹配成功的指定位置字符串.

sub(pattern,repl,string,count=0,flags=0)
# pattern: 正则模型
# repl : 要替换的字符串或可执行对象
# string : 要匹配的字符串
# count : 指定匹配个数
# flags : 匹配模式 >>> origin = "hello alex bcd abcd lge acd 19" >>> re.sub("a[a-z]+","999999",origin,1) #匹配以a开头则字串,并替换成9999,替换1次
'hello 999999 bcd abcd lge acd 19'
>>> re.sub("a[a-z]+","999999",origin,2) #匹配以a开头则字串,并替换成9999,替换2次
'hello 999999 bcd 999999 lge acd 19'

regex.split() 字符串切割函数,用来实现对指定字符串的分割工作,根据正则匹配分割字符串.

split(pattern,string,maxsplit=0,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# maxsplit:指定分割个数
# flags : 匹配模式 >>> origin = "hello alex bcd abcd lge acd 19" >>> re.split("alex",origin,1) #无分组切割
['hello ', ' bcd abcd lge acd 19']
>>> re.split("(alex)",origin,1) #有分组,以alex最为分隔符,切割字符串
['hello ', 'alex', ' bcd abcd lge acd 19']

单独匹配

>>> ptr = re.compile(r"[A-Z]")
>>> ptr.search("Hello lyshark")
<re.Match object; span=(0, 1), match='H'>
>>> ptr.findall("Hello lyshark")
['H']

re.DOTALL

# 正则表达式默认以单行开始匹配的
import re def re_pattern_syntax():
# .表示任意单一字符
# *表示前一个字符出现>=0次
# re.DOTALL就可以匹配换行符\n,默认是以行来匹配的
print(re.match(r'.*', 'abc\nedf').group())
print('*' * 80)
print(re.match(r'.*', 'abc\nedf',re.DOTALL).group()) if __name__ == '__main__':
re_pattern_syntax()

re.MULTILINE

# 正则表达式默认以单行开始匹配的
import re def re_pattern_syntax1():
# ^表示字符串开头(单行)
# re.MULTILINE多行匹配字符串开头
print(re.findall(r'^abc', 'abc\nedf'))
print('*' * 80)
print(re.findall(r'^abc', 'abc\nabc',re.MULTILINE)) def re_pattern_syntax2():
# $表示字符串结尾
# re.MULTILINE表示行的结束
print(re.findall(r'abc\d$', 'abc1\nabc2'))
print('*' * 80)
print(re.findall(r'abc\d$', 'abc1\nabc2',re.MULTILINE)) if __name__ == '__main__':
re_pattern_syntax1()
re_pattern_syntax2()

?非贪婪模式

import re

def re_pattern_syntax4():
# greedy贪婪/non-greedy非贪婪,默认的是贪婪的匹配
s = '<H1>title</H1>'
print(re.match(r'<.+>', s).group()) #贪婪模式会匹配尽量多的匹配
print(re.match(r'<.+?>', s).group()) #非贪婪模式匹配尽量少的匹配
print(re.match(r'<(.+)>', s).group(1))
print(re.match(r'<(.+?)>', s).group(1)) def re_pattern_syntax5():
# {m}/{m,}/{m,n}
print(re.match(r'ab{2,4}', 'abbbbbbb').group()) #贪婪模式尽量匹配多
print(re.match(r'ab{2,4}?', 'abbbbbbb').group()) #非贪婪模式尽量匹配少
print('*' * 80) if __name__ == '__main__':
re_pattern_syntax4()
re_pattern_syntax5()

re.I/re.IGNORECASE

import re

def re_pattern_flags():
# re.I/re.IGNORECASE
print(re.match(r'(Name)\s*:\s*(\w+)','NAME : Joey',re.IGNORECASE).groups())
print('*' * 80) if __name__ == '__main__':
re_pattern_syntax_meta_char()

re.VERBOSE

import re

def re_pattern_flags1():
# re.VERBOSE此标识位可以添加注释/re.compile
s = 'the number is 20.5'
r = re.compile(r'''
\d+ # 整数部分
\.? # 小数点,可能包含也可能不包含
\d* # 小数部分,可选
''',re.VERBOSE)
print(re.search(r,s).group())
print(r.search(s).group())
print('*' * 80) if __name__ == '__main__':
re_pattern_syntax_meta_char1()

Python RE 正则表达式模块的更多相关文章

  1. Python re(正则表达式)模块

    python正则表达式 正则表达式是一个特殊的字符序列,它能帮助我们方便的检查一个字符串是否与某种模式匹配.Python自1.5版本起增加了re模块,它提供Perl风格的正则表达式模式.re模块使Py ...

  2. 24、python re正则表达式模块

    一.re模块的基本使用 Python里数量词默认是贪婪的,总是尝试匹配尽可能多的字符.正则表达式是用来匹配处理字符串的. 假如你需要匹配文本中的字符\,Python里的使用原生字符串表示:r'\\'表 ...

  3. python re(正则表达式模块)学习

    一.简介 正则表达式本身是一种小型的.高度专业化的编程语言,而在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配.正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎 ...

  4. python re正则表达式模块

    模块的的作用主要是用于字符串和文本处理,查找,搜索,替换等 复习一下基本的正则表达式吧  .:匹配除了换行符以为的任意单个字符  *:匹配任意字符,一个,零个,多个都能匹配得到 俗称贪婪模式 +:匹配 ...

  5. Python之正则表达式模块

    正则表达式符号: . ^ $ * + ? {} () | [] .一个点代表一个字符 ^代表开头 $代表结尾 *代表有0到无数个 [0,+00] ?代表有0到1个      [0,1] +代表有1到无 ...

  6. 【Python】正则表达式纯代码极简教程

    <Python3正则表达式>文字版详细教程链接:https://www.cnblogs.com/leejack/p/9189796.html ''' 内容:Python3正则表达式 日期: ...

  7. 【Python】正则表达式简单教程

    说明:本文主要是根据廖雪峰网站的正则表达式教程学习,并根据需要做了少许修改,此处记录下来以备后续查看. <Python正则表达式纯代码极简教程>链接:https://www.cnblogs ...

  8. python 历险记(六)— python 对正则表达式的使用(上篇)

    目录 引言 什么是正则表达式? 正则表达式有什么用? 正则表达式的语法及使用实例 正则表达式语法有哪些? 这些正则到底该怎么用? 小结 参考文档 系列文章列表 引言 刚接触正则表达式,我也曾被它们天书 ...

  9. python的re正则表达式模块学习

    python中re模块的用法   Python 的 re 模块(Regular Expression 正则表达式)提供各种正则表达式的匹配操作,在文本解析.复杂字符串分析和信息提取时是一个非常有用的工 ...

  10. Python的regex模块——更强大的正则表达式引擎

    Python自带了正则表达式引擎(内置的re模块),但是不支持一些高级特性,比如下面这几个: 固化分组    Atomic grouping 占有优先量词    Possessive quantifi ...

随机推荐

  1. c#-微软2

    练习-编写第一个代码: 在第一次练习中你将使用c#将神圣的程序员用语打印到控制台的标准输出 编写第一行代码: 在软件开发者中,有这么一个传统,那就是将"Hello World!"这 ...

  2. 别再问我 2050 可以干什么,Make a Movie in a Day!

    2050 的每个年青人都是新物种.越是不可能见面的人见了面,就越会有奇迹发生,2050 努力让年青人见上另一位年青人,激发新的创造力.一起来 2050 看看? 2050 是什么? 2050 大会是由阿 ...

  3. OS | 透过 inode 来理解硬链接和软链接

    什么是inode? 每个文件都对应一个唯一的inode,inode用来存储文件的元信息,包括: 对应的文件 文件字节数 文件数据块的位置 文件的inode号码 文件的硬链接数 文件的读写权限 文件的时 ...

  4. POJ:3660 Cow Contest (传递闭包 + Floyd)

    POJ 3660 http://poj.org/problem?id=3660 思路: 传递闭包 输入A > B,那么我们可以建立一套A ->B 的边. 然后求出传递闭包. 判断一个人是否 ...

  5. POJ 2726、POJ3074 :数独(二进制DFS)

    题目链接:https://ac.nowcoder.com/acm/contest/1014/B 题目描述 In the game of Sudoku, you are given a large 9 ...

  6. ACM/OI中C++常用优化(实用/调试/技巧)代码(语法)

    一.C++万能编译头文件 #include<bits/stdc++.h> VS2017添加万能头文件 从 #include <iostream> #include <cs ...

  7. 源码深度解析 Handler 机制及应用

    本文以源码分析+实际应用的形式,详细讲解了 Handler 机制的原理,以及在开发中的使用场景和要注意的地方. 一.基本原理回顾 在 Android 开发中,Handler及相关衍生类的应用经常用到, ...

  8. 数字孪生 3D 风电场,智慧风电之海上风电

    前言 截止 2021 年,全球已有 127 个国家做出了"碳中和"的承诺,能源低碳转型和实现碳中和已经成为全球共同的战略目标.根据权威机构预测,到 2050 年,可再生能源发电将占 ...

  9. 五、mongo备份篇 mongoexport、mongoimport 以及mongodump、mongorestore

    系列导航 一.linux单机版mongo安装(带密码验证) 二.mongo集群搭建 三.java连接mongo数据库 四.java对mongo数据库增删改查操作 五.mongo备份篇 mongoexp ...

  10. Power Designer建模之餐饮在线点评系统——概念数据模型

    企业信息管理 局部概念模型 企业 餐饮企业 食材提供商 食材 特色菜 团购活动 优惠券 促销活动 会员团购订单 优惠券下载和浏览记录表 会员信息管理 局部概念模型 会员 会员扩展信息 会员积分记录 餐 ...