1đź‘Ť
So you’re trying to highlight in bold an occurrence of a set of keywords. Right now this code is broken in quite a lot of ways. You’re using the re
module right now to match the keywords but you’re also breaking the keywords and the strings down into individual words, you don’t need to do both and the interaction between these two different approaches to the solving the problem are what is causing you issues.
You can use regular expressions to match multiple possible strings at the same time, that’s what they’re good for! So instead of "^keyword$"
to match just “keyword” you could use "^keyword|hello$"
to match either “keyword” or “hello”. You also use the ^
and $
characters which only match the beginning or end of the entire string, but what you probably wanted originally was to match the beginning or end of words, for this you can use \b
like this r"\b(keyword|hello)\b"
. Note that in the last example I added a r
character before the string, this stands for “raw” and turns off pythons usual handling of back slash characters which conflicts with regular expressions, it’s good practice to always use the r
before the string when the string contains a regular expression. I also used brackets to group together the words.
The regular expression sub
method allows you to substitute things matched by a regular expression with another string. It also allow you to make “back references” in the replacing string that include parts of original string that matched. The parts that it includes are called “groups” and are indicated with brackets in the original regular expression, in the example above there is only one set of brackets and these are the first so they’re indicated by the back reference \1
. The cause of the actual error message you asked about is that your replacement string contained what looked like a backref but there weren’t any groups in your regular expression.
Using that you do something like this:
keywordMatcher = re.compile(r"\b(keyword|hello)\b")
value = keywordMatcher.sub(r"<b>\1</b>", value)
Another thing that isn’t directly related to what you’re asking but is incredibly important is that you are taking source plain text strings (I assume) and making them into HTML, this gives a lot of chance for script injection vulnerabilities which if you don’t take the time to understand and avoid will allow bad guys to hack the applications you build (they can do this in an automated way, so even if you think your app will be too small for anyone to notice it can still get hacked and used for all sorts of bad things, don’t let this happen!). The basic rule is that it’s ok to convert text to HTML but you need to “escape” it first, this is very simple:
from django.utils import html
html_safe = html.escape(my_text)
All this does is convert characters like <
to <
which the browser will show as <
but won’t interpret as the beginning of a tag. So if a bad guy types <script>
into one of your forms and it gets processed by your code it will display it as <script>
and not execute it as a script.
Likewise, if you use an text in a regular expression that you don’t intend to have special regular expression characters then you must escape that too! You can do this using re.escape
:
import re
my_regexp = re.compile(r"\b%s\b" % (re.escape(my_word),))
Ok, so now we’ve got that out of the way here is a method you could use to do what you wanted:
value = "this is my super duper testing thingy"
keywords = "super|my|test"
from django.utils import html
import re
# first we must split up the keywords
keywords = keywords.split("|")
# Next we must make each keyword safe for use in a regular expression,
# this is similar to the HTML escaping we discussed above but not to
# be confused with it.
keywords = [re.escape(k) for k in keywords]
# Now we reform the keywordTags string, but this time we know each keyword is regexp-safe
keywords = "|".join(keywords)
# Finally we create a regular expression that matches *any* of the keywords
keywordMatcher = re.compile(r'\b(%s)\b' % (keywords,))
# We are going to make the value into HTML (by adding <b> tags) so must first escape it
value = html.escape(value)
# We can then apply the regular expression to the value. We use a "back reference" `\0` to say
# that each keyword found should be replace with itself wrapped in a <b> tag
value = keywordMatcher.sub(r"<b>\1</b>", value)
print value
I urge you to take the time to understand what this does, otherwise you’re just going to get yourself into a mess! It’s always easier to just cut and paste and move on but this leads to crappy broken code and worse of all means you yourself don’t improve and don’t learn. All great coders started of as beginner coders who took the time to understand things 🙂