Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #197877
| From | Veek M <veekjunk@foobar.com> |
|---|---|
| Newsgroups | comp.lang.python |
| Subject | Re: re.sub(r'((?<=\A)|(?<=,))(?=,|\Z)', 'NA', ',1,,,two,3,,,') how does it work? |
| Date | 2026-08-11 09:21 +0000 |
| Organization | A noiseless patient Spider |
| Message-ID | <115epji$3s01a$1@dont-email.me> (permalink) |
| References | <1158h6s$1ts3e$1@dont-email.me> <advances-20260809120133@ram.dialup.fu-berlin.de> |
On 9 Aug 2026 11:24:12 GMT, Stefan Ram wrote:
> Veek M <veekjunk@foobar.com> wrote or quoted:
>>If it's matching the empty string '' then why don't we get NA,NA1 etc
>>for ,1
>
> | How Regex Engines Handle Lookarounds and Empty Matches |
> | In regular expressions, lookarounds - including lookaheads (?= . . . )
> | and lookbehinds (?<= . . . ) - are /zero-width assertions/. They act |
> as /conditional checks/. They inspect the string to see if a pattern |
> exists, but they do not "consume" (wipe out or move past) any charac- |
> ters.
> |
> | Understanding how a regex engine processes these assertions requires |
> looking at two distinct phases: /evaluation within a single match |
> attempt/, and /progression through the string/.
> |
> | 1. The Fixed-Position Rule (Chaining Lookarounds)
> |
> | When a regex engine tests a pattern at a specific position in a |
> string, the internal pointer stays completely still until the entire |
> pattern either succeeds or fails.
> |
> | If you chain multiple lookarounds together, they all evaluate | from
> the exact same spatial slot, one after the other.
> |
> | Example: Chaining Lookaheads |
> | Consider the pattern "(?=x)(?=x)" applied to the string "xy".
> |
> | 1. The engine starts at Position 0 (the empty space right before |
> "x").
> |
> | 2. First "(?=x)" check: Looks ahead from Position 0, sees "x", and |
> returns "True". The pointer does not move.
> |
> | 3. Second "(?=x)" check: Looks ahead from the same Position 0, sees |
> "x" again, and returns "True". The pointer does not move.
> |
> | Both assertions pass at Position 0. The engine declares a match of |
> length zero at that position. If one's using a substitution function |
> like Python's "re.sub(r'(?=x)(?=x)?, 'z', ?xy')", it inserts "z" at |
> that empty slot, resulting in "zxy".
> |
> | 2. The Forced-Advance Rule (Preventing Infinite Loops)
> |
> | If lookarounds don't move the pointer, why doesn't a global "search |
> and replace" loop forever, inserting infinitely many "z" at Position |
> 0?
> |
> | Regex engines implement a universal safety mechanism: /The Forced-
> | Advance Rule/.
> |
> | Standard Match If a regex matches actual characters (like "[^,]+"),
> | the pointer naturally moves past those characters for the next search
> | cycle.
> |
> | Zero-Width Match If a regex matches an empty string (zero width),
> | the engine applies the safety rule. After completing the match and |
> performing any substitution, /it forces the pointer forward by exactly |
> one position/ before attempting the next match.
> |
> | Edge Cases & Common Logic Traps |
> | Edge Case A: Contradictory Lookarounds |
> | What happens if you look ahead for two different characters at the |
> same time?
> |
> | (?=x)(?=y)
> |
> | Result This will never match anything.
> |
> | Why At any given empty slot in a string, the single character |
> immediately following that slot cannot be both "x" and "y" simul-
> | taneously.
> |
> | Edge Case B: Conflicting Lookahead and Lookbehind |
> | You can combine lookaheads and lookbehinds to pinpoint exact |
> boundaries.
> |
> | (?<=x)(?=y)
> |
> | Result This successfully matches the empty space between an "x" and
> | a "y" (such as inside the string "xy").
> |
> | Why At Position 1 (between "x" and "y"), the lookbehind looks back-
> | ward and sees "x" ("True"), while the lookahead looks forward and sees
> | "y" ("True").
> |
> | Edge Case C: Quantifiers on Zero-Width Matches |
> | What happens if you tell a lookaround to repeat using a quantifier |
> like "*" (zero or more times)?
> |
> | (?=x)*
> |
> | Result This creates an immediate zero-width match at every single |
> position in the string, even where "x" does not exist.
> |
> | Why At a position where "x" is absent, "(?=x)" fails. However,
> | because "*" means "zero or more times," matching it zero times is |
> considered a total success. The engine registers an empty match,
> | forces the pointer forward by one via the safety rule, and repeats |
> this at every slot.
>
> Lines marked with "| " come from my editing, where I start by writing
> prompts for the chatbot and then edit the generated texts and format
> them for USENET.
Thank you for writing such a clear and detailed response to my question. I
am still pondering much of what you said and trying to fit it in with what
I've read and what others have said. Thanks man!
Back to comp.lang.python | Previous | Next — Previous in thread | Find similar | Unroll thread
re.sub(r'((?<=\A)|(?<=,))(?=,|\Z)', 'NA', ',1,,,two,3,,,') how does it work? Veek M <veekjunk@foobar.com> - 2026-08-09 00:21 +0000
Re: re.sub(r'((?<=\A)|(?<=,))(?=,|\Z)', 'NA', ',1,,,two,3,,,') how does it work? Jon Ribbens <jon+usenet@unequivocal.eu> - 2026-08-09 01:26 +0000
Re: re.sub(r'((?<=\A)|(?<=,))(?=,|\Z)', 'NA', ',1,,,two,3,,,') how does it work? ram@zedat.fu-berlin.de (Stefan Ram) - 2026-08-09 11:24 +0000
Re: re.sub(r'((?<=\A)|(?<=,))(?=,|\Z)', 'NA', ',1,,,two,3,,,') how does it work? Veek M <veekjunk@foobar.com> - 2026-08-11 09:21 +0000
csiph-web