Custom Def #7: Search and Replace in Lines

Published June 3, 2020, 1:47 a.m.

Last time we figured out long term storage of variables and how to save input to use for later.  This time we will adjust our method for determining what part of speech we want.  We'd like to be able to use the madlib generator with multiple stories and so it is worth some effort to figure out a better way to get the part of speech that we want to replace.

Since we can control the stories used for input, we can create special keywords that aren't a part of normal speech but that our code can latch on to to recognize that we want to take some input.  What I have chosen to do is to remove the function calls from our appended line and replace the '%s' from the line with the keywords '__adjective__' and '__noun__' respectively.  The double underscore has a benefit of being easy to parse within a string as well as looking like a blank that is asking to be filled in with some part of speech.  My lines become:

lines = []
# lines.append( "\n Now for the story! \n")
# lines.append( "Once upon a time there was a %s with 2 weak arms." % getNoun())
# lines.append( "The dog was very small and frail.")
# lines.append( "One day a %s %s spoke to the dog and said..." % (getNewWordFromDict(MadLibDict,"adjective"), getNewWordFromDict(MadLibDict,"noun")))
lines.append( "One day a __adjective__ __noun__ spoke to the dog and said..." )
# lines.append( "'You are too weak.  You must eat protein %s!'" % getNoun())
# lines.append( "The dog was annoyed, but he started excercising daily.")
# lines.append( "Pretty soon, he became stronger and more muscular!")
# lines.append( "Now the dog shows off his arms on his own workout videos sponsored by P- 89 -X!" )
# lines.append( "The End!")

where we are still momentarily commenting out most of the story while we are building the code.

To make this usable we need to create a new definition.

def readandreplaceline(line):
	copyline = line

I've called my definition 'readandreplaceline' and it takes 'line' as input.  The line is assume to be a string that is part of a sentence.  I make a copy of the line that I call copyline to start.

Next I will use a python function called 'split'.  Split is an attribute of strings that converts a string to a list of words.  Split assumes that the words are separated by spaces and so all spaces are ignored.  The split function can be fed any other string or character to be used as a delimiter;  the default is space.

splitline = line.split()

Now that I have a list of words, I want to loop through the list and test each word in the list to see if the word has a double underscore in it!

for word in splitline:
		if "__" in word:
			part_of_speech = word.replace("__", "")

Now I am saying that the 'part_of_speech' that I am interested in is the word with the double underscore replaced by nothing. Now that I have the part of speech, I can use one of our definitions to get a new word.  I will choose the getNewWord function:

			newword = getNewWord(part_of_speech)

Now I am going to pull some code out of the blue on you.  As you can see above when we define the variable 'part_of_speech', we take the 'word' and we remove all instances of the "__" (double underscore).  So we could easily use that replace function on our line and replace all instances of __noun__ with some noun we choose with the getNewWord function.  But what happens if we have a line with more than one __noun__ in it?  All instances of __noun__ will be replaced in the line using that replace function.  So we have to be a little more clever here.

It turns out that strings in python are iterable and zero-indexed:  Meaning the following string:

a_string = "I am a string"

can be broken down into individual characters such that:

a_string[0]=  'I'
a_string[1]=  ' '
a_string[2]=  'a'
a_string[3]=  'm'
a_string[4]=  ' '
a_string[5]=  'a'
a_string[6]=  ' '
a_string[7]=  's'
a_string[8]=  't'
a_string[9]=  'r' 
a_string[10]= 'i'
a_string[11]= 'n'
a_string[12]= 'g'

In python a substrings can be defined as such:

a_string = "I am a string"
substring1 = a_string[:6]
print(substring1)
'I am a'
substring2 = a_string[6:]
print(substring2)
' string'

where having no number before a colon implies starting at the beginning of the string and having no number after a colon means ending at the end of the string.  The number,6, for substring1 means 'all character up to, but not including, 6.  In substring2 the number 6 means all character from (and including) index 6 and onward.

Also in python strings can be 'concatonated' with simple addition syntax:

print(substring + substring2)
'I am a string'

We can use these features in the following way.  We define an integer, j = 0 and test substrings of our line until the word we are looking for is in the substring.  Then we replace the 'word' in the substring with the 'newword' and add the remainder of the string back to the line:

			j =0
			while word not in copyline[:j]:
				j+=1
			copyline = copyline[:j].replace(word, newword)+copyline[j:]

All together it should look like:

def readandreplaceline(line):
	copyline = line
	splitline = line.split()
	for word in splitline:
		if "__" in word:
			part_of_speech = word.replace("__", "")
			newword = getNewWord(part_of_speech)
			j =0
			while word not in copyline[:j]:
				j+=1
			copyline = copyline[:j].replace(word, newword)+copyline[j:]
	return copyline

Now this function should be added to the part of the code that loops through the lines:

for line in lines:
	line = readandreplaceline(line)
	engine.say(line,line)
engine.runAndWait()

A test run yeilds:

C:\PythonProject>python madlib.py
Give me an adjective, and press 'Enter'.
burly
Give me an noun, and press 'Enter'.
cat
One day a burly cat spoke to the dog and said...

We are now very close to a madlib generator!  Next time we will put more pieces together.  Thanks for watching.  See you in the next tutorial.

skip_nextCustom Def #8: Now for the Story...
  • Custom Def #1: Import pyttsx3

  • Custom Def #2: Read a story from lines.

  • Custom Def #3: Using Multiple Definitions

  • Custom Def #4: Generalized Definitions

  • Custom Def #5: Dictionaries and Definitions

  • Custom Def #6: Python Pickles

  • Custom Def #7: Search and Replace in Lines
    (currently viewing)
  • Custom Def #8: Now for the Story...