正则表达式
原文出处:http://www.amk.ca/python/howto/regex/ 原文作者:A.M. Kuchling (amk@amk.ca) 授权许可:创作共用协议 翻译人员:FireHare 校对人员:Leal 适用版本:Python 1.5 及后续版本 摘要
本文可以从http://www.amk.ca/python/howto捕获 目录[隐藏]
简介Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。Python 1.5之前版本则是通过 regex 模块提供 Emacs 风格的模式。Emacs 风格模式可读性稍差些,而且功能也不强,因此编写新代码时尽量不要再使用 regex 模块,当然偶尔你还是可能在老代码里发现其踪影。
[编辑]简单模式我们将从最简单的正则表达式学习开始。由于正则表达式常用于字符串操作,那我们就从最常见的任务:字符匹配 下手。
字符匹配大多数字母和字符一般都会和自身匹配。例如,正则表达式 test 会和字符串“test”完全匹配。(你也可以使用大小写不敏感模式,它还能让这个 RE 匹配“Test”或“TEST”;稍后会有更多解释。) 这个规则当然会有例外;有些字符比较特殊,它们和自身并不匹配,而是会表明应和一些特殊的东西匹配,或者它们会影响到 RE 其它部分的重复次数。本文很大篇幅专门讨论了各种元字符及其作用。 这里有一个元字符的完整列表;其含义会在本指南余下部分进行讨论。 . ^ $ * +? { [ ] | ( ) 我们首先考察的元字符是"[" 和 "]"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集。字符可以单个列出,也可以用“-”号分隔的两个给定字符来表示一个字符区间。例如,[abc] 将匹配"a","b",或 "c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致。如果你只想匹配小写字母,那么 RE 应写成 [a-z]. 元字符在类别里并不起作用。例如,[akm$]将匹配字符"a","k","m",或 "$" 中的任意一个;"$"通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。 你可以用补集来匹配不在区间范围内的字符。其做法是把"^"作为类别的首个字符;其它地方的"^"只会简单匹配 "^"字符本身。例如,[^5] 将匹配除 "5" 之外的任意字符。 也许最重要的元字符是反斜杠""。 做为 Python 中的字符串字母,反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符,这样你就可以在模式中匹配它们了。举个例子,如果你需要匹配字符 "[" 或 "",你可以在它们之前用反斜杠来取消它们的特殊意义: [ 或 。 一些用 "" 开始的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。下列是可用的预设特殊字符: d 匹配任何十进制数;它相当于类 [0-9]。 D 匹配任何非数字字符;它相当于类 [^0-9]。 s 匹配任何空白字符;它相当于类 [ tnrfv]。 S 匹配任何非空白字符;它相当于类 [^ tnrfv]。 w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。 W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。
这样特殊字符都可以包含在一个字符类中。如,[s,.]字符类将匹配任何空白字符或","或"."。 本节最后一个元字符是 . 。它匹配除了换行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配换行。"." 通常被用于你想匹配“任何字符”的地方。 [编辑]重复正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方法所不能做到的。 不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么优秀了。它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。 我们讨论的第一个重复功能的元字符是 *。* 并不匹配字母字符 "*";相反,它指定前一个字符可以被匹配零次或更多次,而不是只有一次。 举个例子,ca*t 将匹配 "ct" (0 个 "a" 字符),"cat" (1 个 "a"),"caaat" (3 个 "a" 字符)等等。RE 引擎有各种来自 C 的整数类型大小的内部限制,以防止它匹配超过20亿个 "a" 字符;你也许没有足够的内存去建造那么大的字符串,所以将不会累计到那个限制。 象 * 这样地重复是“贪婪的”;当重复一个 RE 时,匹配引擎会试着重复尽可能多的次数。如果模式的后面部分没有被匹配,匹配引擎将退回并再次尝试更小的重复。
RE 的结尾部分现在可以到达了,它匹配 "abcb"。这证明了匹配引擎一开始会尽其所能进行匹配,如果没有匹配然后就逐步退回并反复尝试 RE 剩下来的部分。直到它退回尝试匹配 [bcd] 到零次为止,如果随后还是失败,那么引擎就会认为该字符串根本无法匹配 RE 。
现在我们已经看了一些简单的正则表达式,那么我们实际在 Python 中是如何使用它们的呢? re 模块提供了一个正则表达式引擎的接口,可以让你将 REs 编译成对象并用它们来进行匹配。 [编辑]编译正则表达式正则表达式被编译成 `RegexObject` 实例,可以为不同的操作提供方法,如模式匹配搜索或字符串替换。 #!python >>> import re >>> p = re.compile('ab*') >>> print p <_sre.SRE_Pattern object at 0xb76e1a70> re.compile() 也接受可选的标志参数,常用来实现不同的特殊功能和语法变更。我们稍后将查看所有可用的设置,但现在只举一个例子: #!python >>> p = re.compile('ab*',re.IGNORECASE) RE 被做为一个字符串发送给 re.compile()。REs 被处理成字符串是因为正则表达式不是 Python 语言的核心部分,也没有为它创建特定的语法。(应用程序根本就不需要 REs,因此没必要包含它们去使语言说明变得臃肿不堪。)而 re 模块则只是以一个 C 扩展模块的形式来被 Python 包含,就象 socket 或 zlib 模块一样
在早期规定中,正则表达式用反斜杠字符 ("") 来表示特殊格式或允许使用特殊字符而不调用它的特殊用法。这就与 Python 在字符串中的那些起相同作用的相同字符产生了冲突。
解决的办法就是为正则表达式使用 Python 的 raw 字符串表示;在字符串前加个 "r" 反斜杠就不会被任何特殊方式处理,所以 r"n" 就是包含"" 和 "n" 的两个字符,而 "n" 则是一个字符,表示一个换行。正则表达式通常在 Python 代码中都是用这种 raw 字符串表示。
一旦你有了已经编译了的正则表达式的对象,你要用它做什么呢?`RegexObject` 实例有一些方法和属性。这里只显示了最重要的几个,如果要看完整的列表请查阅 Python Library Reference
你可以用采用人机对话并用 re 模块实验的方式来学习它。如果你有 Tkinter 的话,你也许可以考虑参考一下 Tools/scripts/redemo.py,一个包含在 Python 发行版里的示范程序。 首先,运行 Python 解释器,导入 re 模块并编译一个 RE: #!python Python 2.2.2 (#1,Feb 10 2003,12:57:01) >>> import re >>> p = re.compile('[a-z]+') >>> p <_sre.SRE_Pattern object at 80c3c28> 现在,你可以试着用 RE 的 [a-z]+ 去匹配不同的字符串。一个空字符串将根本不能匹配,因为 + 的意思是 “一个或更多的重复次数”。 在这种情况下 match() 将返回 None,因为它使解释器没有输出。你可以明确地打印出 match() 的结果来弄清这一点。 #!python >>> p.match("") >>> print p.match("") None 现在,让我们试着用它来匹配一个字符串,如 "tempo"。这时,match() 将返回一个 MatchObject。因此你可以将结果保存在变量里以便後面使用。 #!python >>> m = p.match( 'tempo') >>> print m <_sre.SRE_Match object at 80c4f68> 现在你可以查询 `MatchObject` 关于匹配字符串的相关信息了。MatchObject 实例也有几个方法和属性;最重要的那些如下所示:
#!python >>> m.group() 'tempo' >>> m.start(),m.end() (0,5) >>> m.span() (0,5) group() 返回 RE 匹配的子串。start() 和 end() 返回匹配开始和结束时的索引。span() 则用单个元组把开始和结束时的索引一起返回。因为匹配方法检查到如果 RE 在字符串开始处开始匹配,那么 start() 将总是为零。然而, `RegexObject` 实例的 search 方法扫描下面的字符串的话,在这种情况下,匹配开始的位置就也许不是零了。 #!python >>> print p.match('::: message') None >>> m = p.search('::: message'); print m <re.MatchObject instance at 80c9650> >>> m.group() 'message' >>> m.span() (4,11) 在实际程序中,最常见的作法是将 `MatchObject` 保存在一个变量里,然後检查它是否为 None,通常如下所示: #!python p = re.compile( ... ) m = p.match( 'string goes here' ) if m: print 'Match found: ',m.group() else: print 'No match' 两个 `RegexObject` 方法返回所有匹配模式的子串。findall()返回一个匹配字符串行表: #!python >>> p = re.compile('d+') >>> p.findall('12 drummers drumming,11 pipers piping,10 lords a-leaping') ['12','11','10'] findall() 在它返回结果时不得不创建一个列表。在 Python 2.2中,也可以用 finditer() 方法。 #!python >>> iterator = p.finditer('12 drummers drumming,11 ... 10 ...') >>> iterator <callable-iterator object at 0x401833ac> >>> for match in iterator: ... print match.span() ... (0,2) (22,24) (29,31)[编辑]模块级函数 你不一定要产生一个 `RegexObject` 对象然后再调用它的方法;re 模块也提供了顶级函数调用如 match()、search()、sub() 等等。这些函数使用 RE 字符串作为第一个参数,而后面的参数则与相应 `RegexObject` 的方法参数相同,返回则要么是 None 要么就是一个 `MatchObject` 的实例。 #!python >>> print re.match(r'Froms+','Fromage amk') None >>> re.match(r'Froms+','From amk Thu May 14 19:12:10 1998') <re.MatchObject instance at 80c5978> Under the hood,这些函数简单地产生一个 RegexOject 并在其上调用相应的方法。它们也在缓存里保存编译后的对象,因此在将来调用用到相同 RE 时就会更快。
#!python ref = re.compile( ... ) entityref = re.compile( ... ) charref = re.compile( ... ) starttagopen = re.compile( ... ) 我通常更喜欢使用编译对象,甚至它只用一次,但很少人会像我这样做(如同一个纯粹主义者)。 [编辑]编译标志编译标志让你可以修改正则表达式的一些运行方式。在 re 模块中标志可以使用两个名字,一个是全名如 IGNORECASE,一个是缩写,一字母形式如 I。(如果你熟悉 Perl 的模式修改,一字母形式使用同样的字母;例如 re.VERBOSE的缩写形式是 re.X。)多个标志可以通过按位 OR-ing 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
I 使匹配对大小写不敏感;字符类和字符串匹配字母时忽略大小写。举个例子,[A-Z]也可以匹配小写字母,Spam 可以匹配 "Spam","spam",或 "spAM"。这个小写字母并不考虑当前位置。 L 影响 w,W,b,和 B,这取决于当前的本地化设置。 locales 是 C 语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。举个例子,如果你正在处理法文文本,你想用 w+ 来匹配文字,但 w 只匹配字符类 [A-Za-z];它并不能匹配 "é" 或 "?"。如果你的系统配置适当且本地化设置为法语,那么内部的 C 函数将告诉程序 "é" 也应该被认为是一个字母。当在编译正则表达式时使用 LOCALE 标志会得到用这些 C 函数来处理 w 后的编译对象;这会更慢,但也会象你希望的那样可以用 w+ 来匹配法文文本。 M
S 使 "." 特殊字符完全匹配任何字符,包括换行;没有这个标志, "." 匹配除了换行外的任何字符。 X
#!python charref = re.compile(r"""&[[]] # Start of a numeric entity reference|||here has wrong.i can't fix ( [0-9]+[^0-9] # Decimal form | 0[0-7]+[^0-7] # Octal form | x[0-9a-fA-F]+[^0-9a-fA-F] # Hexadecimal form ) """,re.VERBOSE) 没有 verbose 设置, RE 会看起来象这样: #!python charref = re.compile("&#([0-9]+[^0-9]" "|0[0-7]+[^0-7]" "|x[0-9a-fA-F]+[^0-9a-fA-F])") 在上面的例子里,Python 的字符串自动连接可以用来将 RE 分成更小的部分,但它比用 re.VERBOSE 标志时更难懂 [编辑]更多模式功能到目前为止,我们只展示了正则表达式的一部分功能。在本节,我们将展示一些新的元字符和如何使用组来检索被匹配的文本部分。 == == [编辑]更多的元字符粗体文字链接标题还有一些我们还没展示的元字符,其中的大部分将在本节展示。
|
^
#!python >>> print re.search('^From','From Here to Eternity') <re.MatchObject instance at 80c1520> >>> print re.search('^From','Reciting From Memory') None $
#!python >>> print re.search('}$','{block}') <re.MatchObject instance at 80adfa8> >>> print re.search('}$','{block} ') None >>> print re.search('}$','{block}n') <re.MatchObject instance at 80adfa8> 匹配一个 "$",使用 $ 或将其包含在字符类中,如[$]。 A
Z Matches only at the end of the string. b 单词边界。这是个零宽界定符(zero-width assertions)只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。
#!python >>> p = re.compile(r'bclassb') >>> print p.search('no class at all') <re.MatchObject instance at 80c8f28> >>> print p.search('the declassified algorithm') None >>> print p.search('one subclass is') None 当用这个特殊序列时你应该记住这里有两个微妙之处。第一个是 Python 字符串和正则表达式之间最糟的冲突。在 Python 字符串里,"b" 是反斜杠字符,ASCII值是8。如果你没有使用 raw 字符串时,那么 Python 将会把 "b" 转换成一个回退符,你的 RE 将无法象你希望的那样匹配它了。下面的例子看起来和我们前面的 RE 一样,但在 RE 字符串前少了一个 "r" 。 #!python >>> p = re.compile('bclassb') >>> print p.search('no class at all') None >>> print p.search('b' + 'class' + 'b') <re.MatchObject instance at 80c3ee0> 第二个在字符类中,这个限定符(assertion)不起作用,b 表示回退符,以便与 Python 字符串兼容。 B
你经常需要得到比 RE 是否匹配还要多的信息。正则表达式常常用来分析字符串,编写一个 RE 匹配感兴趣的部分并将其分成几个小组。举个例子,一个 RFC-822 的头部用 ":" 隔成一个头部名和一个值,这就可以通过编写一个正则表达式匹配整个头部,用一组匹配头部名,另一组匹配头部值的方式来处理。
#!python >>> p = re.compile('(ab)*') >>> print p.match('ababababab').span() (0,10) 组用 "(" 和 ")" 来指定,并且得到它们匹配文本的开始和结尾索引;这就可以通过一个参数用 group()、start()、end() 和 span() 来进行检索。组是从 0 开始计数的。组 0 总是存在;它就是整个 RE,所以 `MatchObject` 的方法都把组 0 作为它们缺省的参数。稍后我们将看到怎样表达不能得到它们所匹配文本的 span。 #!python >>> p = re.compile('(a)b') >>> m = p.match('ab') >>> m.group() 'ab' >>> m.group(0) 'ab' 小组是从左向右计数的,从1开始。组可以被嵌套。计数的数值可以通过从左到右计算打开的括号数来确定。 #!python >>> p = re.compile('(a(b)c)d') >>> m = p.match('abcd') >>> m.group(0) 'abcd' >>> m.group(1) 'abc' >>> m.group(2) 'b' group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 #!python >>> m.group(2,1,2) ('b','abc','b') The groups() 方法返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 #!python >>> m.groups() ('abc',sans-serif; line-height:15px">模式中的逆向引用允许你指定先前捕获组的内容,该组也必须在字符串当前位置被找到。举个例子,如果组 1 的内容能够在当前位置找到的话,1 就成功否则失败。记住 Python 字符串也是用反斜杠加数据来允许字符串中包含任意字符的,所以当在 RE 中使用逆向引用时确保使用 raw 字符串。
`RegexObject` 的 split() 方法在 RE 匹配的地方将字符串分片,将返回列表。它同字符串的 split() 方法相似但提供更多的定界符;split()只支持空白符和固定字符串。就象你预料的那样,也有一个模块级的 re.split() 函数。 split(string [,maxsplit = 0]) 通过正则表达式将字符串分片。如果捕获括号在 RE 中使用,那么它们的内容也会作为结果列表的一部分返回。如果 maxsplit 非零,那么最多只能分出 maxsplit 个分片。
#!python >>> p = re.compile(r'W+') >>> p.split('This is a test,short and sweet,of split().') ['This','is','a','test','short','and','sweet','of','split',''] >>> p.split('This is a test,of split().',3) ['This','test,of split().'] 有时,你不仅对定界符之间的文本感兴趣,也需要知道定界符是什么。如果捕获括号在 RE 中使用,那么它们的值也会当作列表的一部分返回。比较下面的调用: #!python >>> p = re.compile(r'W+') >>> p2 = re.compile(r'(W+)') >>> p.split('This... is a test.') ['This',''] >>> p2.split('This... is a test.') ['This','... ',' ','.',''] 模块级函数 re.split() 将 RE 作为第一个参数,其他一样。 #!python >>> re.split('[W]+','Words,words,words.') ['Words','words',''] >>> re.split('([W]+)',',''] >>> re.split('[W]+',words.',1) ['Words','words,words.'][编辑]搜索和替换 其他常见的用途就是找到所有模式匹配的字符串并用不同的字符串来替换它们。sub() 方法提供一个替换值,可以是字符串或一个函数,和一个要被处理的字符串。 sub(replacement,string[,count = 0]) 返回的字符串是在字符串中用 RE 最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回。
#!python >>> p = re.compile( '(blue|white|red)') >>> p.sub( 'colour','blue socks and red shoes') 'colour socks and colour shoes' >>> p.sub( 'colour','blue socks and red shoes',count=1) 'colour socks and red shoes' subn() 方法作用一样,但返回的是包含新字符串和替换执行次数的两元组。 #!python >>> p = re.compile( '(blue|white|red)') >>> p.subn( 'colour','blue socks and red shoes') ('colour socks and colour shoes',2) >>> p.subn( 'colour','no colours at all') ('no colours at all',0) 空匹配只有在它们没有紧挨着前一个匹配时才会被替换掉。 #!python >>> p = re.compile('x*') >>> p.sub('-','abxd') '-a-b-d-' 如果替换的是一个字符串,任何在其中的反斜杠都会被处理。"n" 将会被转换成一个换行符,"r"转换成回车等等。未知的转义如 "j" 则保持原样。逆向引用,如 "6",被 RE 中相应的组匹配而被子串替换。这使你可以在替换后的字符串中插入原始文本的一部分。
#!python >>> p = re.compile('section{ ( [^}]* ) }',re.VERBOSE) >>> p.sub(r'subsection{1}','section{First} section{second}') 'subsection{First} subsection{second}' 还可以指定用 (?P<name>...) 语法定义的命名组。"g<name>" 将通过组名 "name" 用子串来匹配,并且 "g<number>" 使用相应的组号。所以 "g<2>" 等于 "2",但能在替换字符串里含义不清,如 "g<2>0"。("20" 被解释成对组 20 的引用,而不是对后面跟着一个字母 "0" 的组 2 的引用。) #!python >>> p = re.compile('section{ (?P<name> [^}]* ) }','section{First}') 'subsection{First}' >>> p.sub(r'subsection{g<1>}','section{First}') 'subsection{First}' >>> p.sub(r'subsection{g<name>}','section{First}') 'subsection{First}' 替换也可以是一个甚至给你更多控制的函数。如果替换是个函数,该函数将会被模式中每一个不重复的匹配所调用。在每次调用时,函数会被传入一个 `MatchObject` 的对象作为参数,因此可以用这个对象去计算出替换字符串并返回它。
#!python >>> def hexrepl( match ): ... "Return the hex string for a decimal number" ... value = int( match.group() ) ... return hex(value) ... >>> p = re.compile(r'd+') >>> p.sub(hexrepl,'Call 65490 for printing,49152 for user code.') 'Call 0xffd2 for printing,0xc000 for user code.' 当使用模块级的 re.sub() 函数时,模式作为第一个参数。模式也许是一个字符串或一个 `RegexObject`;如果你需要指定正则表达式标志,你必须要么使用 `RegexObject` 做第一个参数,或用使用模式内嵌修正器,如 sub("(?i)b+","x","bbbb BBBB") returns 'x x'。 [编辑]常见问题正则表达式对一些应用程序来说是一个强大的工具,但在有些时候它并不直观而且有时它们不按你期望的运行。本节将指出一些最容易犯的常见错误。 [编辑]使用字符串方式有时使用 re 模块是个错误。如果你匹配一个固定的字符串或单个的字符类,并且你没有使用 re 的任何象 IGNORECASE 标志的功能,那么就没有必要使用正则表达式了。字符串有一些方法是对固定字符串进行操作的,它们通常快很多,因为它们都是一个个经过优化的 C 小循环,用以代替大的、更具通用性的正则表达式引擎。
总之,在使用 re 模块之前,先考虑一下你的问题是否可以用更快、更简单的字符串方法来解决。 [编辑]match() vs search()match() 函数只检查 RE 是否在字符串开始处匹配,而 search() 则是扫描整个字符串。记住这一区别是重要的。记住,match() 只报告一次成功的匹配,它将从 0 处开始;如果匹配不是从 0 开始的,match() 将不会报告它。 #!python >>> print re.match('super','superstition').span() (0,5) >>> print re.match('super','insuperable') None 另一方面,search() 将扫描整个字符串,并报告它找到的第一个匹配。 #!python >>> print re.search('super',5) >>> print re.search('super','insuperable').span() (2,7) 有时你可能倾向于使用 re.match(),只在RE的前面部分添加 .* 。请尽量不要这么做,最好采用 re.search() 代替之。正则表达式编译器会对 REs 做一些分析以便可以在查找匹配时提高处理速度。一个那样的分析机会指出匹配的第一个字符是什么;举个例子,模式 Crow 必须从 "C" 开始匹配。分析机可以让引擎快速扫描字符串以找到开始字符,并只在 "C" 被发现后才开始全部匹配。 添加 .* 会使这个优化失败,这就要扫描到字符串尾部,然后回溯以找到 RE 剩余部分的匹配。使用 re.search() 代替。 [编辑]贪婪 vs 不贪婪当重复一个正则表达式时,如用 a*,操作结果是尽可能多地匹配模式。当你试着匹配一对对称的定界符,如 HTML 标志中的尖括号时这个事实经常困扰你。匹配单个 HTML 标志的模式不能正常工作,因为 .* 的本质是“贪婪”的 #!python >>> s = '<html><head><title>Title</title>' >>> len(s) 32 >>> print re.match('<.*>',s).span() (0,32) >>> print re.match('<.*>',s).group() <html><head><title>Title</title> RE 匹配 在 "
#!python >>> print re.match('<.*?>',s).group() <html> 注意用正则表达式分析 HTML 或 XML 是痛苦的。变化混乱的模式将处理常见情况,但 HTML 和 XML 则是明显会打破正则表达式的特殊情况;当你编写一个正则表达式去处理所有可能的情况时,模式将变得非常复杂。象这样的任务用 HTML 或 XML 解析器。 粗体文字粗体文字' [编辑]不用 re.VERBOSE 现在你可能注意到正则表达式的表示是十分紧凑,但它们非常不好读。中度复杂的 REs 可以变成反斜杠、圆括号和元字符的长长集合,以致于使它们很难读懂。
#!python pat = re.compile(r""" s* # Skip leading whitespace (?P<header>[^:]+) # Header name s*: # Whitespace,and a colon (?P<value>.*?) # The header's value -- *? used to # lose the following trailing whitespace s*$ # Trailing whitespace to end-of-line """,sans-serif; line-height:15px">这个要难读得多:#!python pat = re.compile(r"s*(?P<header>[^:]+)s*:(?P<value>.*?)s*$")[编辑]反馈 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |