You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
re.sub[n] doesn't seem to handle /Z replacements correctly in all cases #54537
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2019-04-27.04:41:18.584>created_at=<Date2010-11-05.14:33:47.243>labels= ['expert-regex', 'type-bug']
title="re.sub[n] doesn't seem to handle /Z replacements correctly in all cases"updated_at=<Date2019-04-27.04:41:18.583>user='https://bugs.python.org/AlexanderSchmolck'
In certain cases a zero-width /Z match that should be replaced isn't.
An example might help:
re.compile('(?m)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\\n])\Z)').subn(lambda m:next('<'+k+'>' for k,v in m.groupdict().items() if v is not None), 'foobar ')
this gives
('foobar<trailing_ws>', 1)
I would have expected
('foobar<trailing_ws><no_final_newline>', 2)
Contrast this with the following behavior:
[m.span() for m in re.compile('(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\\n])\Z)', re.M).finditer('foobar ')]
gives
[(6, 7), (7, 7)]
The matches are clearly not overlapping and the re module docs for sub say "Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl.", so I would have expected two replacements.
It's a bug caused by trying to avoid getting stuck when a zero-width match is found. Basically the fix is to advance one character after a zero-width match, but that doesn't always give the correct result.
There are a number of related issues like issue bpo-1647489 ("zero-length match confuses re.finditer()").
BreamoreBoy commented on Sep 30, 2014
BreamoreBoymannequin
Mannequin
More actions
@serhiy can you take a look at this as I recall you've been doing some regex work?
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 23 2018, 23:31:17) [MSC v.1916 32 bit (Intel)] on win32
>>> re.compile('(?m)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\n])\Z)').subn(lambdam:next('<'+k+'>'for k,v in m.groupdict().items() if v isnotNone), 'foobar ')
('foobar<trailing_ws>', 1)
Python 3.7.3rc1 (tags/v3.7.3rc1:69785b2127, Mar 12 2019, 22:37:55) [MSC v.1916 64 bit (AMD64)] on win32
>>> re.compile('(?m)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\n])\Z)').subn(lambdam:next('<'+k+'>'for k,v in m.groupdict().items() if v isnotNone), 'foobar ')
('foobar<trailing_ws><no_final_newline>', 2)
Activity
AlexanderSchmolck commentedon Nov 5, 2010
In certain cases a zero-width /Z match that should be replaced isn't.
An example might help:
re.compile('(?m)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\\n])\Z)').subn(lambda m:next('<'+k+'>' for k,v in m.groupdict().items() if v is not None), 'foobar ')
this gives
('foobar<trailing_ws>', 1)
I would have expected
('foobar<trailing_ws><no_final_newline>', 2)
Contrast this with the following behavior:
[m.span() for m in re.compile('(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\\n])\Z)', re.M).finditer('foobar ')]
gives
[(6, 7), (7, 7)]
The matches are clearly not overlapping and the re module docs for sub say "Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl.", so I would have expected two replacements.
This seems to be what perl is doing:
echo -n 'foobar ' | perl -pe 's/(?m)(?P<trailing_ws>[ \t]+\r*$)|(?P<no_final_newline>(?<=[^\\n])\Z)/<$&>/g'
gives
foobar< ><>%
mrabarnett commentedon Nov 5, 2010
It's a bug caused by trying to avoid getting stuck when a zero-width match is found. Basically the fix is to advance one character after a zero-width match, but that doesn't always give the correct result.
There are a number of related issues like issue bpo-1647489 ("zero-length match confuses re.finditer()").
BreamoreBoy commentedon Sep 30, 2014
@serhiy can you take a look at this as I recall you've been doing some regex work?
animalize commentedon Apr 27, 2019
This bug was fixed in Python 3.7, see bpo-32308.