【问题标题】:How do you match only valid roman numerals with a regular expression?如何仅将有效的罗马数字与正则表达式匹配?
【发布时间】:2010-09-21 00:15:43
【问题描述】:

想到my other problem,我决定我什至不能创建一个匹配罗马数字的正则表达式(更不用说生成它们的上下文无关语法了)

问题是只匹配有效的罗马数字。 例如,990 不是“XM”,而是“CMXC”

我在为此制作正则表达式时遇到的问题是,为了允许或不允许某些字符,我需要回顾一下。 让我们以成千上万为例。

我可以允许 M{0,2}C?M(允许 900、1000、1900、2000、2900 和 3000)。但是,如果匹配在 CM 上,我不能允许后面的字符是 C 或 D(因为我已经在 900 了)。

如何在正则表达式中表达这一点?
如果它在正则表达式中根本无法表达,那么它是否可以在上下文无关语法中表达?

【问题讨论】:

    标签: regex roman-numerals


    【解决方案1】:

    您可以为此使用以下正则表达式:

    ^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$
    

    分解它,M{0,4} 指定了千位部分,基本上将其限制在04000 之间。比较简单:

       0: <empty>  matched by M{0}
    1000: M        matched by M{1}
    2000: MM       matched by M{2}
    3000: MMM      matched by M{3}
    4000: MMMM     matched by M{4}
    

    如果您想允许更大的数字,当然可以使用M* 之类的东西来允许任何个数(包括零)。

    接下来是(CM|CD|D?C{0,3}),稍微复杂一些,这是针对数百个部分的,涵盖了所有可能性:

      0: <empty>  matched by D?C{0} (with D not there)
    100: C        matched by D?C{1} (with D not there)
    200: CC       matched by D?C{2} (with D not there)
    300: CCC      matched by D?C{3} (with D not there)
    400: CD       matched by CD
    500: D        matched by D?C{0} (with D there)
    600: DC       matched by D?C{1} (with D there)
    700: DCC      matched by D?C{2} (with D there)
    800: DCCC     matched by D?C{3} (with D there)
    900: CM       matched by CM
    

    第三,(XC|XL|L?X{0,3}) 遵循与上一节相同的规则,但对于十位:

     0: <empty>  matched by L?X{0} (with L not there)
    10: X        matched by L?X{1} (with L not there)
    20: XX       matched by L?X{2} (with L not there)
    30: XXX      matched by L?X{3} (with L not there)
    40: XL       matched by XL
    50: L        matched by L?X{0} (with L there)
    60: LX       matched by L?X{1} (with L there)
    70: LXX      matched by L?X{2} (with L there)
    80: LXXX     matched by L?X{3} (with L there)
    90: XC       matched by XC
    

    最后,(IX|IV|V?I{0,3}) 是单位部分,处理 09 并且也类似于前两个部分(罗马数字,尽管它们看起来很奇怪,但一旦你弄清楚它们是什么,请遵循一些逻辑规则是):

    0: <empty>  matched by V?I{0} (with V not there)
    1: I        matched by V?I{1} (with V not there)
    2: II       matched by V?I{2} (with V not there)
    3: III      matched by V?I{3} (with V not there)
    4: IV       matched by IV
    5: V        matched by V?I{0} (with V there)
    6: VI       matched by V?I{1} (with V there)
    7: VII      matched by V?I{2} (with V there)
    8: VIII     matched by V?I{3} (with V there)
    9: IX       matched by IX
    

    请记住,该正则表达式也将匹配一个空字符串。如果您不想要这个(并且您的正则表达式引擎足够现代),您可以使用积极的后视和前瞻:

    (?<=^)M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})(?=$)
    

    (另一种选择是预先检查长度是否不为零)。

    【讨论】:

    • 可能。我只能在问题中看到它需要达到 3999(不是特定的明确要求)的推论,但无论如何我都允许它达到 4999。如果您确实想将其限制为 3999,那么请务必删除其中一位女士。
    • 有什么办法可以避免匹配空字符串?
    • @Aashish:当罗马人是一支不可忽视的力量时,MMMM 是正确的方式。在核心帝国分崩离析之后很久,overbar 就出现了。
    • @paxdiablo 这就是我发现 mmmcm 失败的原因。字符串 regx = "^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0, 3})$"; if(input.matches(regx)) -> 这对于 java 中的 MMMCM / MMMM 评估为 false。
    • /^M{0,3}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})$/i
    【解决方案2】:

    实际上,您的前提是有缺陷的。 990 IS“XM”,以及“CMXC”。

    罗马人远没有你三年级的老师那么关心“规则”。只要加起来就OK了。因此,对于 4,“IIII”与“IV”一样好。而“IIM”对于 998 来说非常酷。

    (如果您在处理这个问题时遇到困难……请记住,英语拼写直到 1700 年代才正式化。在那之前,只要读者能弄明白,这就足够了)。

    【讨论】:

    • 当然,这很酷。但在我看来,我的“严格的三年级老师”语法需求产生了一个更有趣的正则表达式问题......
    • 好点詹姆斯,一个人应该是一个严格的作者,但一个宽容的读者。
    【解决方案3】:

    只是保存在这里:

    (^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$)
    

    匹配所有罗马数字。不关心空字符串(至少需要一个罗马数字字母)。应该在 PCRE、Perl、Python 和 Ruby 中工作。

    在线 Ruby 演示:http://rubular.com/r/KLPR1zq3Hj

    在线转换:http://www.onlineconversion.com/roman_numerals_advanced.htm

    【讨论】:

    • 我不知道为什么,但主要答案在 MemoQ 的自动翻译列表中对我不起作用。然而,这个解决方案确实 - 不包括字符串开始/结束符号。
    • @orlando2bjr 很高兴为您提供帮助。是的,在这种情况下,我自己匹配一个数字,没有环境。如果您在文本中查找它,请确保您需要删除 ^$。干杯!
    • 如何在文本块中的任何位置进行匹配。仅当该行仅包含数字字符时才会匹配
    • @Verty00 查看之前的评论
    • 这里是同一个非捕获组,以便更好地清理结果。如果需要,您还可以使用字边界\b 甚至在外部使用另一个非捕获组 (?:) (\b(?=[MDCLXVI])M*(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})\b)
    【解决方案4】:

    为避免匹配空字符串,您需要重复该模式四次并将每个0 依次替换为1,并考虑VLD

    (M{1,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|C?D|D?C{1,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|X?L|L?X{1,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|I?V|V?I{1,3}))
    

    在这种情况下(因为此模式使用^$),您最好先检查空行,不要费心匹配它们。如果您使用的是word boundaries,那么您没有问题,因为没有空词之类的东西。 (至少正则表达式没有定义一个;不要开始哲学化,我在这里很务实!)


    在我自己的特定(现实世界)案例中,我需要在词尾匹配数字,但我没有找到其他方法。我需要从纯文本文档中删除脚注编号,其中诸如“红海cl和大堡礁cli”之类的文本已转换为@987654331 @。但是我仍然遇到问题,像 Tahitifantastic 这样的有效词被擦洗成 Tahitfantasti

    【讨论】:

    • 我有类似的问题(!):对项目列表(I 或 i 类型的 HTML OL)的剩余/剩余罗马数字进行“左修剪”。因此,当有剩余时,我需要在项目文本的开头(左侧)使用您的正则表达式进行清理(如修剪功能)......但更简单:项目从不使用 MC 或 @ 987654338@,那么,你有这种简化的正则表达式吗?
    • ...好吧,这里看起来还可以(!),(X{1,3}(IX|IV|V?I{0,3})|X{0,3}(IX|I?V|V?I{1,3}))
    • 你不需要重复模式,拒绝空字符串。你可以use a lookahead assertion
    【解决方案5】:

    幸运的是,数字范围被限制在 1..3999 左右。因此,您可以逐步构建正则表达式。

    <opt-thousands-part><opt-hundreds-part><opt-tens-part><opt-units-part>
    

    每个部分都将处理罗马符号的变幻莫测。例如,使用 Perl 表示法:

    <opt-hundreds-part> = m/(CM|DC{0,3}|CD|C{1,3})?/;
    

    重复并组装。

    添加&lt;opt-hundreds-part&gt; 可以进一步压缩:

    <opt-hundreds-part> = m/(C[MD]|D?C{0,3})/;
    

    由于 'D?C{0,3}' 子句不能匹配任何内容,因此不需要问号。而且,最有可能的是,括号应该是非捕获类型 - 在 Perl 中:

    <opt-hundreds-part> = m/(?:C[MD]|D?C{0,3})/;
    

    当然,它也应该不区分大小写。

    您还可以扩展它以处理 James Curran 提到的选项(允许 XM 或 IM 用于 990 或 999,CCCC 用于 400 等)。

    <opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/;
    

    【讨论】:

    • thousands hundreds tens units开头,方便create a FSM that computes and validates given Roman numerals
    • 你说的是什么意思,还好数字的范围限制在1..3999左右?谁限制了它?
    • @SexyBeast:5,000 没有任何标准的罗马符号,更不用说更大的数字了,所以一直有效的规律性停止工作。
    • 不知道你为什么这么认为,但罗马数字可以代表数百万的数字。 en.wikipedia.org/wiki/Roman_numerals#Large_numbers
    • @AmbroseChapel:正如我所说,5,000 没有任何(单一)标准符号,更不用说更大的数字了。您必须使用您链接到的 Wikipedia 文章中概述的多种不同系统中的一种,并且您在系统的正字法上遇到上划线、下划线或反向 C 等问题。您必须向任何人解释什么您正在使用的系统及其含义;一般而言,人们不会识别 M 以外的罗马数字。您可以选择不这样想;那是你的特权,就像我的特权支持我以前的 cmets。
    【解决方案6】:
    import re
    pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
    if re.search(pattern, 'XCCMCI'):
        print 'Valid Roman'
    else:
        print 'Not valid Roman'
    

    真正想了解逻辑的朋友,请看diveintopython的3页分步说明。

    与原始解决方案(具有M{0,4})的唯一区别是因为我发现“MMMM”不是有效的罗马数字(老罗马人很可能没有考虑过这个巨大的数字,并且会不同意我的观点)。如果您是不同意的古罗马人之一,请原谅我并使用 {0,4} 版本。

    【讨论】:

    • 答案中的正则表达式允许空数字。如果你不想要它;你可以use a lookahead assertion,拒绝空字符串(它也会忽略字母的大小写)。
    【解决方案7】:

    在我的例子中,我试图在文本中用一个单词来查找和替换所有出现的罗马数字,所以我不能使用行的开头和结尾。所以@paxdiablo 解决方案发现了许多零长度匹配。 我最终得到了以下表达式:

    (?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})
    

    我最终的 Python 代码是这样的:

    import re
    text = "RULES OF LIFE: I. STAY CURIOUS; II. NEVER STOP LEARNING"
    text = re.sub(r'(?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})', 'ROMAN', text)
    print(text)
    

    输出:

    RULES OF LIFE: ROMAN. STAY CURIOUS; ROMAN. NEVER STOP LEARNING
    

    【讨论】:

    • 用这个试试text = "I'm RULES OF LIFE: I. STAY CURIOUS; II. NEVER STOP LEARNING" ,它会输出ROMAN'm RULES OF LIFE: ROMAN. STAY CURIOUS; ROMAN. NEVER STOP LEARNING
    • 这在 javascript 中也适用于我。
    【解决方案8】:

    我看到了多个不涵盖空字符串或使用前瞻来解决此问题的答案。我想添加一个新的答案,它确实涵盖了空字符串并且不使用前瞻。正则表达式如下:

    ^(I[VX]|VI{0,3}|I{1,3})|((X[LC]|LX{0,3}|X{1,3})(I[VX]|V?I{0,3}))|((C[DM]|DC{0,3}|C{1,3})(X[LC]|L?X{0,3})(I[VX]|V?I{0,3}))|(M+(C[DM]|D?C{0,3})(X[LC]|L?X{0,3})(I[VX]|V?I{0,3}))$

    我允许无限 MM+ 但当然有人可以更改为 M{1,4} 如果需要只允许 1 或 4。

    下面是一个有助于理解它在做什么的可视化,前面有两个在线演示:

    Debuggex Demo

    Regex 101 Demo

    【讨论】:

    • 我非常喜欢这个。它更长,但似乎也更高效。为了降低结果的复杂性,您可以使用非捕获组:(?:I[VX]|VI{0,3}|I{1,3})|(?:(X[LC]|LX{0,3}|X{1,3})(?:I[VX]|V?I{0,3}))|(?:(?:C[DM]|DC{0,3}|C{1,3})(?:X[LC]|L?X{0,3})(?:I[VX]|V?I{0,3}))|(?:M+(?:C[DM]|D?C{0,3})(?:X[LC]|L?X{0,3})(?:I[VX]|V?I{0,3}))
    【解决方案9】:

    正如杰里米和帕克斯在上面指出的那样...... '^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3}) $' 应该是您所追求的解决方案...

    应该附加的特定 URL (恕我直言) 是 http://thehazeltree.org/diveintopython/7.html

    示例 7.8 是使用 {n,m} 的简写形式

    【讨论】:

      【解决方案10】:

      我在这里回答这个问题Regular Expression in Python for Roman Numerals
      因为它被标记为这个问题的完全重复。

      名称可能相似,但这是一个特定的正则表达式问题/问题
      从这个问题的答案可以看出。

      可以将正在寻找的项目组合成一个交替,然后
      封装在捕获组中,该捕获组将使用 findall()
      放入列表中 功能。
      这样做是这样的:

      >>> import re
      >>> target = (
      ... r"this should pass v" + "\n"
      ... r"this is a test iii" + "\n"
      ... )
      >>>
      >>> re.findall( r"(?m)\s(i{1,3}v*|v)$", target )
      ['v', 'iii']
      

      对因子和仅捕获数字的正则表达式修改如下:

       (?m)
       \s 
       (                     # (1 start)
            i{1,3} 
            v* 
         |  v
       )                     # (1 end)
       $
      

      【讨论】:

        【解决方案11】:

        下面的表达式对我来说可以验证罗马数字。

        ^M{0,4}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$
        

        这里,

        • M{0,4} 将匹配数千个
        • C[MD]|D?C{0,3} 将匹配数百个
        • X[CL]|L?X{0,3} 将匹配十位
        • I[XV]|V?I{0,3} 将匹配单位

        下面是一个有助于理解它在做什么的可视化,前面有两个在线演示:

        Debuggex Demo

        Regex 101 Demo

        Python 代码:

        import re
        regex = re.compile("^M{0,4}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")
        matchArray = regex.match("MMMCMXCIX")
        

        【讨论】:

          【解决方案12】:

          @paxdiablo 为避免匹配空字符串而建议的 积极 后视和前瞻对我来说似乎不起作用。

          我已通过使用 negative 前瞻来修复它:

          (?!$)M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})
          

          注意:如果你在正则表达式的末尾附加一些东西(例如“foobar”,那么显然你必须用(?!f)替换(?!$)(其中f是“foobar”的第一个字符)。

          【讨论】:

            【解决方案13】:

            Steven Levithan 在his post 中使用了这个正则表达式,它在“去罗马化”值之前验证罗马数字:

            /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
            

            【讨论】:

              【解决方案14】:

              这适用于 Java 和 PCRE 正则表达式引擎,现在应该适用于最新的 JavaScript,但可能不适用于所有上下文。

              (?&lt;![A-Z])(M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3}))(?![A-Z])

              第一部分是残酷的负面回顾。但是,出于逻辑目的,它是最容易理解的。基本上,第一个(?&lt;!) 是说不匹配中间([MATCH]) 如果有字母在中间([MATCH]) 和最后一个(?!) 说不匹配中间([MATCH]) 如果有字母紧随其后。

              中间的([MATCH])只是匹配罗马数字序列最常用的正则表达式。但是现在,如果它周围有任何字母,你不想匹配它。

              你自己看看。 https://regexr.com/4vce5

              【讨论】:

                【解决方案15】:

                这里有一些非常棒的答案,但没有一个适合我,因为我需要能够只匹配字符串中的有效罗马数字而不匹配空字符串,并且只匹配它们自己的数字(即不在一个单词中) .

                让我把你介绍给Reilly's Modern roman numerals strict expression

                ^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$
                

                开箱即用它与我需要的非常接近,但它只会匹配独立的罗马数字,并且当更改为匹配字符串时,它将在某些点匹配空字符串(单词以大写 V、M 等开头) .) 并且还会部分匹配无效的罗马数字,例如 MMLLVVDD、XXLLVVDD、MMMMDLVX、XVXDLMM 和 MMMCCMLXXV。

                所以,经过一些修改,我最终得到了这个:

                (?<![MDCLXVI])(?=[MDCLXVI])M{0,3}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})[^ ]\b
                

                添加的negative lookbehind 将确保它不会部分匹配无效的罗马数字并将第一个 M 锁定为 3,因为这是它在 Roman numeral standard form 中的最高值。

                截至目前,这是唯一一个通过我的extensive test suit 测试的 4000 多个测试的正则表达式,其中包括从 1 到 3999 的所有可能的罗马数字、字符串中的罗马数字和我上面提到的无效罗马数字。

                这是来自https://regex101.com/ 的截图:

                【讨论】:

                • 我在开头添加了一个单词边界,因此它不会捕获恰好以拉丁数字结尾的单词(如果字符串匹配不区分大小写)。
                【解决方案16】:

                Jeremy 和 Pax 的解决方案的问题是,它也匹配“无”。

                以下正则表达式至少需要一个罗马数字:

                ^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$
                

                【讨论】:

                • 那一个行不通(除非你使用了一个非常奇怪的正则表达式实现)——| 的左边部分可以匹配一个空字符串和所有有效的罗马数字,所以右边侧是完全多余的。是的,它仍然匹配一个空字符串。
                • “Jeremy 和 Pax 的解决方案的问题是”......与这个答案的问题完全相同。如果您要针对假定的问题提出解决方案,您可能应该对其进行测试。 :-)
                • 我得到了空字符串
                【解决方案17】:

                我会为我的工作编写函数。 这是 PowerShell 中的两个罗马数字函数。

                function ConvertFrom-RomanNumeral
                {
                  <#
                    .SYNOPSIS
                        Converts a Roman numeral to a number.
                    .DESCRIPTION
                        Converts a Roman numeral - in the range of I..MMMCMXCIX - to a number.
                    .EXAMPLE
                        ConvertFrom-RomanNumeral -Numeral MMXIV
                    .EXAMPLE
                        "MMXIV" | ConvertFrom-RomanNumeral
                  #>
                    [CmdletBinding()]
                    [OutputType([int])]
                    Param
                    (
                        [Parameter(Mandatory=$true,
                                   HelpMessage="Enter a roman numeral in the range I..MMMCMXCIX",
                                   ValueFromPipeline=$true,
                                   Position=0)]
                        [ValidatePattern("^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$")]
                        [string]
                        $Numeral
                    )
                
                    Begin
                    {
                        $RomanToDecimal = [ordered]@{
                            M  = 1000
                            CM =  900
                            D  =  500
                            CD =  400
                            C  =  100
                            XC =   90
                            L  =   50
                            X  =   10
                            IX =    9
                            V  =    5
                            IV =    4
                            I  =    1
                        }
                    }
                    Process
                    {
                        $roman = $Numeral + " "
                        $value = 0
                
                        do
                        {
                            foreach ($key in $RomanToDecimal.Keys)
                            {
                                if ($key.Length -eq 1)
                                {
                                    if ($key -match $roman.Substring(0,1))
                                    {
                                        $value += $RomanToDecimal.$key
                                        $roman  = $roman.Substring(1)
                                        break
                                    }
                                }
                                else
                                {
                                    if ($key -match $roman.Substring(0,2))
                                    {
                                        $value += $RomanToDecimal.$key
                                        $roman  = $roman.Substring(2)
                                        break
                                    }
                                }
                            }
                        }
                        until ($roman -eq " ")
                
                        $value
                    }
                    End
                    {
                    }
                }
                
                function ConvertTo-RomanNumeral
                {
                  <#
                    .SYNOPSIS
                        Converts a number to a Roman numeral.
                    .DESCRIPTION
                        Converts a number - in the range of 1 to 3,999 - to a Roman numeral.
                    .EXAMPLE
                        ConvertTo-RomanNumeral -Number (Get-Date).Year
                    .EXAMPLE
                        (Get-Date).Year | ConvertTo-RomanNumeral
                  #>
                    [CmdletBinding()]
                    [OutputType([string])]
                    Param
                    (
                        [Parameter(Mandatory=$true,
                                   HelpMessage="Enter an integer in the range 1 to 3,999",
                                   ValueFromPipeline=$true,
                                   Position=0)]
                        [ValidateRange(1,3999)]
                        [int]
                        $Number
                    )
                
                    Begin
                    {
                        $DecimalToRoman = @{
                            Ones      = "","I","II","III","IV","V","VI","VII","VIII","IX";
                            Tens      = "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC";
                            Hundreds  = "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM";
                            Thousands = "","M","MM","MMM"
                        }
                
                        $column = @{Thousands = 0; Hundreds = 1; Tens = 2; Ones = 3}
                    }
                    Process
                    {
                        [int[]]$digits = $Number.ToString().PadLeft(4,"0").ToCharArray() |
                                            ForEach-Object { [Char]::GetNumericValue($_) }
                
                        $RomanNumeral  = ""
                        $RomanNumeral += $DecimalToRoman.Thousands[$digits[$column.Thousands]]
                        $RomanNumeral += $DecimalToRoman.Hundreds[$digits[$column.Hundreds]]
                        $RomanNumeral += $DecimalToRoman.Tens[$digits[$column.Tens]]
                        $RomanNumeral += $DecimalToRoman.Ones[$digits[$column.Ones]]
                
                        $RomanNumeral
                    }
                    End
                    {
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2019-02-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多