在字符串中查找子字符串,但仅当整个单词?

Posted

技术标签:

【中文标题】在字符串中查找子字符串,但仅当整个单词?【英文标题】:Find substring in string but only if whole words? 【发布时间】:2011-05-08 11:38:29 【问题描述】:

在 Python 中查找另一个字符串中的字符串的优雅方法是什么,但前提是子字符串在整个单词中,而不是单词的一部分?

也许一个例子可以说明我的意思:

string1 = "ADDLESHAW GODDARD"
string2 = "ADDLESHAW GODDARD LLP"
assert string_found(string1, string2)  # this is True
string1 = "ADVANCE"
string2 = "ADVANCED BUSINESS EQUIPMENT LTD"
assert not string_found(string1, string2)  # this should be False

我怎样才能最好地编写一个名为 string_found 的函数来满足我的需要?我想也许我可以用这样的东西来捏造它:

def string_found(string1, string2):
   if string2.find(string1 + " "):
      return True
   return False

但这感觉不是很优雅,而且如果它在 string2 的末尾,也不会匹配 string1。也许我需要一个正则表达式? (argh 正则表达式恐惧)

【问题讨论】:

【参考方案1】:

您可以使用regular expressions和字边界特殊字符\b(由我突出显示):

匹配空字符串,但只匹配单词的开头或结尾。单词被定义为一系列字母数字或下划线字符,因此单词的结尾由空格或非字母数字、非下划线字符表示。请注意,\b 定义为\w\W 之间的边界,因此被视为字母数字的精确字符集取决于UNICODELOCALE 标志的值。在字符范围内,\b 表示退格字符,以与 Python 的字符串文字兼容。

def string_found(string1, string2):
   if re.search(r"\b" + re.escape(string1) + r"\b", string2):
      return True
   return False

Demo


如果单词边界对您来说只是空格,您也可以在字符串中预先添加和附加空格:

def string_found(string1, string2):
   string1 = " " + string1.strip() + " "
   string2 = " " + string2.strip() + " "
   return string2.find(string1)

【讨论】:

对理论建议投了赞成票。您的脚本 OTOH 将不起作用。 '\b' 是退格 ('\x08') 字符的转义序列。我建议将r'\b%s\b' % (re.escape(string1)) 作为re.search() 的第一个参数。事实上,整个函数可以简化为return re.search(r'\b%s\b' % (re.escape(string1)), string2) is not None @Walter:不确定\b。据说:在一个字符范围内\b代表退格字符,...至少对我有用。但是,是的,字符串替换也很好:) 当 \b 在字符范围 [a-z0-9\b] 内时...? \b 应该可以工作,并且在我所做的非常简短的测试中做到了 @Walter:您的r'\b%s\b' % (re.escape(string1)) 与Felix 的r"\b" + re.escape(string1) + r"\b" 具有相同的结果;旁注:你的额外括号没有用,因为它们不代表长度为一的元组。虽然if ...: return True; else: return False 也是我的一大烦恼。 在我的用例中,有很多情况下 string_found() 返回 False。为了使 False 情况更快,在运行昂贵的 re.search() 之前,在 string2 中添加对 string1 的测试: def string_found(string1, string2): if string1 in string2 and if re.search(r"\b" + re .escape(string1) + r"\b", string2): ...【参考方案2】:

我相信最简单和最 Pythonic 的方法是将字符串分解为单个单词并扫描匹配:


    string = "My Name Is Josh"
    substring = "Name"

    for word in string.split():
        if substring == word:
            print("Match Found")

为了奖励,这里有一个单线:

any([substring == word for word in string.split()])

【讨论】:

我喜欢这个,因为它最接近 unix 中的grep -w 喜欢这种 python 方法。有效,正是我想要的! 真正的一行是if word in string.split()【参考方案3】:

这是一种无需正则表达式(根据要求)的方法,假设您希望任何空格用作单词分隔符。

import string

def find_substring(needle, haystack):
    index = haystack.find(needle)
    if index == -1:
        return False
    if index != 0 and haystack[index-1] not in string.whitespace:
        return False
    L = index + len(needle)
    if L < len(haystack) and haystack[L] not in string.whitespace:
        return False
    return True

这里有一些 demo code(键盘是个好主意:感谢 Felix Kling 提醒我)

【讨论】:

只要确保“保存”键盘粘贴,它们就不会过期。 (我在键盘注释中包含了一个链接,仅供我以后自己做笔记。) 对于那些想要确保标点符号和空格被认为是有效的整个单词分隔符的人...修改上面的代码如下:not in (string.whitespace + string.punctuation)还要注意这个函数是两倍以上与建议的 RegEx 替代方案一样高效......如果你经常使用它,这个功能就是要走的路。 很棒的解决方案。对于 5000k 行,我有 1e-05 而使用正则表达式 0.0018。快 180 倍。 代码不太正确。如果子字符串有 两次 或多次出现,第一个 not 是一个完整的单词,而第二个是一个完整的单词,代码将只考虑第一个并返回错误的。必须查看所有匹配项,如果没有一个符合条件则返回 false。 添加了我的答案:***.com/a/41391098/212942,它建立在您的代码之上。【参考方案4】:

我正在建设this answer。

上面代码的问题是,当haystack中有多个needle出现时,它会返回false,第二个满足搜索条件但不满足第一个。

这是我的版本:

def find_substring(needle, haystack):
  search_start = 0
  while (search_start < len(haystack)):
    index = haystack.find(needle, search_start)
    if index == -1:
      return False
    is_prefix_whitespace = (index == 0 or haystack[index-1] in string.whitespace)
    search_start = index + len(needle)
    is_suffix_whitespace = (search_start == len(haystack) or haystack[search_start] in string.whitespace)
    if (is_prefix_whitespace and is_suffix_whitespace):
      return True
  return False

希望有帮助!

【讨论】:

这是完美的。【参考方案5】:

使用re 或正则表达式模块完成此任务的一种方法是:

import re

string1 = "pizza pony"
string2 = "who knows what a pizza pony is?"

search_result = re.search(r'\b' + string1 + '\W', string2)

print(search_result.group())

【讨论】:

对此答案的站点注释。正则表达式比“find()”慢得多,而且文本很大,应该考虑使用 str.find()【参考方案6】:
def string_found(string1,string2):
    if string2 in string1 and string2[string2.index(string1)-1]==" 
    " and string2[string2.index(string1)+len(string1)]==" ":return True
    elif string2.index(string1)+len(string1)==len(string2) and 
    string2[string2.index(string1)-1]==" ":return True
    else:return False

【讨论】:

它做了他们想做的事?不知道你还想要什么 我们试图在我们的答案中提供详细信息,以便 OP 以及任何登陆此页面并提出类似问题并可能具有不同理解水平的人都能理解它们。不过,欢迎使用 Stack,您可能会觉得这很有帮助 --> ***.com/help/how-to-answer【参考方案7】:

请原谅我的 REGEX 研究员,但更简单的答案是:

text = "this is the esquisidiest piece never ever writen"
word = "is"
" 0 ".format(text).lower().count(" 0 ".format(word).lower())

这里的技巧是在要搜索的“文本”和“单词”周围添加 2 个空格,这样您就可以保证只返回整个单词的计数,并且您不会遇到结尾和开头的问题搜索的“文本”。

【讨论】:

如果,例如,单词 word one 正在寻找的周围或两侧有一个非字母可选字符,会发生什么?例如: text = "这是从未写过的最精致的作品。" word = "writen" 。注意最后的点。【参考方案8】:

感谢@Chris Larson 的评论,我测试并更新如下:

import re

string1 = "massage"
string2 = "muscle massage gun"
try:
    re.search(r'\b' + string1 + r'\W', string2).group()
    print("Found word")
except AttributeError as ae:
    print("Not found")

【讨论】:

以上是关于在字符串中查找子字符串,但仅当整个单词?的主要内容,如果未能解决你的问题,请参考以下文章

字符串相关(排序, 单词查找树, 子字符串查找)

如何确保 replaceAll 将替换整个单词而不是子字符串

Elasticsearch:查找子字符串匹配

如何在包含子字符串的数据框中查找所有行?

用后缀树查找两个单词中最长的子串

在字符串列表中查找相等的子字符串