1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# @creator (C) 2003 Guido U. Draheim 4# @license http://creativecommons.org/licenses/by-nc-sa/2.0/de/ 5 6from __future__ import print_function 7 8import re 9 10try: 11 basestring 12except NameError: 13 basestring = str 14 15# ---------------------------------------------------------- Regex Match() 16# beware, stupid python interprets backslashes in replace-parts only partially! 17class MatchReplace: 18 """ A MatchReplace is a mix of a Python Pattern and a Replace-Template """ 19 def __init__(self, matching, template, count = 0, flags = None): 20 """ setup a substition from regex 'matching' into 'template', 21 the replacement count default of 0 will replace all occurrences. 22 The first argument may be a Match object or it is a string that 23 will be turned into one by using Match(matching, flags). """ 24 self.template = template 25 MatchReplace.__call__(self, matching, template, count, flags) 26 def __call__(self, matching, template = None, count = 0, flags = None): 27 """ other than __init__ the template may be left off to be unchanged""" 28 if isinstance(count, basestring): # count/flags swapped over? 29 flags = count; count = 0 30 if isinstance(matching, Match): 31 self.matching = matching 32 else: 33 self.matching = Match()(matching, flags) ## python 2.4.2 bug 34 if template is not None: 35 self.template = template 36 self.count = count 37 def __and__(self, string): 38 """ z = MatchReplace('foo', 'bar') & 'foo'; assert z = 'bar' """ 39 text, self.matching.replaced = \ 40 self.matching.regex.subn(self.template, string, self.count) 41 return text 42 def __rand__(self, string): 43 """ z = 'foo' & Match('foo') >> 'bar'; assert z = 'bar' """ 44 text, self.matching.replaced = \ 45 self.matching.regex.subn(self.template, string, self.count) 46 return text 47 def __iand__(self, string): 48 """ x = 'foo' ; x &= Match('foo') >> 'bar'; assert x == 'bar' """ 49 string, self.matching.replaced = \ 50 self.matching.regex.subn(self.template, string, self.count) 51 return string 52 def __rshift__(self, count): 53 " shorthand to set the replacement count: Match('foo') >> 'bar' >> 1 " 54 self.count = count ; return self 55 def __rlshift__(self, count): 56 self.count = count ; return self 57 58class Match: 59 """ A Match is actually a mix of a Python Pattern and MatchObject """ 60 def __init__(self, pattern = None, flags = None): 61 """ flags is a string: 'i' for case-insensitive etc.; it is just 62 short for a regex prefix: Match('foo','i') == Match('(?i)foo') """ 63 Match.__call__(self, pattern, flags) 64 def __call__(self, pattern, flags = None): 65 assert isinstance(pattern, str) or pattern is None 66 assert isinstance(flags, str) or flags is None 67 self.replaced = 0 # set by subn() inside MatchReplace 68 self.found = None # set by search() to a MatchObject 69 self.pattern = pattern 70 if pattern is not None: 71 if flags: 72 self.regex = re.compile("(?"+flags+")"+self.pattern) 73 else: 74 self.regex = re.compile(self.pattern) 75 return self 76 def __repr__(self): 77 return self.pattern 78 def __truth__(self): 79 return self.found is not None 80 def __and__(self, string): 81 self.found = self.regex.search(string) 82 return self.__truth__() 83 def __rand__(self, string): 84 self.found = self.regex.search(string) 85 return self.__truth__() 86 def __rshift__(self, template): 87 return MatchReplace(self, template) 88 def __rlshift__(self, template): 89 return MatchReplace(self, template) 90 def __getitem__(self, index): 91 return self.group(index) 92 def group(self, index): 93 assert self.found is not None 94 return self.found.group(index) 95 def finditer(self, string): 96 return self.regex.finditer(string) 97 98if __name__ == "__main__": 99 # matching: 100 if "foo" & Match("oo"): 101 print("oo") 102 x = Match() 103 if "foo" & x("(o+)"): 104 print(x[1]) 105 # replacing: 106 y = "fooboo" & Match("oo") >> "ee" 107 print(y) 108 r = Match("oo") >> "ee" 109 print("fooboo" & r) 110 s = MatchReplace("oo", "ee") 111 print("fooboo" & s) 112