比较字符串是任何编程语言共同的基本任务。

当涉及到 Python 时,有几种方法可以做到这一点。最好的总是取决于用例,但我们可以将它们缩小到最适合这个目标的几个。

在本文中,我们将做到这一点。

在本教程结束时,您将学会:

  • 如何使用==!=运算符比较字符串

  • 如何使用is运算符比较两个字符串

  • 如何使用<><=>=运算符比较字符串

  • 如何比较两个字符串忽略大小写

  • 执行字符串比较时如何忽略空格

  • 如何通过模糊匹配判断两个字符串是否相似

  • 如何比较两个字符串并返回差值

  • 字符串比较不起作用时如何调试

我们走吧!

使用==!=运算符比较字符串

在 Python 中检查两个字符串是否相等的最简单方法是使用==运算符。如果你正在寻找相反的东西,那么!=就是你所需要的。而已!

==!=是布尔运算符,这意味着它们返回TrueFalse。例如,如果两个字符串匹配,==返回True,否则返回False

>>> name = 'Carl'

>>> another_name = 'Carl'

>>> name == another_name
True

>>> name != another_name
False

>>> yet_another_name = 'Josh'

>>> name == yet_another_name
False

这些运算符也区分大小写,这意味着大写字母的处理方式不同。下面的示例显示,city以大写L开头,而capital以小写l开头。结果,Python 在将它们与==进行比较时返回False

python_is_string_2.png

>>> name = 'Carl'

>>> yet_another_name = 'carl'

>>> name == yet_another_name
False

>>> name != yet_another_name
True

使用is运算符比较字符串

在 Python 中比较两个字符串是否相等的另一种方法是使用is运算符。但是,它执行的比较类型与==不同。is运算符比较 2 个字符串是否相同 instance

在 Python 和许多其他语言中,如果两个对象在内存中是同一个对象,我们就说它们是同一个实例。

>>> name = 'John Jabocs Howard'

>>> another_name = name

>>> name is another_name
True

>>> yet_another_name = 'John Jabocs Howard'

>>> name is yet_another_name
False

>>> id(name)
140142470447472

>>> id(another_name)
140142470447472

>>> id(yet_another_name)
140142459568816

下图显示了此示例如何在内存中表示。

python_is_string_1.png

如您所见,我们正在比较 identitiesnot 内容。具有相同标识的对象通常具有相同的引用,并共享相同的内存位置。使用is运算符时请记住这一点。

使用 <、>、<u003d 和 >u003d 运算符比较字符串

第三种按字母顺序比较字符串的方法。当我们需要确定两个字符串的字典顺序时,这很有用。

让我们看一个例子。

>>> name = 'maria'

>>> another_name = 'marcus'

>>> name < another_name
False

>>> name > another_name
True

>>> name <= another_name
False

>>> name >= another_name
True

为了确定顺序,Python 逐个字符地比较字符串。在我们的示例中,前三个字母是相同的mar,但下一个字母不同,marcus中的c位于maria中的i之前。

python_is_string_4.png

请务必记住,这种比较是区分大小写的。 Python 以不同的方式处理大写和小写。例如,如果我们将"maria"更改为"Maria",那么结果是不同的,因为Mm之前。

>>> name = 'Maria'

>>> another_name = 'marcus'

>>> name < another_name
True

>>> ord('M') < ord('m')
True

>>> ord('M')
77

>>> ord('m')
109

python_is_string_3.png

⚠️ 警告⚠️:避免使用这些运算符比较表示数字的字符串。比较是根据字母顺序进行的,这导致"2" < "10"被评估为False

>>> a = '2'

>>> b = '10'

>>> a < b
False

>>> a <= b
False

>>> a > b
True

>>> a >= b
True

通过忽略大小写比较两个字符串

有时我们可能需要比较两个字符串——字符串列表,甚至字符串字典——无论大小写如何。

实现这一点将取决于我们正在处理的字母表。对于 ASCII 字符串,我们可以使用str.lower()将两个字符串转换为小写,或者使用str.upper()将它们转换为大写并进行比较。

对于其他字母,例如希腊语或德语,转换为小写以使字符串不区分大小写并不总是有效。让我们看一些例子。

假设我们有一个名为'Straße'的德语字符串,意思是"Street"。您也可以不使用ß编写相同的单词,在这种情况下,单词变为Strasse。如果我们尝试将其小写或大写,看看会发生什么。

>>> a = 'Atraße'

>>> a = 'Straße'

>>> b = 'strasse'

>>> a.lower() == b.lower()
False

>>> a.lower()
'straße'

>>> b.lower()
'strasse'

发生这种情况是因为对str.lower()的简单调用不会对ß执行任何操作。它的小写形式相当于ss,但ß本身的形式和形状在小写或大写中是相同的。

忽略大小写并进行有效的不区分大小写的字符串比较的最佳方法是使用str.casefold。根据文档:

大小写折叠类似于小写,但更具侵略性,因为它旨在删除字符串中的所有大小写区别。

让我们看看当我们改用str.casefold时会发生什么。

>>> a = 'Straße'

>>> b = 'strasse'

>>> a.casefold() == b.casefold()
True

>>> a.casefold()
'strasse'

>>> b.casefold()
'strasse'

如何比较两个字符串并忽略空格

有时您可能想通过忽略空格字符来比较两个字符串。这个问题的最佳解决方案取决于空格在哪里,字符串中是否有多个空格等等。

我们将看到的第一个示例考虑到字符串之间的唯一区别是其中一个具有前导和/或尾随空格。在这种情况下,我们可以使用str.strip方法](https://miguendes.me/python-trim-string)修剪两个字符串并使用==运算符来比较它们。


>>> s1 = 'Hey, I really like this post.'

>>> s2 = '      Hey, I really like this post.   '

>>> s1.strip() == s2.strip()
True

但是,有时您的字符串中到处都是空格,其中包括多个空格。如果是这样,那么str.strip是不够的。

>>> s2 = '      Hey, I really      like this post.   '

>>> s1 = 'Hey, I really like this post.'

>>> s1.strip() == s2.strip()
False

然后,另一种方法是使用正则表达式删除重复的空格。这个方法只返回重复的字符,所以我们仍然需要去掉前导和尾随的字符。

>>> s2 = '      Hey, I really      like this post.   '

>>> s1 = 'Hey, I really like this post.'

>>> re.sub('\s+', ' ', s1.strip())
'Hey, I really like this post.'

>>> re.sub('\s+', ' ', s2.strip())
'Hey, I really like this post.'

>>> re.sub('\s+', ' ', s1.strip()) == re.sub('\s+', ' ', s2.strip())
True

或者,如果您不关心重复并想要删除所有内容,则只需将空字符串作为第二个参数传递给re.sub

>>> s2 = '      Hey, I really      like this post.   '

>>> s1 = 'Hey, I really like this post.'

>>> re.sub('\s+', '', s1.strip())
'Hey,Ireallylikethispost.'

>>> re.sub('\s+', '', s2.strip())
'Hey,Ireallylikethispost.'

>>> re.sub('\s+', '', s1.strip()) == re.sub('\s+', '', s2.strip())
True

最后也是最后一种方法是使用转换表。这个解决方案是一个有趣的正则表达式替代方案。

>>> table = str.maketrans({' ': None})

>>> table
{32: None}

>>> s1.translate(table)
'Hey,Ireallylikethispost.'

>>> s2.translate(table)
'Hey,Ireallylikethispost.'

>>> s1.translate(table) == s2.translate(table)
True

这种方法的一个好处是它不仅可以删除空格,还可以删除其他字符,例如标点符号。

>>> import string

>>> table = str.maketrans(dict.fromkeys(string.punctuation + ' '))

>>> s1.translate(table)
'HeyIreallylikethispost'

>>> s2.translate(table)
'HeyIreallylikethispost'

>>> s1.translate(table) == s2.translate(table)
True

如何比较两个字符串的相似度(模糊字符串匹配)

另一个流行的字符串比较用例是检查两个字符串是否几乎相等。在这个任务中,我们感兴趣的是知道它们有多相似,而不是比较它们的相等性。

为了更容易理解,考虑一个场景,当我们有两个字符串并且我们愿意忽略拼写错误时。不幸的是,==运算符无法做到这一点。

我们可以通过两种不同的方式解决这个问题:

  • 使用标准库中的difflib

  • 使用外部库如jellysifh

使用difflib

标准库中的difflib有一个SequenceMatcher类,该类提供了一个ratio()方法,该方法以百分比形式返回字符串相似度的度量。

假设您有两个相似的字符串,例如a = "preview"b = "previeu"。它们之间的唯一区别是最后一个字母。让我们假设这种差异对您来说足够小,并且您想忽略它。

通过使用SequenceMatcher.ratio(),我们可以获得它们相似的百分比,并使用该数字来断言两个字符串是否足够相似。

from difflib import SequenceMatcher

>>> a = "preview"

>>> b = "previeu"

>>> SequenceMatcher(a=a, b=b).ratio()
0.8571428571428571

在这个例子中,SequenceMatcher告诉我们两个字符串有 85% 相似。然后我们可以使用这个数字作为阈值并忽略差异。

>>> def is_string_similar(s1: str, s2: str, threshold: float = 0.8) -> bool
    ...: :
    ...:     return SequenceMatcher(a=s1, b=s2).ratio() > threshold
    ...:

>>> is_string_similar(s1="preview", s2="previeu")
True

>>> is_string_similar(s1="preview", s2="preview")
True

>>> is_string_similar(s1="preview", s2="previewjajdj")
False

不过有一个问题。阈值取决于字符串的长度。例如,两个非常小的字符串,比如a = "ab"b = "ac"将有 50% 的差异。

>>> SequenceMatcher(a="ab", b="ac").ratio()
0.5

因此,设置一个合适的阈值可能会很棘手。作为替代方案,我们可以尝试另一种算法,一种计算字符串中字母的换位的算法。好消息是,存在这样的算法,这就是我们接下来要看到的。

使用 Damerau-Levenshtein 距离

Damerau-Levenshtein 算法计算将一个字符串更改为另一个字符串所需的最小操作数。

换句话说,它告诉了单个字符有多少插入、删除或替换;或者我们需要执行两个相邻字符的转置,以使两个字符串相等。

在 Python 中,我们可以使用jellysifh库中的函数damerau_levenshtein_distance

让我们看看上一节中最后一个示例的 Damerau-Levenshtein 距离是多少。

>>> import jellyfish

>>> jellyfish.damerau_levenshtein_distance('ab', 'ac')
1

是1!这意味着将"ac"转换为"ab"我们需要 1 次更改。第一个例子呢?

>>> s1 = "preview"

>>> s2 = "previeu"

>>>  jellyfish.damerau_levenshtein_distance(s1, s2)
1

也是1!这很有意义,毕竟我们只需要编辑最后一个字母以使它们相等。

这样,我们可以根据变化的数量而不是比率来设置阈值。

>>> def are_strings_similar(s1: str, s2: str, threshold: int = 2) -> bool:
    ...:     return jellyfish.damerau_levenshtein_distance(s1, s2) <= threshold
    ...: 

>>> are_strings_similar("ab", "ac")
True

>>> are_strings_similar("ab", "ackiol")
False

>>> are_strings_similar("ab", "cb")
True

>>> are_strings_similar("abcf", "abcd")
True

# this ones are not that similar, but we have a default threshold of 2
>>> are_strings_similar("abcf", "acfg")
True

>>> are_strings_similar("abcf", "acyg")
False

如何比较两个字符串并返回差值

有时我们提前知道两个字符串是不同的,我们想知道是什么让它们不同。换句话说,我们想要获得他们的“差异”。

在上一节中,我们使用difflib来判断两个字符串是否足够相似。这个模块实际上比这更强大,我们可以使用它来比较字符串并显示它们的差异。

烦人的是它需要一个字符串列表,而不仅仅是一个字符串。然后它返回一个生成器,您可以使用它来加入单个字符串并打印差异。


>>> import difflib

>>> d = difflib.Differ()

>>> diff = d.compare(['my string for test'], ['my str for test'])

>>> diff
<generator object Differ.compare at 0x7f27703250b0>

>>> list(diff)
['- my string for test', '?       ---\n', '+ my str for test']

>>> print('\n'.join(diff))
- my string for test
?       ---

+ my str for test

字符串比较不起作用?

在本节中,我们将讨论字符串比较不起作用的原因以及如何修复它。根据我的经验,两个主要原因是:

  • 使用错误的运算符

  • 有尾随空格或换行符

使用is而不是==比较字符串

这在 Python 新手开发人员中很常见。使用错误的运算符很容易,尤其是在比较字符串时。

正如我们在本文中所讨论的,仅使用is运算符 if 来检查两个字符串是否相同 instances

有一个尾随空格的换行符 (\n)

这在从input函数读取字符串时非常常见。每当我们使用此功能收集信息时,用户可能会不小心添加尾随空格。

如果您将input的结果存储在变量中,您将不会轻易看到问题。

>>> a = 'hello'

>>> b = input('Enter a word: ')
Enter a word: hello 

>>> a == b
False

>>> a
'hello'

>>> b
'hello '

>>> a == b.strip()
True

这里的解决方案是从用户输入的字符串中去掉的空格,然后进行比较。您可以对您不信任的任何输入源执行此操作。

结论

在本指南中,我们看到了在 Python 中比较字符串的 8 种不同方法和两个最常见的错误。我们看到了如何利用不同的操作来执行字符串比较,以及如何使用外部库进行字符串模糊匹配。

关键要点:

  • 使用==!=运算符比较两个字符串是否相等

  • 使用is运算符检查两个字符串是否为同一个实例

  • 使用<><=>=运算符按字母顺序比较字符串

  • 使用str.casefold()比较两个字符串忽略大小写

  • 使用本机方法或正则表达式修剪字符串以在执行字符串比较时忽略空格

  • 使用difflibjellyfish来检查两个字符串是否几乎相等(模糊匹配)

  • 使用difflib比较两个字符串并返回差值

  • 字符串比较不起作用?检查尾随或前导空格,或了解您是否为工作使用了正确的运算符

今天就到这里了,希望你能学到一些新东西。下次见!

您可能喜欢的其他帖子:

  • 如何在 Python 中选择 isdigit()、isdecimal() 和 isnumeric()

  • 在 Python 中比较两个字典的最佳方法

  • 在 Python 中比较两个列表的最佳方法

  • 在 Python 中修剪字符串的 15 种简单方法

  • Pylint:如何修复“c0209:格式化可能是 f 字符串的常规字符串(考虑使用 f 字符串)”

  • 如何用 Python 实现随机字符串生成器

  • 如何在 Python 中检查字符串是否为有效 URL

  • Python F-String:73个例子帮你掌握

本帖原发于https://miguendes.me

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐