I am reading an article about Python regular expressions Regular Expression HOWTO, and I feel a bit confused by the example under the topic of lookahead. The example is about to find filenames with some conditions. A filename consists of a base name and an extension, separated by a dot .. For example, a filename can be news.rc and news is the base name and rc is the filename's extension. We want to select filenames where the extension is not bat. A appropriate RE from the article is .*[.](?!bat$)[^.]*$. The author says that "[^.]* makes sure that the pattern works when there are multiple dots in the filename.", which makes me feel confused. I think that [^.] means to match anything but '.' and [^.]* means to do this action over and over again. For example, the regex engine applies .*[.](?!bat$)[^.]*$ to 'autoexec.bat.ty'. .* match 'autoexec' and [.] match '.'. Since 'bat.ty' is not 'bat', (?!bat$) won't fail and start to match [^.]*. What I expect is that it will stop at the second '.' of 'autoexec.bat.ty' and obtain match 'autoexec.bat'. However, the match from python is 'autoexec.bat.ty'. p = re.compile('.*[.](?!bat$)[^.]*$') m = p.match("autoexec.bat.ty") m <re.Match object; span=(0, 15), match='autoexec.bat.ty'> Suppose we in a simple case that to use RE [^.]* to match 'AB.CD', it will return 'AB'. I don't understand what the difference is between these two situations. print(re.match('[^.]*', 'AB.CD')) <re.Match object; span=(0, 2), match='AB'> Continue reading...