技术: Python中正则表达式的支持

regular expression operations in python 2.7

python支持正则表达式是借助外部的re库, 该库在安装python的时候就已经绑定了.
但是注意, 这个库是PERE的阉割版, 仅仅支持核心部分正则语法(就常用而言,足够了).

引子

本文没有像官方文档写remodule一样去全篇幅的介绍相关的库, 而是从python开发者的角度去使用正则表达式, 当然你也可以去看文档, 其网址如下:

https://docs.python.org/2/library/re.html

正文

Raw Python strings

When writing regular expression in Python, it is recommended that you use raw strings instead of regular Python strings. Raw strings begin with a special prefix (r) and signal Python not to interpret backslashes and special metacharacters in the string, allowing you to pass them through directly to the regular expression engine.

This means that a pattern like “\n\w” will not be interpreted and can be written as r”\n\w” instead of “\n\w” as in other languages, which is much easier to read.

Matching a string

The re package has a number of top level methods, and to test whether a regular expression matches a specific string in Python, you can use re.search(). This method either returns None if the pattern doesn’t match, or a re.MatchObject with additional information about which part of the string the match was found.

Note that this method stops after the first match, so this is best suited for testing a regular expression more than extracting data.

1
matchObject = re.search(pattern, input_str, flags=0)

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import re
# Lets use a regular expression to match a date string. Ignore
# the output since we are just testing if the regex matches.
regex = r"([a-zA-Z]+) (\d+)"
if re.search(regex, "June 24"):
# Indeed, the expression "([a-zA-Z]+) (\d+)" matches the date string

# If we want, we can use the MatchObject's start() and end() methods
# to retrieve where the pattern matches in the input string, and the
# group() method to get all the matches and captured groups.
match = re.search(regex, "June 24")

# This will print [0, 7), since it matches at the beginning and end of the
# string
print "Match at index %s, %s" % (match.start(), match.end())

# The groups contain the matched values. In particular:
# match.group(0) always returns the fully matched string
# match.group(1) match.group(2), ... will return the capture
# groups in order from left to right in the input string
# match.group() is equivalent to match.group(0)

# So this will print "June 24"
print "Full match: %s" % (match.group(0))
# So this will print "June"
print "Month: %s" % (match.group(1))
# So this will print "24"
print "Day: %s" % (match.group(2))
else:
# If re.search() does not match, then None is returned
print "The regex pattern does not match. :("

Capturing groups

Unlike the re.search() method above, we can use re.findall() to perform a global search over the whole input string. If there are capture groups in the pattern, then it will return a list of all the captured data, but otherwise, it will just return a list of the matches themselves, or an empty list if no matches are found.

If you need additional context for each match, you can use re.finditer() which instead returns an iterator of re.MatchObjects to walk through. Both methods take the same parameters.

如下:

1
matchList = re.findall(pattern, input_str, flags=0)

以及

1
matchList = re.finditer(pattern, input_str, flags=0)

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import re
# Lets use a regular expression to match a few date strings.
regex = r"[a-zA-Z]+ \d+"
matches = re.findall(regex, "June 24, August 9, Dec 12")
for match in matches:
# This will print:
# June 24
# August 9
# Dec 12
print "Full match: %s" % (match)

# To capture the specific months of each date we can use the following pattern
regex = r"([a-zA-Z]+) \d+"
matches = re.findall(regex, "June 24, August 9, Dec 12")
for match in matches:
# This will now print:
# June
# August
# Dec
print "Match month: %s" % (match)

# If we need the exact positions of each match
regex = r"([a-zA-Z]+) \d+"
matches = re.finditer(regex, "June 24, August 9, Dec 12")
for match in matches:
# This will now print:
# 0 7
# 9 17
# 19 25
# which corresponds with the start and end of each match in the input string
print "Match at index: %s, %s" % (match.start(), match.end())

Finding and replacing strings

Another common task is to find and replace a part of a string using regular expressions, for example, to replace all instances of an old email domain, or to swap the order of some text. You can do this in Python with the re.sub() method.

The optional count argument is the exact number of replacements to make in the input string, and if this is value is less than or equal to zero, then every match in the string is replaced.

1
replacedString = re.sub(pattern, replacement_pattern, input_str, count, flags=0)

example:

1
2
3
4
5
6
7
8
9
10
import re
# Lets try and reverse the order of the day and month in a date
# string. Notice how the replacement string also contains metacharacters
# (the back references to the captured groups) so we use a raw
# string for that as well.
regex = r"([a-zA-Z]+) (\d+)"

# This will reorder the string and print:
# 24 of June, 9 of August, 12 of Dec
print re.sub(regex, r"\2 of \1", "June 24, August 9, Dec 12")

re Flags

In the Python regular expression methods above, you will notice that each of them also take an optional flags argument. Most of the available flags are a convenience and can be written into the into the regular expression itself directly, but some can be useful in certain cases.

  • re.IGNORECASE makes the pattern case insensitive so that it matches strings of different capitalizations
  • re.MULTILINE is necessary if your input string has newline characters (\n) and allows the start and end metacharacter (^ and $ respectively) to match at the beginning and end of each line instead of at the beginning and end of the whole input string
  • re.DOTALL allows the dot (.) metacharacter match all characters, including the newline character (\n)

Compiling a pattern for performance

In Python, creating a new regular expression pattern to match many strings can be slow, so it is recommended that you compile them if you need to be testing or extracting information from many input strings using the same expression. This method returns a re.RegexObject.

1
regexObject = re.compile(pattern, flags=0)

The returned object has exactly the same methods as above, except that they take the input string and no longer require the pattern or flags for each call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
# Lets create a pattern and extract some information with it
regex = re.compile(r"(\w+) World")
result = regex.search("Hello World is the easiest")
if result:
# This will print:
# 0 11
# for the start and end of the match
print result.start(), result.end()

# This will print:
# Hello
# Bonjour
# for each of the captured groups that matched
for result in regex.findall("Hello World, Bonjour World"):
print result

# This will substitute "World" with "Earth" and print:
# Hello Earth
print regex.sub(r"\1 Earth", "Hello World")

一个方便的工具, 可以在线测试您写的相关语句:
https://regex101.com/#python

尾巴

本文已经做了相当多的介绍和演示, 在python2.7中具体使用了regex.
如果上面的介绍还是不够, 下面也给出了更多的参考(可以参考官方文档):

  1. 《Python Document》
  2. 《Python Compatible Regex Tester》
文章目录
  1. 1. 引子
  2. 2. 正文
    1. 2.1. Raw Python strings
    2. 2.2. Matching a string
    3. 2.3. Capturing groups
    4. 2.4. Finding and replacing strings
    5. 2.5. re Flags
    6. 2.6. Compiling a pattern for performance
  3. 3. 尾巴
|