【问题标题】:Split a string by spaces -- preserving quoted substrings -- in Python在 Python 中用空格分割字符串——保留带引号的子字符串
【发布时间】:2010-09-09 22:44:05
【问题描述】:

我有一个这样的字符串:

this is "a test"

我正在尝试在 Python 中编写一些东西以按空格将其拆分,同时忽略引号内的空格。我正在寻找的结果是:

['this','is','a test']

PS。我知道你会问“如果引号中有引号会发生什么,好吧,在我的应用程序中,这永远不会发生。

【问题讨论】:

    标签: python regex


    【解决方案1】:

    您需要来自内置shlex 模块的split

    >>> import shlex
    >>> shlex.split('this is "a test"')
    ['this', 'is', 'a test']
    

    这应该完全符合您的要求。

    【讨论】:

    • 使用 "posix=False" 保留引用。 shlex.split('this is "a test"', posix=False) 返回['this', 'is', '"a test"']
    • @MatthewG。 Python 2.7.3 中的“修复”意味着将 unicode 字符串传递给 shlex.split() 将触发 UnicodeEncodeError 异常。
    【解决方案2】:

    查看shlex 模块,尤其是shlex.split

    >>> import shlex
    >>> shlex.split('This is "a test"')
    ['This', 'is', 'a test']
    

    【讨论】:

    • 哇,令人印象深刻。您发布的时间与@Jerub 完全相同。提问后 2 分钟!
    【解决方案3】:

    试试这个:

      def adamsplit(s):
        result = []
        inquotes = False
        for substring in s.split('"'):
          if not inquotes:
            result.extend(substring.split())
          else:
            result.append(substring)
          inquotes = not inquotes
        return result
    

    一些测试字符串:

    'This is "a test"' -> ['This', 'is', 'a test']
    '"This is \'a test\'"' -> ["This is 'a test'"]
    

    【讨论】:

    • 请提供您认为会失败的字符串的 repr。
    • adamsplit("This is 'a test'")['This', 'is', "'a", "test'"]
    • OP 只说“在引号内”,并且只有一个带双引号的例子。
    • 有没有办法保留引号本身?例如,['This', 'is', "'a test'"]
    【解决方案4】:

    如果你不关心子字符串而不是简单

    >>> 'a short sized string with spaces '.split()
    

    性能:

    >>> s = " ('a short sized string with spaces '*100).split() "
    >>> t = timeit.Timer(stmt=s)
    >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
    171.39 usec/pass
    

    或字符串模块

    >>> from string import split as stringsplit; 
    >>> stringsplit('a short sized string with spaces '*100)
    

    性能:字符串模块似乎比字符串方法执行得更好

    >>> s = "stringsplit('a short sized string with spaces '*100)"
    >>> t = timeit.Timer(s, "from string import split as stringsplit")
    >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
    154.88 usec/pass
    

    或者你可以使用RE引擎

    >>> from re import split as resplit
    >>> regex = '\s+'
    >>> medstring = 'a short sized string with spaces '*100
    >>> resplit(regex, medstring)
    

    性能

    >>> s = "resplit(regex, medstring)"
    >>> t = timeit.Timer(s, "from re import split as resplit; regex='\s+'; medstring='a short sized string with spaces '*100")
    >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
    540.21 usec/pass
    

    对于非常长的字符串,您不应将整个字符串加载到内存中,而是拆分行或使用迭代循环

    【讨论】:

    • 您似乎错过了问题的全部要点。字符串中有引号部分不需要拆分。
    【解决方案5】:

    由于这个问题被标记为正则表达式,我决定尝试正则表达式方法。我先将引号部分中的所有空格替换为\x00,然后用空格分割,然后将\x00替换回每个部分中的空格。

    两个版本做同样的事情,但是 splitter 比 splitter2 更易读。

    import re
    
    s = 'this is "a test" some text "another test"'
    
    def splitter(s):
        def replacer(m):
            return m.group(0).replace(" ", "\x00")
        parts = re.sub('".+?"', replacer, s).split()
        parts = [p.replace("\x00", " ") for p in parts]
        return parts
    
    def splitter2(s):
        return [p.replace("\x00", " ") for p in re.sub('".+?"', lambda m: m.group(0).replace(" ", "\x00"), s).split()]
    
    print splitter2(s)
    

    【讨论】:

    • 您应该改用 re.Scanner。它更可靠(实际上我已经使用 re.Scanner 实现了类似 shlex)。
    • +1 嗯,这是一个非常聪明的想法,将问题分解为多个步骤,因此答案并不是非常复杂。 Shlex 并没有完全满足我的需求,即使尝试对其进行调整。单程正则表达式解决方案变得非常奇怪和复杂。
    【解决方案6】:

    我看到这里的正则表达式方法看起来很复杂和/或错误。这让我感到惊讶,因为正则表达式语法可以很容易地描述“空格或被引号包围的东西”,并且大多数正则表达式引擎(包括 Python 的)都可以在正则表达式上拆分。所以如果你要使用正则表达式,为什么不直接说出你的意思呢?:

    test = 'this is "a test"'  # or "this is 'a test'"
    # pieces = [p for p in re.split("( |[\\\"'].*[\\\"'])", test) if p.strip()]
    # From comments, use this:
    pieces = [p for p in re.split("( |\\\".*?\\\"|'.*?')", test) if p.strip()]
    

    解释:

    [\\\"'] = double-quote or single-quote
    .* = anything
    ( |X) = space or X
    .strip() = remove space and empty-string separators
    

    不过,shlex 可能会提供更多功能。

    【讨论】:

    • 我的想法大致相同,但建议改为 [t.strip('"') for t in re.findall(r'[^\s"]+|"[^"] *"', '这是一个“测试”')]
    • +1 我使用它是因为它比 shlex 快很多。
    • 那个代码几乎看起来像 perl,你没听说过 r"raw strings" 吗?
    • 为什么是三个反斜杠?一个简单的反斜杠不会做同样的事情吗?
    • 在使用正则表达式时应该使用原始字符串。
    【解决方案7】:

    根据您的用例,您可能还想查看csv 模块:

    import csv
    lines = ['this is "a string"', 'and more "stuff"']
    for row in csv.reader(lines, delimiter=" "):
        print(row)
    

    输出:

    ['this', 'is', 'a string']
    ['and', 'more', 'stuff']
    

    【讨论】:

    • 有用,当 shlex 去除一些需要的字符时
    • CSV 的use two double quotes in a row(如并排,"")表示一个双引号",因此会将两个双引号变成一个单引号'this is "a string""' 和@987654329 @ 都将映射到 ['this', 'is', 'a string"']
    【解决方案8】:

    嗯,似乎找不到“回复”按钮...无论如何,这个答案是基于 Kate 的方法,但正确地将字符串拆分为包含转义引号的子字符串,并且还删除了开头和结尾的引号子字符串:

      [i.strip('"').strip("'") for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]
    

    这适用于'This is " a \\\"test\\\"\\\'s substring"' 之类的字符串(不幸的是,为了防止 Python 删除转义,必须使用疯狂的标记)。

    如果不希望返回列表中的字符串产生转义,您可以使用这个稍作改动的函数版本:

    [i.strip('"').strip("'").decode('string_escape') for i in re.split(r'(\s+|(?<!\\)".*?(?<!\\)"|(?<!\\)\'.*?(?<!\\)\')', string) if i.strip()]
    

    【讨论】:

      【解决方案9】:

      为了解决某些 Python 2 版本中的 unicode 问题,我建议:

      from shlex import split as _split
      split = lambda a: [b.decode('utf-8') for b in _split(a.encode('utf-8'))]
      

      【讨论】:

      • 对于 python 2.7.5 这应该是:split = lambda a: [b.decode('utf-8') for b in _split(a)] 否则你会得到:UnicodeDecodeError: 'ascii' codec can't decode byte ... in position ...: ordinal not in range(128)
      【解决方案10】:

      我使用 shlex.split 处理 70,000,000 行 squid 日志,太慢了。所以我切换到re。

      如果你对 shlex 有性能问题,请试试这个。

      import re
      
      def line_split(line):
          return re.findall(r'[^"\s]\S*|".+?"', line)
      

      【讨论】:

        【解决方案11】:

        我建议:

        测试字符串:

        s = 'abc "ad" \'fg\' "kk\'rdt\'" zzz"34"zzz "" \'\''
        

        同时捕获 "" 和 '':

        import re
        re.findall(r'"[^"]*"|\'[^\']*\'|[^"\'\s]+',s)
        

        结果:

        ['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz', '""', "''"]
        

        忽略空的 "" 和 '':

        import re
        re.findall(r'"[^"]+"|\'[^\']+\'|[^"\'\s]+',s)
        

        结果:

        ['abc', '"ad"', "'fg'", '"kk\'rdt\'"', 'zzz', '"34"', 'zzz']
        

        【讨论】:

        • 也可以写成re.findall("(?:\".*?\"|'.*?'|[^\s'\"]+)", s)
        【解决方案12】:

        要保留引号,请使用此功能:

        def getArgs(s):
            args = []
            cur = ''
            inQuotes = 0
            for char in s.strip():
                if char == ' ' and not inQuotes:
                    args.append(cur)
                    cur = ''
                elif char == '"' and not inQuotes:
                    inQuotes = 1
                    cur += char
                elif char == '"' and inQuotes:
                    inQuotes = 0
                    cur += char
                else:
                    cur += char
            args.append(cur)
            return args
        

        【讨论】:

        • 当与更大的字符串比较时,你的函数太慢了
        【解决方案13】:

        不同答案的速度测试:

        import re
        import shlex
        import csv
        
        line = 'this is "a test"'
        
        %timeit [p for p in re.split("( |\\\".*?\\\"|'.*?')", line) if p.strip()]
        100000 loops, best of 3: 5.17 µs per loop
        
        %timeit re.findall(r'[^"\s]\S*|".+?"', line)
        100000 loops, best of 3: 2.88 µs per loop
        
        %timeit list(csv.reader([line], delimiter=" "))
        The slowest run took 9.62 times longer than the fastest. This could mean that an intermediate result is being cached.
        100000 loops, best of 3: 2.4 µs per loop
        
        %timeit shlex.split(line)
        10000 loops, best of 3: 50.2 µs per loop
        

        【讨论】:

          【解决方案14】:

          公认的shlex 方法的主要问题是它不会忽略带引号的子字符串之外的转义字符,并且在某些极端情况下会产生一些意外的结果。

          我有以下用例,其中我需要一个拆分函数来拆分输入字符串,以便保留单引号或双引号子字符串,并能够在此类子字符串中转义引号。不带引号的字符串中的引号不应与任何其他字符区别对待。一些具有预期输出的示例测试用例:

           输入字符串 |预期产出
          ================================================
           'abc def' | ['abc', 'def']
           "abc \\s def" | ['abc', '\\s', 'def']
           '"abc def" ghi' | ['abc def', 'ghi']
           "'abc def' ghi" | ['abc def', 'ghi']
           '"abc \\" def" ghi' | ['abc " def', 'ghi']
           "'abc \\' def' ghi" | [“abc'def”,'ghi']
           "'abc \\s def' ghi" | ['abc \\s def', 'ghi']
           '"abc \\s def" ghi' | ['abc \\s def', 'ghi']
           '"" 测试' | ['', '测试']
           "'' 测试" | ['', '测试']
           "abc'def" | [“abc'def”]
           "abc'def'" | ["abc'def'"]
           "abc'def' ghi" | ["abc'def'", 'ghi']
           "abc'def'ghi" | [“abc'def'ghi”]
           'abc"def' | ['abc"def']
           'abc“定义”' | ['abc“定义”']
           'abc"def" ghi' | ['abc"def"', 'ghi']
           'abc"def"ghi' | ['abc"def"ghi']
           "r'AA'r'.*_xyz$'" | ["r'AA'", "r'.*_xyz$'"]
           'abc"def ghi"' | ['abc"def ghi"']
           'abc"def ghi""jkl"' | ['abc"def ghi""jkl"']
           'a"b c"d"e"f"g h"' | ['a"b c"d"e"f"g h"']
           'c="ls /" 键入键' | ['c="ls /"', '类型', '键']
           "abc'def ghi'" | ["abc'def ghi'"]
           "c='ls /' 键入键" | ["c='ls /'", 'type', 'key']

          我最终得到了以下函数来拆分字符串,以便所有输入字符串的预期输出结果:

          import re
          
          def quoted_split(s):
              def strip_quotes(s):
                  if s and (s[0] == '"' or s[0] == "'") and s[0] == s[-1]:
                      return s[1:-1]
                  return s
              return [strip_quotes(p).replace('\\"', '"').replace("\\'", "'") \
                      for p in re.findall(r'(?:[^"\s]*"(?:\\.|[^"])*"[^"\s]*)+|(?:[^\'\s]*\'(?:\\.|[^\'])*\'[^\'\s]*)+|[^\s]+', s)]
          

          它不漂亮;但它有效。以下测试应用程序检查其他方法(目前为shlexcsv)和自定义拆分实现的结果:

          #!/bin/python2.7
          
          import csv
          import re
          import shlex
          
          from timeit import timeit
          
          def test_case(fn, s, expected):
              try:
                  if fn(s) == expected:
                      print '[ OK ] %s -> %s' % (s, fn(s))
                  else:
                      print '[FAIL] %s -> %s' % (s, fn(s))
              except Exception as e:
                  print '[FAIL] %s -> exception: %s' % (s, e)
          
          def test_case_no_output(fn, s, expected):
              try:
                  fn(s)
              except:
                  pass
          
          def test_split(fn, test_case_fn=test_case):
              test_case_fn(fn, 'abc def', ['abc', 'def'])
              test_case_fn(fn, "abc \\s def", ['abc', '\\s', 'def'])
              test_case_fn(fn, '"abc def" ghi', ['abc def', 'ghi'])
              test_case_fn(fn, "'abc def' ghi", ['abc def', 'ghi'])
              test_case_fn(fn, '"abc \\" def" ghi', ['abc " def', 'ghi'])
              test_case_fn(fn, "'abc \\' def' ghi", ["abc ' def", 'ghi'])
              test_case_fn(fn, "'abc \\s def' ghi", ['abc \\s def', 'ghi'])
              test_case_fn(fn, '"abc \\s def" ghi', ['abc \\s def', 'ghi'])
              test_case_fn(fn, '"" test', ['', 'test'])
              test_case_fn(fn, "'' test", ['', 'test'])
              test_case_fn(fn, "abc'def", ["abc'def"])
              test_case_fn(fn, "abc'def'", ["abc'def'"])
              test_case_fn(fn, "abc'def' ghi", ["abc'def'", 'ghi'])
              test_case_fn(fn, "abc'def'ghi", ["abc'def'ghi"])
              test_case_fn(fn, 'abc"def', ['abc"def'])
              test_case_fn(fn, 'abc"def"', ['abc"def"'])
              test_case_fn(fn, 'abc"def" ghi', ['abc"def"', 'ghi'])
              test_case_fn(fn, 'abc"def"ghi', ['abc"def"ghi'])
              test_case_fn(fn, "r'AA' r'.*_xyz$'", ["r'AA'", "r'.*_xyz$'"])
              test_case_fn(fn, 'abc"def ghi"', ['abc"def ghi"'])
              test_case_fn(fn, 'abc"def ghi""jkl"', ['abc"def ghi""jkl"'])
              test_case_fn(fn, 'a"b c"d"e"f"g h"', ['a"b c"d"e"f"g h"'])
              test_case_fn(fn, 'c="ls /" type key', ['c="ls /"', 'type', 'key'])
              test_case_fn(fn, "abc'def ghi'", ["abc'def ghi'"])
              test_case_fn(fn, "c='ls /' type key", ["c='ls /'", 'type', 'key'])
          
          def csv_split(s):
              return list(csv.reader([s], delimiter=' '))[0]
          
          def re_split(s):
              def strip_quotes(s):
                  if s and (s[0] == '"' or s[0] == "'") and s[0] == s[-1]:
                      return s[1:-1]
                  return s
              return [strip_quotes(p).replace('\\"', '"').replace("\\'", "'") for p in re.findall(r'(?:[^"\s]*"(?:\\.|[^"])*"[^"\s]*)+|(?:[^\'\s]*\'(?:\\.|[^\'])*\'[^\'\s]*)+|[^\s]+', s)]
          
          if __name__ == '__main__':
              print 'shlex\n'
              test_split(shlex.split)
              print
          
              print 'csv\n'
              test_split(csv_split)
              print
          
              print 're\n'
              test_split(re_split)
              print
          
              iterations = 100
              setup = 'from __main__ import test_split, test_case_no_output, csv_split, re_split\nimport shlex, re'
              def benchmark(method, code):
                  print '%s: %.3fms per iteration' % (method, (1000 * timeit(code, setup=setup, number=iterations) / iterations))
              benchmark('shlex', 'test_split(shlex.split, test_case_no_output)')
              benchmark('csv', 'test_split(csv_split, test_case_no_output)')
              benchmark('re', 'test_split(re_split, test_case_no_output)')
          

          输出:

          shlex [确定] abc def -> ['abc', 'def'] [失败] abc \s def -> ['abc', 's', 'def'] [确定]“abc def”ghi->['abc def','ghi'] [ 好 ] 'abc def' ghi -> ['abc def', 'ghi'] [确定]“abc\”def“ghi->['abc“def','ghi'] [失败] 'abc \' def' ghi -> 异常:没有结束引号 [ OK ] 'abc \s def' ghi -> ['abc \\s def', 'ghi'] [ 好 ] "abc \s def" ghi -> ['abc \\s def', 'ghi'] [确定]“”测试-> ['','测试'] [确定]''测试-> ['','测试'] [失败] abc'def -> 异常:没有结束引号 [失败] abc'def' -> ['abcdef'] [失败] abc'def' ghi -> ['abcdef', 'ghi'] [失败] abc'def'ghi -> ['abcdefghi'] [失败] abc"def -> 异常:没有结束引号 [失败] abc"def" -> ['abcdef'] [失败] abc"def" ghi -> ['abcdef', 'ghi'] [失败] abc"def"ghi -> ['abcdefghi'] [失败] r'AA' r'.*_xyz$' -> ['rAA', 'r.*_xyz$'] [失败] abc"def ghi" -> ['abcdef ghi'] [失败] abc"def ghi""jkl" -> ['abcdef ghijkl'] [失败] a"b c"d"e"f"g h" -> ['ab cdefg h'] [失败] c="ls /" 键入键 -> ['c=ls /', 'type', 'key'] [失败] abc'def ghi' -> ['abcdef ghi'] [失败] c='ls /' 键入键 -> ['c=ls /', 'type', 'key'] CSV [确定] abc def -> ['abc', 'def'] [ 好 ] abc \s def -> ['abc', '\\s', 'def'] [确定]“abc def”ghi->['abc def','ghi'] [失败] 'abc def' ghi -> ["'abc", "def'", 'ghi'] [失败] "abc \" def" ghi -> ['abc \\', 'def"', 'ghi'] [失败] 'abc \' def' ghi -> ["'abc", "\\'", "def'", 'ghi'] [失败] 'abc \s def' ghi -> ["'abc", '\\s', "def'", 'ghi'] [ 好 ] "abc \s def" ghi -> ['abc \\s def', 'ghi'] [确定]“”测试-> ['','测试'] [失败]''测试-> [“''”,'测试'] [ 好 ] abc'def -> ["abc'def"] [确定] abc'def'-> [“abc'def'”] [OK] abc'def' ghi -> ["abc'def'", 'ghi'] [确定] abc'def'ghi -> [“abc'def'ghi”] [确定] abc"def -> ['abc"def'] [确定] abc“def”-> ['abc“def”'] [确定] abc"def" ghi -> ['abc"def"', 'ghi'] [确定] abc"def"ghi -> ['abc"def"ghi'] [确定] r'AA' r'.*_xyz$' -> ["r'AA'", "r'.*_xyz$'"] [失败] abc"def ghi" -> ['abc"def', 'ghi"'] [失败] abc"def ghi""jkl" -> ['abc"def', 'ghi""jkl"'] [失败] a"b c"d"e"f"g h" -> ['a"b', 'c"d"e"f"g', 'h"'] [失败] c="ls /" 键入键-> ['c="ls', '/"', 'type', 'key'] [失败] abc'def ghi' -> ["abc'def", "ghi'"] [失败] c='ls /' 类型键 -> ["c='ls", "/'", 'type', 'key'] 回覆 [确定] abc def -> ['abc', 'def'] [ 好 ] abc \s def -> ['abc', '\\s', 'def'] [确定]“abc def”ghi->['abc def','ghi'] [ 好 ] 'abc def' ghi -> ['abc def', 'ghi'] [确定]“abc\”def“ghi->['abc“def','ghi'] [确定]'abc\'def'ghi->[“abc'def”,'ghi'] [ OK ] 'abc \s def' ghi -> ['abc \\s def', 'ghi'] [ 好 ] "abc \s def" ghi -> ['abc \\s def', 'ghi'] [确定]“”测试-> ['','测试'] [确定]''测试-> ['','测试'] [ 好 ] abc'def -> ["abc'def"] [确定] abc'def'-> [“abc'def'”] [OK] abc'def' ghi -> ["abc'def'", 'ghi'] [确定] abc'def'ghi -> [“abc'def'ghi”] [确定] abc"def -> ['abc"def'] [确定] abc“def”-> ['abc“def”'] [确定] abc"def" ghi -> ['abc"def"', 'ghi'] [确定] abc"def"ghi -> ['abc"def"ghi'] [确定] r'AA' r'.*_xyz$' -> ["r'AA'", "r'.*_xyz$'"] [确定] abc"def ghi" -> ['abc"def ghi"'] [确定] abc"def ghi""jkl" -> ['abc"def ghi""jkl"'] [确定] a"b c"d"e"f"g h" -> ['a"b c"d"e"f"g h"'] [ OK ] c="ls /" 键入键-> ['c="ls /"', 'type', 'key'] [OK] abc'def ghi' -> ["abc'def ghi'"] [ OK ] c='ls /' 键入键 -> ["c='ls /'", 'type', 'key'] shlex:每次迭代 0.335ms csv:每次迭代 0.036 毫秒 回复:每次迭代 0.068 毫秒

          所以性能比shlex好很多,并且可以通过预编译正则表达式进一步提高,在这种情况下它将优于csv方法。

          【讨论】:

          • 不确定你在说什么:``` >>> shlex.split('this is "a test"') ['this', 'is', 'a test'] >>> shlex.split('this is \\"a test\\"') ['this', 'is', '"a', 'test"'] >>> shlex.split('this is "一个 \\"test\\""') ['this', 'is', 'a "test"'] ```
          • @morsik,你的意思是什么?也许您的用例与我的不匹配?当您查看测试用例时,您会看到所有 shlex 的行为与我的用例不同的情况。
          • 我很有希望,但不幸的是,在我需要 shlexcsv 也失败的情况下,你的方法也失败了。要解析的字符串:command="echo hi" type key.
          • @Jean-BernardJansen,在处理报价时确实存在一些问题;我已经更新了正则表达式,它现在应该可以更好地处理带引号的子字符串了。
          【解决方案15】:

          出于性能原因,re 似乎更快。这是我使用保留外引号的最小贪心运算符的解决方案:

          re.findall("(?:\".*?\"|\S)+", s)
          

          结果:

          ['this', 'is', '"a test"']
          

          它将aaa"bla blub"bbb 之类的结构放在一起,因为这些标记没有用空格分隔。如果字符串包含转义字符,则可以这样匹配:

          >>> a = "She said \"He said, \\\"My name is Mark.\\\"\""
          >>> a
          'She said "He said, \\"My name is Mark.\\""'
          >>> for i in re.findall("(?:\".*?[^\\\\]\"|\S)+", a): print(i)
          ...
          She
          said
          "He said, \"My name is Mark.\""
          

          请注意,这也通过模式的\S 部分匹配空字符串""

          【讨论】:

          • 此解决方案的另一个重要优点是它在分隔字符方面的多功能性(例如, 通过'(?:".*?"|[^,])+')。这同样适用于引用(封闭)字符。
          【解决方案16】:

          作为一个选项尝试 tssplit:

          In [1]: from tssplit import tssplit
          In [2]: tssplit('this is "a test"', quote='"', delimiter='')
          Out[2]: ['this', 'is', 'a test']
          

          【讨论】:

            猜你喜欢
            • 2023-01-13
            • 2013-12-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-09-03
            • 1970-01-01
            相关资源
            最近更新 更多