【问题标题】:Slice a string after a certain phrase?在某个短语之后切一个字符串?
【发布时间】:2009-10-27 22:03:44
【问题描述】:

我有一批字符串需要删减。它们基本上是一个描述符,后跟代码。我只想保留描述符。

'a descriptor dps 23 fd'
'another 23 fd'
'and another fd'
'and one without a code'

上面的代码是dps23fd。它们可以按任何顺序出现,彼此无关并且可能根本不存在(如最后一种情况)。

代码列表是固定的(或至少可以预测),因此假设代码从未在合法描述符中使用,我如何在代码的第一个实例之后剥离所有内容。

我正在使用 Python。

【问题讨论】:

  • 在您的示例中,代码是什么,输出应该是什么样的?

标签: python


【解决方案1】:

正如@THC4K 在评论中指出的那样,简短的回答:

string.split(pattern, 1)[0]

其中string 是您的原始字符串,pattern 是您的“中断”模式,1 表示拆分不超过 1 次,[0] 表示获取拆分返回的第一个元素。

在行动:

>>> s = "a descriptor 23 fd"
>>> s.split("23", 1)[0]
'a descriptor '
>>> s.split("fdasfdsafdsa", 1)[0]
'a descriptor 23 fd'

这是表达我之前写的内容的一种更简短的方式,无论如何我都会保留在这里。

如果您需要删除多个模式,这是 reduce 内置函数的理想选择:

>>> string = "a descriptor dps foo 23 bar fd quux"
>>> patterns = ["dps", "23", "fd"]
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, string)
'a descriptor '
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, "uiopuiopuiopuipouiop")
'uiopuiopuiopuipouiop'

这基本上是说:对于patterns中的每个pat:取string并重复应用string.split(pat, 1)[0](如上所述),每次都对先前返回的值的结果进行操作。如您所见,如果字符串中没有任何模式,则仍然返回原始字符串。


最简单的答案是列表/字符串切片结合string.find

>>> s = "a descriptor 23 fd"
>>> s[:s.find("fd")]
'a descriptor 23 '
>>> s[:s.find("23")]  
'a descriptor '
>>> s[:s.find("gggfdf")] # <-- look out! last character got cut off
'a descriptor 23 f'

更好的方法(避免在s.find 返回 -1 时切断缺失模式中的最后一个字符)可能是包装在一个简单的函数中:

>>> def cutoff(string, pattern):
...     idx = string.find(pattern)
...     return string[:idx if idx != -1 else len(string)]
... 
>>> cutoff(s, "23")
'a descriptor '
>>> cutoff(s, "asdfdsafdsa")
'a descriptor 23 fd'

[:s.find(x)] 语法意味着从索引 0 到冒号右侧的部分字符串;在这种情况下,RHS 是s.find 的结果,它返回您传递的字符串的索引。

【讨论】:

  • the_string.split(pattern,1)[0] 和我想的一样。
  • 用分割标记,如果字符串中没有代码会发生什么(虽然存在可能性)?两者都没有更好的方法来一次检查多个代码。这两个示例似乎一次只处理一个。
【解决方案2】:

你似乎在描述这样的事情:

def get_descriptor(text):
    codes = ('12', 'dps', '23')
    for c in codes:
        try:
            return text[:text.index(c)].rstrip()
        except ValueError:
            continue

    raise ValueError("No descriptor found in `%s'" % (text))

例如,

>>> get_descriptor('a descriptor dps 23 fd')
'a descriptor'

【讨论】:

    【解决方案3】:
    codes = ('12', 'dps', '23')
    
    def get_descriptor(text):
        words = text.split()
        for c in codes:
            if c in words:
                i = words.index(c)
                return " ".join(words[:i])
        raise ValueError("No code found in `%s'" % (text))
    

    【讨论】:

      【解决方案4】:

      我可能会使用正则表达式来做到这一点:

      >>> import re
      >>> descriptors = ('foo x', 'foo y', 'bar $', 'baz', 'bat')
      >>> data = ['foo x 123', 'foo y 123', 'bar $123', 'baz 123', 'bat 123', 'nothing']
      >>> p = re.compile("(" + "|".join(map(re.escape, descriptors)) + ")")
      >>> for s in data:
              m = re.match(p, s)
              if m: print m.groups()[0]
      foo x
      foo y
      bar $
      baz
      bat
      

      我并不完全清楚您是否希望提取的内容包含描述符之前的文本,或者您是否希望每行文本都以描述符开头;以上涉及后者。对于前者,只需稍微改变模式,使其捕获描述符第一次出现之前的所有字符:

      >>> p = re.compile("(.*(" + "|".join(map(re.escape, descriptors)) + "))")
      

      【讨论】:

        【解决方案5】:

        这是一个适用于所有代码的答案,而不是强迫您为每个代码调用函数,并且比上面的一些答案更简单。它也适用于您的所有示例。

        strings = ('a descriptor dps 23 fd', 'another 23 fd', 'and another fd',
                          'and one without a code')
        codes = ('dps', '23', 'fd')
        
        def strip(s):
            try:
                return s[:min(s.find(c) for c in codes if c in s)]
            except ValueError:
                return s
        
        print map(strip, strings)
        

        输出:

        ['a descriptor ', 'another ', 'and another ', 'and one without a code']
        

        我相信这符合您的所有标准。

        编辑:我很快意识到如果您不希望出现异常,您可以删除 try catch:

        def strip(s):
            if not any(c in s for c in codes):
                return s
            return s[:min(s.find(c) for c in codes if c in s)]
        

        【讨论】:

          【解决方案6】:
              def crop_string(string, pattern):
                  del_items = []
                  for indx, val in enumerate(pattern):
                      a = string.split(val, 1)
                      del_items.append(a[indx])
          
                  for del_item in del_items:
                      string = string.replace(del_item, "")
                  return string
          

          示例:

          我想裁剪字符串并只从中取出数组..

          strin = "crop the array [1,2,3,4,5]
          pattern["[","]"]
          

          用法:

          a = crop_string(strin ,pattern )
          print a 
          
          # --- Prints "[1,2,3,4,5]"
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-02-14
            • 1970-01-01
            • 1970-01-01
            • 2022-06-18
            • 2011-06-02
            • 2020-11-23
            • 1970-01-01
            相关资源
            最近更新 更多