regex

repository·hg·Indexed 20 days ago

https://github.com/mrabarnett/mrab-regex

A high-performance Python regular expression module designed as an alternative to 're'. It provides backwards compatibility with the standard library while adding advanced features such as nested sets, full Unicode case-folding, recursive patterns, fuzzy matching, and partial matches. The module supports 94 Unicode properties (including Script, Block, and General Category) and can release the GIL during matching on immutable strings via the concurrent=True argument.

Tokens
15.2K
Snippets
40
Records
74
Agent score
67%

What's inside regex

  1. Apply scoped flags to subpatterns

    hg
    You can apply specific flags to a subpattern only, rather than the entire regex, using the syntax (?flags-flags:...). This allows you to turn flags on or off for a specific part of the expression.
    (?flags-flags:...)
  2. Use Set Operators in character sets

    hg

    You can perform set operations within a character set [...]. The operators, in order of increasing precedence, are:

    • ||: Union (e.g., [a||b])
    • ~~: Symmetric difference (e.g., [x~~y] is x or y, but not both)
    • &&: Intersection (e.g., [a&&b])
    • --: Difference (e.g., [a--b] is a but not b)

    Implicit union (simple juxtaposition like [ab]) has the highest precedence.

    # Examples of set operations
    # [[a-z]--[qw]] -> all lowercase letters except q and w
    # [\p{ASCII}&&\p{Letter}] -> ASCII letters
  3. Match Unicode characters by name or property

    hg

    The regex module provides advanced Unicode support:

    Named Characters

    Use \N{name} to match specific Unicode characters (requires support in Python's Unicode database).

    Unicode Properties, Scripts, and Blocks

    Use \p{property=value}, \P{property=value}, \p{value}, or \P{value} to match characters based on Unicode properties.

    • \p{property:value} or \p{property=value} matches a character with that property.
    • \P{...} or \p{^...} is the inverse.
    • Short form \p{value} checks properties in order: General_Category, Script, Block, then binary properties.

    Short form prefixes:

    • Is<Name>: Matches a script or binary property (e.g., \p{IsLatin} for Script=Latin).
    • In<Name>: Matches a block (e.g., \p{InBasicLatin} for Block=BasicLatin).
    \p{Latin}
    \p{InBasicLatin}
    \p{IsAlphabetic}
  4. Use duplicate group names for repeated patterns

    hg

    Unlike the standard re module, regex allows you to use the same name for multiple groups.

    • group(name): Returns the last capture of that named group.
    • captures(name): Returns a list of all captures for that named group.
    # Both groups capture, the second capture 'overwrites' the first in .group()
    # but both are preserved in .captures()
    m = regex.match(r"(?P<item>\w+)? or (?P<item>\w+)?", "first or second")
    print(m.group("item"))    # 'second'
    print(m.captures("item")) # ['first', 'second']
  5. Understand named group numbering and branch resets

    hg

    All groups have a group number starting from 1.

    Key Rules:

    • Groups with the same name share the same group number.
    • Groups with different names have different group numbers.
    • If the same name is used for multiple groups, later captures 'overwrite' earlier ones, but all captures are available via the .captures() method of the match object.
    • Branch Resets (?|...): Group numbers are reused across different branches. If groups in different branches have different names, they will have different numbers.

    Example of branch reset numbering: In (\s+)(?|(?P<foo>[A-Z]+)|(\w+) (?P<foo>[0-9]+)), there are 2 groups:

    1. (\s+) is group 1.
    2. Both (?P<foo>[A-Z]+) and (\w+) (?P<foo>[0-9]+) are group 2 because of the branch reset and the shared name foo.
  6. Use branch reset for group number reuse

    hg

    The branch reset syntax (?|...|...) allows group numbers to be reused across different alternatives in a pattern. However, if alternatives contain groups with different names, they will still have distinct group numbers.

    >>> import regex
    >>> regex.match(r"(?|(first)|(second))", "first").groups()
    ('first',)
    >>> regex.match(r"(?|(first)|(second))", "second").groups()
    ('second',)
  7. Compare Version 0 and Version 1 behaviors

    hg

    The regex module provides two modes to ensure compatibility with the standard re module while offering enhanced features.

    Version 0 (Old Behavior)

    Indicated by the VERSION0 flag. This mode mimics the standard re module.

    • Zero-width matches: Handled as they were in re before Python 3.7 (.split won't split at zero-width; .sub advances by one character).
    • Inline flags: Apply to the entire pattern and cannot be turned off.
    • Sets: Only simple sets are supported.
    • Unicode: Case-insensitive matches use simple case-folding by default.

    Version 1 (New Behavior)

    Indicated by the VERSION1 flag. This is the enhanced mode.

    • Zero-width matches: Handled correctly.
    • Inline flags: Apply to the end of the group or pattern and can be turned off.
    • Sets: Supports nested sets and set operations.
    • Unicode: Case-insensitive matches use full case-folding by default.

    If no version is specified, the module defaults to regex.DEFAULT_VERSION.

  8. Use possessive quantifiers to prevent backtracking

    hg

    Possessive quantifiers match a subpattern up to a specified limit and do not allow the engine to backtrack into them if the subsequent pattern fails. This is equivalent to using an atomic group (?>(?:...)+).

    Supported syntax:

    • (?:...)?+ (possessive optional)
    • (?:...)*+ (possessive zero or more)
    • (?:...)++ (possessive one or more)
    • (?:...){min,max}+ (possessive range)
    (?:...)++
  9. Use nested sets and set operations in Version 1

    hg

    In Version 1 behavior (indicated by VERSION1), you can use nested sets and set operations (like subtraction). This is not possible in Version 0.

    Example of set subtraction in Version 1: [[a-z]--[aeiou]] matches any lowercase letter except vowels.

    In Version 0, the same pattern [[a-z]--[aeiou]] is interpreted literally as a set containing [ and a-z, followed by --, then a set of aeiou, and finally ].

  10. Use POSIX character classes

    hg

    POSIX character classes are supported using the [[:class:]] syntax. These are often treated as alternatives to Unicode properties, though some definitions differ from Unicode.

    Mappings:

    • [[:alnum:]] $\rightarrow$ \p{posix_alnum}
    • [[:digit:]] $\rightarrow$ \p{posix_digit}
    • [[:punct:]] $\rightarrow$ \p{posix_punct}
    • [[:xdigit:]] $\rightarrow$ \p{posix_xdigit}

    Syntax:

    • [[:alpha:]]
    • [[:^alpha:]] (negated)
    [[:alpha:]]
    [[:^alpha:]]
  11. Use partial matches for incremental input validation

    hg

    Partial matches allow you to determine if a string could potentially match a pattern if it were not truncated. This is useful for real-time validation (e.g., checking a user's input character-by-character).

    To use this feature, pass the partial=True keyword argument to match(), search(), fullmatch(), or finditer().

    Match objects returned via partial matching will have a partial attribute set to True. A complete match will have partial=False (or the attribute will be absent depending on the specific method call context, but the example shows False for complete matches).

    pattern = regex.compile(r'\d{4}')
    
    # Partial match: matches '123' but expects more digits
    print(pattern.fullmatch('123', partial=True))
    # <regex.Match object; span=(0, 3), match='123', partial=True>
    
    # Complete match: matches '1234' exactly
    print(pattern.fullmatch('1234', partial=True))
    # <regex.Match object; span=(0, 4), match='1234'>
    
    # Check the attribute on a match object
    print(pattern.match('123', partial=True).partial)
    # True