Assuming ASCII strings:
string1 = 'Hello'
string2 = 'hello'
if string1.lower() == string2.lower():
print("The strings are the same (case insensitive)")
else:
print("The strings are NOT the same (case insensitive)")
As of Python 3.3, casefold() is a better alternative:
string1 = 'Hello'
string2 = 'hello'
if string1.casefold() == string2.casefold():
print("The strings are the same (case insensitive)")
else:
print("The strings are NOT the same (case insensitive)")
If you want a more comprehensive solution that handles more complex unicode comparisons, see other answers.
Answer from Harley Holcombe on Stack OverflowAssuming ASCII strings:
string1 = 'Hello'
string2 = 'hello'
if string1.lower() == string2.lower():
print("The strings are the same (case insensitive)")
else:
print("The strings are NOT the same (case insensitive)")
As of Python 3.3, casefold() is a better alternative:
string1 = 'Hello'
string2 = 'hello'
if string1.casefold() == string2.casefold():
print("The strings are the same (case insensitive)")
else:
print("The strings are NOT the same (case insensitive)")
If you want a more comprehensive solution that handles more complex unicode comparisons, see other answers.
Comparing strings in a case insensitive way seems trivial, but it's not. I will be using Python 3, since Python 2 is underdeveloped here.
The first thing to note is that case-removing conversions in Unicode aren't trivial. There is text for which text.lower() != text.upper().lower(), such as "ß":
>>> "ß".lower()
'ß'
>>> "ß".upper().lower()
'ss'
But let's say you wanted to caselessly compare "BUSSE" and "Buße". Heck, you probably also want to compare "BUSSE" and "BUẞE" equal - that's the newer capital form. The recommended way is to use casefold:
str.casefold()
Return a casefolded copy of the string. Casefolded strings may be used for caseless matching.
Casefolding is similar to lowercasing but more aggressive because it is intended to remove all case distinctions in a string. [...]
Do not just use lower. If casefold is not available, doing .upper().lower() helps (but only somewhat).
Then you should consider accents. If your font renderer is good, you probably think "ê" == "ê" - but it doesn't:
>>> "ê" == "ê"
False
This is because the accent on the latter is a combining character.
>>> import unicodedata
>>> [unicodedata.name(char) for char in "ê"]
['LATIN SMALL LETTER E WITH CIRCUMFLEX']
>>> [unicodedata.name(char) for char in "ê"]
['LATIN SMALL LETTER E', 'COMBINING CIRCUMFLEX ACCENT']
The simplest way to deal with this is unicodedata.normalize. You probably want to use NFKD normalization, but feel free to check the documentation. Then one does
>>> unicodedata.normalize("NFKD", "ê") == unicodedata.normalize("NFKD", "ê")
True
To finish up, here this is expressed in functions:
import unicodedata
def normalize_caseless(text):
return unicodedata.normalize("NFKD", text.casefold())
def caseless_equal(left, right):
return normalize_caseless(left) == normalize_caseless(right)
Python : How to compare strings and ignore white space and special characters - Stack Overflow
performance - Comparing strings, ignoring case, punctuation, and whitespace - Code Review Stack Exchange
How can I perform case-insensitive string comparison in Python? - Python - Data Science Dojo Discussions
Ignore punctuation and case when comparing two strings in Python - Stack Overflow
Videos
So I have a script which replaces a set of strings in a text file, but it needs to be case sensitive, is this a built in function or do I need to do some black magic
This removes punctuation and whitespace before doing the comparison:
In [32]: import string
In [33]: def compare(s1, s2):
...: remove = string.punctuation + string.whitespace
...: return s1.translate(None, remove) == s2.translate(None, remove)
In [34]: compare('Hai, this is a test', 'Hai ! this is a test')
Out[34]: True
>>> def cmp(a, b):
... return [c for c in a if c.isalpha()] == [c for c in b if c.isalpha()]
...
>>> cmp('Hai, this is a test', 'Hai ! this is a test')
True
>>> cmp('Hai, this is a test', 'Hai this is a test')
True
>>> cmp('Hai, this is a test', 'other string')
False
This creates two temporary lists, but doesn't modify the original strings in any way.
You might want to create a combined CharacterSet of punctuation and whitespace first and then apply this set using a single operation. This should speed things up. Please have a look at this code snippet:
let punctuation = CharacterSet.punctuationCharacters
let whitespace = CharacterSet.whitespaces
let unwanted = punctuation.union(whitespace)
let components = filterString.components(separatedBy: unwanted).joined(" ")
let filterStringContainsSearchText = components.contains(searchText)
Optimizing your search will require that you don't repeat costly operations on every item if they can be performed once beforehand.
It would also help to avoid the overhead of special character processing if the search text doesn't justify it (e.g if it has no spaces or special characters itself).
Finally, procedural code (for loops) are optimized more efficiently by the compiler than their "functional" equivalent (.filter / .map) so the traversal of songs should be performed in a loop instead of a .filter()
I would suggest extending the MPMediaQuery class to allow it to provide a filtered list of songs in the most optimal way possible. This has some reuse value and will keep your main logic more readable and maintainable.
On the other hand, if the MPMediaQuery goes back to storage every time, it will be more efficient to create a simple function that takes the array of songs as a parameter.
For example:
extension MPMediaQuery
{
static func songsMatching(_ searchText:String) -> [MPMediaItem]
{
// Common function to turn special characters into spaces
// will have a certain amount of overhead so we will try
// to avoid using it if possible...
func normalizedText(_ text:String) -> String
{
let chars = text.unicodeScalars.enumerated()
.map ({
CharacterSet.punctuationCharacters.contains($1)
|| CharacterSet.whitespaces.contains($1)
? " " : Character($1)
})
return String(chars)
}
// Search string should only be prepared once
// This means that the whole filtering operation needs to be
// included in the optimization.
let searchString = searchText.lowercased()
let cleanedString = normalizedText(searchString)
// based on search string content, we can forgo complex comparisons
// We only need to manage special characters if the search text
// contains any.
// If the search text doesn't contain spaces or special characters
// then removing them from the titles is unnecessary
let simpleFilter = !cleanedString.contains(" ")
// The Swift optimizer is much more efficient compiling procedural
// code (As opposed to the functional style .filter{} and such)
// This can give a 20x speed boost in some cases.
var result:[MPMediaItem] = []
for song in songs().items ?? []
{
guard let title = song.title?.lowercased()
else { continue }
if simpleFilter
{
if title.contains(searchString)
{ result.append(song) }
}
else
{
if normalizedText(title).contains(searchString)
{ result.append(song) }
}
}
return result
}
}
You can create a new string that has the properties you want, and then compare with the new string(s). This will strip everything but numbers, letters, and spaces while making all letters lowercase.
''.join([letter.lower() for letter in ' '.join(words) if letter.isalnum() or letter == ' '])
To strip everything but letters from a string you can do something like:
from string import ascii_letters
''.join([letter for letter in word if letter in ascii_letters])
You could use a regex:
import re
st="Money is the last money."
words=st.split()
beats=['money','nonsense']
for i,word in enumerate(words):
if word=='match': continue
for tgt in beats:
word=re.sub(r'\b{}\b'.format(tgt),'match',word,flags=re.I)
words[i]=word
print print ' '.join(words)
prints
match is the last match.