【问题标题】:How to extract the substring between two markers?如何提取两个标记之间的子串?
【发布时间】:2023-01-11 12:37:53
【问题描述】:

假设我有一个字符串 'gfgfdAAA1234ZZZuijjk',我只想提取 '1234' 部分。

我只知道AAA之前和ZZZ之后我感兴趣的部分是1234

使用 sed 可以用字符串做这样的事情:

echo "$STRING" | sed -e "s|.*AAA\(.*\)ZZZ.*|\1|"

这将给我 1234 作为结果。

如何在 Python 中做同样的事情?

【问题讨论】:

  • 一个 python 3.8 的衬里 text[text.find(start:='AAA')+len(start):text.find('ZZZ')]

标签: python string substring


【解决方案1】:

使用正则表达式 - documentation 以供进一步参考

import re

text = 'gfgfdAAA1234ZZZuijjk'

m = re.search('AAA(.+?)ZZZ', text)
if m:
    found = m.group(1)

# found: 1234

或者:

import re

text = 'gfgfdAAA1234ZZZuijjk'

try:
    found = re.search('AAA(.+?)ZZZ', text).group(1)
except AttributeError:
    # AAA, ZZZ not found in the original string
    found = '' # apply your error handling

# found: 1234

【讨论】:

  • 如果模式在大多数情况下都匹配,则第二种解决方案更好,因为它是 Easier to ask for forgiveness than permission.
  • 索引不是从0开始吗?所以你需要使用 group(0) 而不是 group(1)?
  • @Alexander,不,group(0) 将返回完整匹配的字符串:AAA1234ZZZ,而 group(1) 将仅返回第一组匹配的字符:1234
  • @Bengt:为什么?第一个解决方案对我来说看起来很简单,而且代码行数更少。
  • 在这个表达式中?将 + 修改为非贪婪的,即。它将匹配从 1 开始的任意次数,但尽可能少,只在必要时扩展。如果没有 ?,第一组将匹配 gfgfAAA2ZZZkeAAA43ZZZonife 作为 2ZZZkeAAA43,但是如果有 ?它只会匹配 2,然后搜索多个(或将其剥离并再次搜索)将匹配 43。
【解决方案2】:
>>> s = 'gfgfdAAA1234ZZZuijjk'
>>> start = s.find('AAA') + 3
>>> end = s.find('ZZZ', start)
>>> s[start:end]
'1234'

然后,如果需要,您也可以将正则表达式与 re 模块一起使用,但这在您的情况下不是必需的。

【讨论】:

  • 这个问题似乎暗示输入文本将始终包含“AAA”和“ZZZ”。如果不是这种情况,您的答案将非常失败(我的意思是它返回完全错误的东西而不是空字符串或抛出异常;将“hello there”视为输入字符串)。
  • @user225312 re 方法不是更快吗?
  • 投票,但为了可维护性,我会使用“x = 'AAA' ; s.find(x) + len(x)”而不是“s.find('AAA') + 3”。
  • 如果在s 中找不到任何令牌,s.find 将返回-1。切片运算符 s[begin:end] 将接受它作为有效索引,并返回不需要的子字符串。
  • @confused00 查找比 re stackoverflow.com/questions/4901523/… 快得多
【解决方案3】:

正则表达式

import re

re.search(r"(?<=AAA).*?(?=ZZZ)", your_text).group(0)

如果 your_text 中没有“AAA”和“ZZZ”,上述原样将失败并显示 AttributeError

字符串方法

your_text.partition("AAA")[2].partition("ZZZ")[0]

如果your_text 中不存在“AAA”或“ZZZ”,以上代码将返回一个空字符串。

PS Python 挑战?

【讨论】:

  • 这个答案可能值得更多的赞成票。字符串方法是最健壮的方法。它不需要 try/except。
  • ...很好,虽然有限。分区不是基于正则表达式的,所以它只适用于这种情况,因为搜索字符串受固定文字限制
  • 太好了,非常感谢! - 这适用于字符串,不需要正则表达式
  • 赞成字符串方法,在这种简单的事情中不需要正则表达式,大多数语言都有一个库函数
【解决方案4】:

很惊讶没有人提到这是我的一次性脚本的快速版本:

>>> x = 'gfgfdAAA1234ZZZuijjk'
>>> x.split('AAA')[1].split('ZZZ')[0]
'1234'

【讨论】:

  • @user1810100 基本上提到了在你发布这篇文章的前一天几乎整整 5 年......
  • 向其添加 if s.find("ZZZ") &gt; s.find("AAA"): 可避免 'ZZZ` 不在字符串中时出现问题,这将返回 '1234uuijjk'
  • @tzot 的答案 (stackoverflow.com/a/4917004/358532) 与 partition 而不是 split 似乎更可靠(取决于您的需要),因为如果未找到其中一个子字符串,它会返回一个空字符串。
【解决方案5】:

你可以只使用一行代码

>>> import re

>>> re.findall(r'd{1,5}','gfgfdAAA1234ZZZuijjk')

>>> ['1234']

结果将收到列表...

【讨论】:

    【解决方案6】:
    import re
    print re.search('AAA(.*?)ZZZ', 'gfgfdAAA1234ZZZuijjk').group(1)
    

    【讨论】:

    • AttributeError: 'NoneType' object has no attribute 'groups' - 如果字符串中没有 AAA、ZZZ...
    【解决方案7】:

    您可以为此使用 re 模块:

    >>> import re
    >>> re.compile(".*AAA(.*)ZZZ.*").match("gfgfdAAA1234ZZZuijjk").groups()
    ('1234,)
    

    【讨论】:

      【解决方案8】:

      在 python 中,可以使用正则表达式 (re) 模块中的 findall 方法来提取子字符串形式的字符串。

      >>> import re
      >>> s = 'gfgfdAAA1234ZZZuijjk'
      >>> ss = re.findall('AAA(.+)ZZZ', s)
      >>> print ss
      ['1234']
      

      【讨论】:

        【解决方案9】:
        text = 'I want to find a string between two substrings'
        left = 'find a '
        right = 'between two'
        
        print(text[text.index(left)+len(left):text.index(right)])
        

        给予

        string
        

        【讨论】:

        • 如果文本不包含标记,则抛出 ValueError: substring not found 异常。那很好,
        【解决方案10】:
        >>> s = '/tmp/10508.constantstring'
        >>> s.split('/tmp/')[1].split('constantstring')[0].strip('.')
        

        【讨论】:

          【解决方案11】:

          使用 sed 可以用字符串做这样的事情:

          echo "$STRING" | sed -e "s|.*AAA(.*)ZZZ.*||"

          这将给我 1234 作为结果。

          您可以使用相同的正则表达式对 re.sub 函数执行相同的操作。

          >>> re.sub(r'.*AAA(.*)ZZZ.*', r'', 'gfgfdAAA1234ZZZuijjk')
          '1234'
          

          在基本 sed 中,捕获组由 (..) 表示,但在 python 中它由 (..) 表示。

          【讨论】:

            【解决方案12】:

            您可以在代码中使用此函数找到第一个子字符串(按字符索引)。此外,您还可以找到子字符串之后的内容。

            def FindSubString(strText, strSubString, Offset=None):
                try:
                    Start = strText.find(strSubString)
                    if Start == -1:
                        return -1 # Not Found
                    else:
                        if Offset == None:
                            Result = strText[Start+len(strSubString):]
                        elif Offset == 0:
                            return Start
                        else:
                            AfterSubString = Start+len(strSubString)
                            Result = strText[AfterSubString:AfterSubString + int(Offset)]
                        return Result
                except:
                    return -1
            
            # Example:
            
            Text = "Thanks for contributing an answer to Stack Overflow!"
            subText = "to"
            
            print("Start of first substring in a text:")
            start = FindSubString(Text, subText, 0)
            print(start); print("")
            
            print("Exact substring in a text:")
            print(Text[start:start+len(subText)]); print("")
            
            print("What is after substring "%s"?" %(subText))
            print(FindSubString(Text, subText))
            
            # Your answer:
            
            Text = "gfgfdAAA1234ZZZuijjk"
            subText1 = "AAA"
            subText2 = "ZZZ"
            
            AfterText1 = FindSubString(Text, subText1, 0) + len(subText1)
            BeforText2 = FindSubString(Text, subText2, 0) 
            
            print("
            Your answer:
            %s" %(Text[AfterText1:BeforText2]))
            

            【讨论】:

              【解决方案13】:

              使用 PyParsing

              import pyparsing as pp
              
              word = pp.Word(pp.alphanums)
              
              s = 'gfgfdAAA1234ZZZuijjk'
              rule = pp.nestedExpr('AAA', 'ZZZ')
              for match in rule.searchString(s):
                  print(match)
              

              产生:

              [['1234']]

              【讨论】:

                【解决方案14】:

                如果 text 保证包含子字符串,Python 3.8 的一个衬里:

                text[text.find(start:='AAA')+len(start):text.find('ZZZ')]
                

                【讨论】:

                • 如果文本不包含标记则不起作用。
                • fernando-wittmann 使用 text.index 的类似解决方案抛出异常,允许检测和宽恕。 stackoverflow.com/a/54975532/2719980
                【解决方案15】:

                以防万一有人和我做同样的事情。我不得不在一行中提取括号内的所有内容。例如,如果我有像“美国总统(巴拉克奥巴马)会见......”这样的一行,而我只想得到“巴拉克奥巴马”,这是解决方案:

                regex = '.*((.*?)).*'
                matches = re.search(regex, line)
                line = matches.group(1) + '
                '
                

                IE。您需要用 slash 符号来阻止括号。虽然这是一个关于比 Python 更多的正则表达式的问题。

                此外,在某些情况下,您可能会在正则表达式定义之前看到“r”符号。如果没有 r 前缀,则需要像 C 中那样使用转义字符。Here 对此有更多讨论。

                【讨论】:

                  【解决方案16】:

                  另外,您可以在波纹管函数中找到所有组合

                  s = 'Part 1. Part 2. Part 3 then more text'
                  def find_all_places(text,word):
                      word_places = []
                      i=0
                      while True:
                          word_place = text.find(word,i)
                          i+=len(word)+word_place
                          if i>=len(text):
                              break
                          if word_place<0:
                              break
                          word_places.append(word_place)
                      return word_places
                  def find_all_combination(text,start,end):
                      start_places = find_all_places(text,start)
                      end_places = find_all_places(text,end)
                      combination_list = []
                      for start_place in start_places:
                          for end_place in end_places:
                              print(start_place)
                              print(end_place)
                              if start_place>=end_place:
                                  continue
                              combination_list.append(text[start_place:end_place])
                      return combination_list
                  find_all_combination(s,"Part","Part")
                  

                  结果:

                  ['Part 1. ', 'Part 1. Part 2. ', 'Part 2. ']
                  

                  【讨论】:

                    【解决方案17】:

                    如果您想查找多次出现的情况。

                    content ="Prefix_helloworld_Suffix_stuff_Prefix_42_Suffix_andsoon"
                    strings = []
                    for c in content.split('Prefix_'):
                        spos = c.find('_Suffix')
                        if spos!=-1:
                            strings.append( c[:spos])
                    print( strings )
                    

                    或者更快:

                    strings = [ c[:c.find('_Suffix')] for c in content.split('Prefix_') if c.find('_Suffix')!=-1 ]
                    

                    【讨论】:

                      【解决方案18】:

                      这是一个没有正则表达式的解决方案,它还考虑了第一个子字符串包含第二个子字符串的情况。如果第二个标记在第一个标记之后,此函数将仅查找子字符串。

                      def find_substring(string, start, end):
                          len_until_end_of_first_match = string.find(start) + len(start)
                          after_start = string[len_until_end_of_first_match:]
                          return string[string.find(start) + len(start):len_until_end_of_first_match + after_start.find(end)]
                      

                      【讨论】:

                        【解决方案19】:

                        另一种方法是使用列表(假设您要查找的子字符串仅由数字组成):

                        string = 'gfgfdAAA1234ZZZuijjk'
                        numbersList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
                        output = []
                        
                        for char in string:
                            if char in numbersList: output.append(char)
                        
                        print(f"output: {''.join(output)}")
                        ### output: 1234
                        

                        【讨论】:

                          【解决方案20】:

                          打字稿。获取两个其他字符串之间的字符串。

                          搜索前缀和后缀之间的最短字符串

                          前缀 - 字符串/字符串数组/空(意味着从头开始搜索)。

                          后缀 - 字符串/字符串数组/空(意味着搜索直到结束)。

                          public getStringInBetween(str: string, prefixes: string | string[] | null,
                                                    postfixes: string | string[] | null): string {
                          
                              if (typeof prefixes === 'string') {
                                  prefixes = [prefixes];
                              }
                          
                              if (typeof postfixes === 'string') {
                                  postfixes = [postfixes];
                              }
                          
                              if (!str || str.length < 1) {
                                  throw new Error(str + ' should contain ' + prefixes);
                              }
                          
                              let start = prefixes === null ? { pos: 0, sub: '' } : this.indexOf(str, prefixes);
                              const end = postfixes === null ? { pos: str.length, sub: '' } : this.indexOf(str, postfixes, start.pos + start.sub.length);
                          
                              let value = str.substring(start.pos + start.sub.length, end.pos);
                              if (!value || value.length < 1) {
                                  throw new Error(str + ' should contain string in between ' + prefixes + ' and ' + postfixes);
                              }
                          
                              while (true) {
                                  try {
                                      start = this.indexOf(value, prefixes);
                                  } catch (e) {
                                      break;
                                  }
                                  value = value.substring(start.pos + start.sub.length);
                                  if (!value || value.length < 1) {
                                      throw new Error(str + ' should contain string in between ' + prefixes + ' and ' + postfixes);
                                  }
                              }
                          
                              return value;
                          }
                          

                          【讨论】:

                            【解决方案21】:

                            如果没有匹配项,则返回其他字符串的衬垫。 编辑:改进版本使用next 功能,如果需要,用其他东西替换"not-found"

                            import re
                            res = next( (m.group(1) for m in [re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk" ),] if m), "not-found" )
                            

                            我的另一种方法,不太理想,第二次使用正则表达式,仍然没有找到更短的方法:

                            import re
                            res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
                            

                            【讨论】:

                              猜你喜欢
                              • 2011-06-07
                              • 1970-01-01
                              • 2020-04-21
                              • 1970-01-01
                              • 1970-01-01
                              • 2021-01-17
                              相关资源
                              最近更新 更多