Python 正则表达式超前预测模式
Python Regular expression Lookahead overshooting pattern
我正在尝试提取 FTP LIST 中包含的数据。
我在 Python 2.7 中使用正则表达式。
test = "-rw-r--r-- 1 owner group 75148624 Jan 6 2015 somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv-rw-r--r-- 1 owner group 103576 Feb 27 2015 somename-corrected.csv"
(现在没有代码格式,因此您无需滚动即可看到它)
test = "-rw-r--r-- 1 个所有者组 75148624 2015 年 1 月 6 日 somename.csv-rw-r--r-- 1 个所有者组 223259072 2015 年 2 月 26 日 somename.csv -rw-r--r-- 1 个所有者组 4041411 2015 年 6 月 5 日 somename-adjusted.csv-rw-r--r-- 1 个所有者组 2879228 2015 年 5 月 13 日 somename.csv-rw-r-- r-- 1 个所有者组 11832668 2015 年 2 月 13 日 somename.csv-rw-r--r-- 1 个所有者组 1510522 2015 年 2 月 19 日 somename.csv-rw-r--r-- 1 个所有者组 2826664 2 月25 2015 somename.csv-rw-r--r-- 1 个所有者组 582985 2015 年 2 月 26 日 somename.csv-rw-r--r-- 1 个所有者组 212427 2015 年 2 月 26 日 somename.csv- rw-r--r-- 1 个所有者组 3015592 2015 年 2 月 27 日 somename.csv-rw-r--r-- 1 个所有者组 103576 2015 年 2 月 27 日 somename-corrected.csv"
我已经尝试了以下的各种形式
from re import compile
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})[\s]{1,20}'
'(?P<links>[0-9]{1,8})[\s]{1,20}'
'(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<size>[0-9]{1,16})[\s]{1,20}'
'(?P<month>[A-Za-z]{0,3})[\s]{1,20}'
'(?P<date>[0-9]{1,2})[\s]{1,20}'
'(?P<timeyear>[0-9:]{4,5})[\s]{1,20}'
'(?P<filename>[\s\w\.\-]+)(?=[drwx\-]{10})')
最后一行为
'(?P<filename>.+)(?=[drwx\-]{10})')
'(?P<filename>.+(?=[drwx\-]{10}))')
最初,
'(?P<filename>[\s\w\.\-]+(?=[drwx\-]{10}|$))')
所以我可以捕获最后一个条目
但无论如何,我不断得到以下输出
ftp_list_re.findall(test)
[('-rw-r--r--',
'1',
'owner',
'group',
'75148624',
'Jan',
'6',
'2015',
'somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv')]
我做错了什么?
修复了你的最后一行,文件名组不起作用。请参阅下面的固定正则表达式和演示:
(?P<permissions>[d-][rwx-]{9})[\s]{1,20}
(?P<links>[0-9]{1,8})[\s]{1,20}
(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}
(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}
(?P<size>[0-9]{1,16})[\s]{1,20}
(?P<month>[A-Za-z]{0,3})[\s]{1,20}
(?P<date>[0-9]{1,2})[\s]{1,20}
(?P<timeyear>[0-9:]{4,5})[\s]{1,20}
(?P<filename>[\w\-]+.\w+)
演示 here:
你应该先 sub-pattern 再向前看 non-greedy。此外,您的正则表达式可以像这样缩短一点:
(?P<permissions>[d-][rwx-]{9})\s{1,20}(?P<links>\d{1,8})\s{1,20}(?P<owner>[\w-]{1,16})\s{1,20}(?P<group>[\w-]{1,16})\s{1,20}(?P<size>\d{1,16})\s{1,20}(?P<month>[A-Za-z]{0,3})\s{1,20}(?P<date>\d{1,2})\s{1,20}(?P<timeyear>[\d:]{4,5})\s{1,20}(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)
或使用compile
:
from re import compile
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})\s{1,20}'
'(?P<links>\d{1,8})\s{1,20}'
'(?P<owner>[\w-]{1,16})\s{1,20}'
'(?P<group>[\w-]{1,16})\s{1,20}'
'(?P<size>\d{1,16})\s{1,20}'
'(?P<month>[A-Za-z]{0,3})\s{1,20}'
'(?P<date>\d{1,2})\s{1,20}'
'(?P<timeyear>[\d:]{4,5})\s{1,20}'
'(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)')
代码:
import re
p = re.compile(ur'(?P<permissions>[d-][rwx-]{9})\s{1,20}(?P<links>\d{1,8})\s{1,20}(?P<owner>[\w-]{1,16})\s{1,20}(?P<group>[\w-]{1,16})\s{1,20}(?P<size>[0-9]{1,16})\s{1,20}(?P<month>[A-Za-z]{0,3})\s{1,20}(?P<date>[0-9]{1,2})\s{1,20}(?P<timeyear>[\d:]{4,5})\s{1,20}(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)')
test_str = u"-rw-r--r-- 1 owner group 75148624 Jan 6 2015 somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv-rw-r--r-- 1 owner group 103576 Feb 27 2015 somename-corrected.csv"
re.findall(p, test_str)
正则表达式量词默认为"greedy",这意味着它们将尽可能"eat"。
[\s\w\.\-]+
表示至少找到一个并且尽可能多的空格、单词、点或破折号字符。前瞻防止它吃掉整个输入(实际上,正则表达式引擎将吃掉整个输入,然后根据需要开始后退),这意味着它吃掉每个文件规范行,除了最后一行(前瞻坚持必须被留下)。
添加一个?在量词(*?、+?、?? 等)之后构成量词 "lazy" 或 "reluctant"。这会将“+”的含义从 "match at least one and as many as possible" 更改为 "match at least one and no more than necessary".
因此将最后一个 + 更改为 +?应该可以解决您的问题。
问题不在于前瞻,它工作得很好,而在于它之前的最后一个子表达式。
编辑:
即使进行了此更改,您的正则表达式也不会解析最后的文件规范行。这是因为正则表达式坚持文件名后必须有权限规范。要解决此问题,我们必须允许前瞻性不匹配(但要求它匹配除最后规范之外的所有内容)。进行以下更改将解决此问题
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})[\s]{1,20}'
'(?P<links>[0-9]{1,8})[\s]{1,20}'
'(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<size>[0-9]{1,16})[\s]{1,20}'
'(?P<month>[A-Za-z]{0,3})[\s]{1,20}'
'(?P<date>[0-9]{1,2})[\s]{1,20}'
'(?P<timeyear>[0-9:]{4,5})[\s]{1,20}'
'(?P<filename>[\s\w\.\-]+?)(?=(?:(?:[drwx\-]{10})|$))')
我在这里所做的(除了使 last + lazy 之外)是让前瞻检查两种可能性 - 权限规范或字符串结尾。 ?: 是为了防止捕获这些括号(否则您最终会在匹配中得到不需要的额外数据)。
使用允许用空匹配拆分的 PyPi regex
module,您可以更简单的方式执行相同操作,而无需描述所有字段:
import regex
fields = ('permissions', 'links', 'owner', 'group', 'size', 'month', 'day', 'year', 'filename')
p = regex.compile(r'(?=[d-](?:[r-][w-][x-]){3})', regex.V1)
res = [dict(zip(fields, x.split(None, 9))) for x in p.split(test)[1:]]
我正在尝试提取 FTP LIST 中包含的数据。
我在 Python 2.7 中使用正则表达式。
test = "-rw-r--r-- 1 owner group 75148624 Jan 6 2015 somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv-rw-r--r-- 1 owner group 103576 Feb 27 2015 somename-corrected.csv"
(现在没有代码格式,因此您无需滚动即可看到它)
test = "-rw-r--r-- 1 个所有者组 75148624 2015 年 1 月 6 日 somename.csv-rw-r--r-- 1 个所有者组 223259072 2015 年 2 月 26 日 somename.csv -rw-r--r-- 1 个所有者组 4041411 2015 年 6 月 5 日 somename-adjusted.csv-rw-r--r-- 1 个所有者组 2879228 2015 年 5 月 13 日 somename.csv-rw-r-- r-- 1 个所有者组 11832668 2015 年 2 月 13 日 somename.csv-rw-r--r-- 1 个所有者组 1510522 2015 年 2 月 19 日 somename.csv-rw-r--r-- 1 个所有者组 2826664 2 月25 2015 somename.csv-rw-r--r-- 1 个所有者组 582985 2015 年 2 月 26 日 somename.csv-rw-r--r-- 1 个所有者组 212427 2015 年 2 月 26 日 somename.csv- rw-r--r-- 1 个所有者组 3015592 2015 年 2 月 27 日 somename.csv-rw-r--r-- 1 个所有者组 103576 2015 年 2 月 27 日 somename-corrected.csv"
我已经尝试了以下的各种形式
from re import compile
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})[\s]{1,20}'
'(?P<links>[0-9]{1,8})[\s]{1,20}'
'(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<size>[0-9]{1,16})[\s]{1,20}'
'(?P<month>[A-Za-z]{0,3})[\s]{1,20}'
'(?P<date>[0-9]{1,2})[\s]{1,20}'
'(?P<timeyear>[0-9:]{4,5})[\s]{1,20}'
'(?P<filename>[\s\w\.\-]+)(?=[drwx\-]{10})')
最后一行为
'(?P<filename>.+)(?=[drwx\-]{10})')
'(?P<filename>.+(?=[drwx\-]{10}))')
最初,
'(?P<filename>[\s\w\.\-]+(?=[drwx\-]{10}|$))')
所以我可以捕获最后一个条目
但无论如何,我不断得到以下输出
ftp_list_re.findall(test)
[('-rw-r--r--',
'1',
'owner',
'group',
'75148624',
'Jan',
'6',
'2015',
'somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv')]
我做错了什么?
修复了你的最后一行,文件名组不起作用。请参阅下面的固定正则表达式和演示:
(?P<permissions>[d-][rwx-]{9})[\s]{1,20}
(?P<links>[0-9]{1,8})[\s]{1,20}
(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}
(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}
(?P<size>[0-9]{1,16})[\s]{1,20}
(?P<month>[A-Za-z]{0,3})[\s]{1,20}
(?P<date>[0-9]{1,2})[\s]{1,20}
(?P<timeyear>[0-9:]{4,5})[\s]{1,20}
(?P<filename>[\w\-]+.\w+)
演示 here:
你应该先 sub-pattern 再向前看 non-greedy。此外,您的正则表达式可以像这样缩短一点:
(?P<permissions>[d-][rwx-]{9})\s{1,20}(?P<links>\d{1,8})\s{1,20}(?P<owner>[\w-]{1,16})\s{1,20}(?P<group>[\w-]{1,16})\s{1,20}(?P<size>\d{1,16})\s{1,20}(?P<month>[A-Za-z]{0,3})\s{1,20}(?P<date>\d{1,2})\s{1,20}(?P<timeyear>[\d:]{4,5})\s{1,20}(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)
或使用compile
:
from re import compile
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})\s{1,20}'
'(?P<links>\d{1,8})\s{1,20}'
'(?P<owner>[\w-]{1,16})\s{1,20}'
'(?P<group>[\w-]{1,16})\s{1,20}'
'(?P<size>\d{1,16})\s{1,20}'
'(?P<month>[A-Za-z]{0,3})\s{1,20}'
'(?P<date>\d{1,2})\s{1,20}'
'(?P<timeyear>[\d:]{4,5})\s{1,20}'
'(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)')
代码:
import re
p = re.compile(ur'(?P<permissions>[d-][rwx-]{9})\s{1,20}(?P<links>\d{1,8})\s{1,20}(?P<owner>[\w-]{1,16})\s{1,20}(?P<group>[\w-]{1,16})\s{1,20}(?P<size>[0-9]{1,16})\s{1,20}(?P<month>[A-Za-z]{0,3})\s{1,20}(?P<date>[0-9]{1,2})\s{1,20}(?P<timeyear>[\d:]{4,5})\s{1,20}(?P<filename>[\s\w.-]+?)(?=[drwx-]{10}|$)')
test_str = u"-rw-r--r-- 1 owner group 75148624 Jan 6 2015 somename.csv-rw-r--r-- 1 owner group 223259072 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 4041411 Jun 5 2015 somename-adjusted.csv-rw-r--r-- 1 owner group 2879228 May 13 2015 somename.csv-rw-r--r-- 1 owner group 11832668 Feb 13 2015 somename.csv-rw-r--r-- 1 owner group 1510522 Feb 19 2015 somename.csv-rw-r--r-- 1 owner group 2826664 Feb 25 2015 somename.csv-rw-r--r-- 1 owner group 582985 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 212427 Feb 26 2015 somename.csv-rw-r--r-- 1 owner group 3015592 Feb 27 2015 somename.csv-rw-r--r-- 1 owner group 103576 Feb 27 2015 somename-corrected.csv"
re.findall(p, test_str)
正则表达式量词默认为"greedy",这意味着它们将尽可能"eat"。
[\s\w\.\-]+
表示至少找到一个并且尽可能多的空格、单词、点或破折号字符。前瞻防止它吃掉整个输入(实际上,正则表达式引擎将吃掉整个输入,然后根据需要开始后退),这意味着它吃掉每个文件规范行,除了最后一行(前瞻坚持必须被留下)。
添加一个?在量词(*?、+?、?? 等)之后构成量词 "lazy" 或 "reluctant"。这会将“+”的含义从 "match at least one and as many as possible" 更改为 "match at least one and no more than necessary".
因此将最后一个 + 更改为 +?应该可以解决您的问题。
问题不在于前瞻,它工作得很好,而在于它之前的最后一个子表达式。
编辑:
即使进行了此更改,您的正则表达式也不会解析最后的文件规范行。这是因为正则表达式坚持文件名后必须有权限规范。要解决此问题,我们必须允许前瞻性不匹配(但要求它匹配除最后规范之外的所有内容)。进行以下更改将解决此问题
ftp_list_re = compile('(?P<permissions>[d-][rwx-]{9})[\s]{1,20}'
'(?P<links>[0-9]{1,8})[\s]{1,20}'
'(?P<owner>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<group>[0-9A-Za-z_-]{1,16})[\s]{1,20}'
'(?P<size>[0-9]{1,16})[\s]{1,20}'
'(?P<month>[A-Za-z]{0,3})[\s]{1,20}'
'(?P<date>[0-9]{1,2})[\s]{1,20}'
'(?P<timeyear>[0-9:]{4,5})[\s]{1,20}'
'(?P<filename>[\s\w\.\-]+?)(?=(?:(?:[drwx\-]{10})|$))')
我在这里所做的(除了使 last + lazy 之外)是让前瞻检查两种可能性 - 权限规范或字符串结尾。 ?: 是为了防止捕获这些括号(否则您最终会在匹配中得到不需要的额外数据)。
使用允许用空匹配拆分的 PyPi regex
module,您可以更简单的方式执行相同操作,而无需描述所有字段:
import regex
fields = ('permissions', 'links', 'owner', 'group', 'size', 'month', 'day', 'year', 'filename')
p = regex.compile(r'(?=[d-](?:[r-][w-][x-]){3})', regex.V1)
res = [dict(zip(fields, x.split(None, 9))) for x in p.split(test)[1:]]