【问题标题】:need help with splitting a string in python需要帮助在 python 中拆分字符串
【发布时间】:2010-08-03 01:14:06
【问题描述】:

我正在尝试使用以下模式标记字符串。

>>> splitter = re.compile(r'((\w*)(\d*)\-\s?(\w*)(\d*)|(?x)\$?\d+(\.\d+)?(\,\d+)?|([A-Z]\.)+|(Mr)\.|(Sen)\.|(Miss)\.|.$|\w+|[^\w\s])')
>>> splitter.split("Hello! Hi, I am debating this predicament called life. Can you help me?")

我得到以下输出。有人可以指出我需要纠正什么吗?我对一堆“无”感到困惑。此外,如果有更好的方法来标记字符串,我真的很感激额外的帮助。

['', 'Hello', None, None, None, None, None, None, None, None, None, None, '', '!', None, None, None, None, None, None, None, None, None, None, ' ', 'Hi', None,None, None, None, None, None, None, None, None, None, '', ',', None, None, None, None, None, None, None, None, None, None, ' ', 'I', None, None, None, None, None, None, None, None, None, None, ' ', 'am', None, None, None, None, None, None,None, None, None, None, ' ', 'debating', None, None, None, None, None, None, None, None, None, None, ' ', 'this', None, None, None, None, None, None, None, None, None, None, ' ', 'predicament', None, None, None, None, None, None, None, None, None, None, ' ', 'called', None, None, None, None, None, None, None, None, None, None, ' ', 'life', None, None, None, None, None, None, None, None, None, None, '', '.', None, None, None, None, None, None, None, None, None, None, ' ', 'Can', None, None, None, None, None, None, None, None, None, None, ' ', 'you', None, None, None, None, None, None, None, None, None, None, ' ', 'help', None, None,None, None, None, None, None, None, None, None, ' ', 'me', None, None, None, None, None, None, None, None, None, None, '', '?', None, None, None, None, None, None, None, None, None, None, '']

我想要的输出是:-

['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life', '.', 'Can', 'you', 'help', 'me', '?']

谢谢。

【问题讨论】:

  • 你认为正确的输出应该是什么?
  • 请告诉我们哪些规则定义了正确的输出应该是什么,而不是一个简单但过于冗长的示例。

标签: python regex string-split


【解决方案1】:

我推荐NLTK 的分词器。那你就不用自己操心繁琐的正则表达式了:

>>> import nltk
>>> nltk.word_tokenize("Hello! Hi, I am debating this predicament called life. Can you help me?")
['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life.', 'Can', 'you', 'help', 'me', '?']

【讨论】:

    【解决方案2】:

    re.split 在用作标记器时会迅速耗尽粉扑。最好是findall(或循环中的match),带有替代模式this|that|another|more

    >>> s = "Hello! Hi, I am debating this predicament called life. Can you help me?"
    >>> import re
    >>> re.findall(r"\w+|\S", s)
    ['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life', '.', 'Can', 'you', 'help', 'me', '?']
    >>>
    

    这将标记定义为一个或多个“单词”字符,或者不是空格的单个字符。您可能更喜欢[A-Za-z][A-Za-z0-9] 或其他东西而不是\w(允许下划线)。你甚至可能想要r"[A-Za-z]+|[0-9]+|\S"

    如果像 Sen.Mr.MissMrsMs 发生了什么事情?)对你来说很重要,你的正则表达式不应该把它们列出来,它应该只定义一个标记以. 结尾,并且您应该有一本字典或一组可能的缩写词。

    将文本拆分成句子很复杂。您可能希望查看nltk 包,而不是尝试重新发明*。

    更新:如果您需要/想要区分令牌的类型,您可以获得这样的索引或名称,而无需(可能很长)链 if/elif/elif/。 ../其他:

    >>> s = "Hello! Hi, I we 0 1 987?"
    
    >>> pattern = r"([A-Za-z]+)|([0-9]+)|(\S)"
    >>> list((m.lastindex, m.group()) for m in re.finditer(pattern, s))
    [(1, 'Hello'), (3, '!'), (1, 'Hi'), (3, ','), (1, 'I'), (1, 'we'), (2, '0'), (2,     '1'), (2, '987'), (3, '?')]
    
    >>> pattern = r"(?P<word>[A-Za-z]+)|(?P<number>[0-9]+)|(?P<other>\S)"
    >>> list((m.lastgroup, m.group()) for m in re.finditer(pattern, s))
    [('word', 'Hello'), ('other', '!'), ('word', 'Hi'), ('other', ','), ('word', 'I'), ('word', 'we'), ('number', '0'), ('number', '1'), ('number', '987'), ('other'
    , '?')]
    >>>
    

    【讨论】:

    • 在对另一个答案的评论中贬低正则表达式似乎有点讽刺,然后在这里使用它们。
    【解决方案3】:

    可能会遗漏一些东西,但我相信像下面这样的东西会起作用:

    s = "Hello! Hi, I am debating this predicament called life. Can you help me?"
    s.split(" ")
    

    这是假设您想要空格。你应该得到一些类似的东西:

    ['Hello!', 'Hi,', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life.', 'Can', 'you', 'help', 'me?']
    

    有了这个,如果你需要一个特定的部分,你可以循环通过它来获得你需要的东西。

    希望这会有所帮助....

    【讨论】:

    • 很抱歉,我没有指定我的目标输出。我已经重新编辑了我上面的问题。给您带来的不便,我们深表歉意。
    • 我没有明确你需要什么,但我应该给你足够的东西让你继续前进。 :-) 干杯!
    • 空格是默认分隔符,所以你可以调用 s.split()。
    • @GreenMatt: """空格是默认分隔符"""???并非如此,默认操作完全不同:分隔符是任何空格;前导和尾随空格被忽略。 s.split() 会比 s.split(' ') 更好,但仍然不是 OP 想要的。
    • @John Machin(和@Tim McNamara):当然,具体而言,空格不是默认分隔符。我可能应该知道比在这里说的更好。但是,正如约翰指出的那样, s.split() 将拆分 OP 呈现的字符串,但标点符号除外。至于那个标点符号,我遵循了回答者评论的精神,即他为 OP 提供了一个开始的地方。如果我想更详细一点,我会自己发布答案!
    【解决方案4】:

    你得到所有这些None 的原因是你的正则表达式中有很多括号组,由| 分隔。每次您的正则表达式找到匹配项时,它只匹配| 给出的备选方案之一。其他未使用的替代项中的括号组设置为None。并且re.split 根据定义在每次匹配时报告所有括号组的值,因此您的结果中有很多None

    您可以很容易地过滤掉它们(例如tokens = [t for t in tokens if t] 或类似的东西),但我认为split 并不是您真正想要的标记化工具。 split 仅用于丢弃空白。如果您真的想使用正则表达式来标记某些东西,这里是另一种方法的玩具示例(我什至不会尝试解开您正在使用的怪物...使用re.VERBOSE 选项来表达爱内德的...但希望这个玩具示例能给您带来想法):

    tokenpattern = re.compile(r"""
    (?P<words>\w+) # Things with just letters and underscores
    |(?P<numbers>\d+) # Things with just digits
    |(?P<other>.+?) # Anything else
    """, re.VERBOSE)
    

    (?P&lt;something&gt;... 业务可让您在下面的代码中通过名称识别您要查找的令牌类型:

    for match in tokenpattern.finditer("99 bottles of beer"):
      if match.group('words'):
        # This token is a word
        word = match.group('words')
        #...
      elif match.group('numbers'):
        number = int(match.group('numbers')):
      else:
        other = match.group('other'):
    

    请注意,这仍然是 r.e.使用一组由|分隔的括号组,因此将发生与您的代码中相同的事情:对于每个匹配项,将定义一个组,将其他组设置为None。此方法会明确检查。

    【讨论】:

      【解决方案5】:

      也许他不是这个意思,但是 John Machin 的评论“str.split 不是一个开始的地方”(作为Frank V's answer 之后的交流的一部分)有点挑战。所以...

      the_string = "Hello! Hi, I am debating this predicament called life. Can you help me?"
      tokens = the_string.split()
      punctuation = ['!', ',', '.', '?']
      output_list = []
      for token in tokens:
          if token[-1] in punctuation:
              output_list.append(token[:-1])
              output_list.append(token[-1])
          else:
              output_list.append(token)
      print output_list
      

      这似乎提供了请求的输出。

      诚然,就代码行数而言,John 的答案更简单。但是,我有几点要支持这种解决方案。

      我并不完全同意 Jamie Zawinski 的“有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 (从我读过的内容来看,他也没有。)我引用这一点的意思是,如果你不习惯正则表达式,那么它可能会很痛苦。

      此外,虽然这通常不会成为问题,但在使用timeit 测量时,上述解决方案的性能始终优于正则表达式解决方案。上述解决方案(删除了 print 语句)大约在 8.9 秒内出现; John 的正则表达式解决方案大约在 11.8 秒时出现。这涉及在运行频率为 2.4 GHz 的四核双处理器系统上进行 10 次尝试,每次迭代 100 万次。

      【讨论】:

        最近更新 更多