Life of a Computer Scientist

eerder vandaag, vertelde ik een collega van het werk dat re.finditer () in Python is meestal de beste manier om invoer te tokeniseren, in plaats van het hand-rollen van een lexer van een iterable van tekens. Hij zei dat het nodig is om het hele bestand in het geheugen te lezen. Ter plaatse heb ik schaapachtig gezegd dat je het waarschijnlijk regel voor regel kunt lezen en opnieuw kunt bellen.finditer () herhaaldelijk als de token regexp niet line grens overschrijdt.
dat is waar, maar geen geweldig antwoord. Het blijkt dat de bouw van een geketende re.finditer () van een iterabel van snaren is mogelijk, hoewel niet helemaal rechttoe rechtaan.
de eerste poging hier toont het belangrijkste idee: sinds re.finditer () wedstrijden zijn niet-overlappende, nadat alle wedstrijden zijn uitgeput, de volgende re.finditer () moet hervatten van de rest van de ongeëvenaarde string.

def finditer_chained_bad(pattern, strings, flags=0): last = '' for s in strings: m = None for m in re.finditer(pattern, last + s, flags): yield m if not m: last = '' ##1 continue # m is the last match object. last = s

de regel met het commentaar ##1 kan een twistpunt zijn. Als re.finditer () had geen match opgeleverd, de implementatiebeslissing hier is om s volledig te verwerpen in de veronderstelling dat er niets in zou ooit overeenkomen, maar men zou kunnen stellen dat een potentiële match zou kunnen worden het overschrijden van de input string grens. Als dit veranderd werd naar last += s, dan zou het werken als de potentiële match de invoergrens overschrijdt, ten koste van onbegrensde geheugengroei als een lange overspanning van de invoer nooit enige overeenkomst opleverde. De onbegrensde groei is noodzakelijk omdat dat betekent dat een potentiële match groter is dan de input brokgrootte, dus om de volledige match te krijgen, moeten we dynamisch de brokgrootte vergroten, wat we hier doen.
een ander probleem is dat de laatste overeenkomst een gedeeltelijke overeenkomst kan zijn die verder gaat in de volgende tekenreeks. Als we het naïef geven, krijgt de beller een gebroken gedeeltelijke match. Om dit te illustreren, stel dat de invoer wordt chunked in 8 tekenreeksen:

def main(): for m in finditer_chained_bad(r'\w+', ): print m.group(0)

het drukt:

helloworld

het woord “wereld” is per ongeluk opgesplitst in twee overeenkomsten vanwege de stringgrens. Om dit op te lossen, hoeven we alleen maar de laatste wedstrijd uit te stellen en de volgende re opnieuw te starten.finditer () vanaf het begin van de laatste wedstrijd. De volledige werkende code wordt:

def finditer_chained(pattern, strings, flags=0): last_s = '' for s in strings: last_m = None last_s += s for m in re.finditer(pattern, last_s, flags): if last_m: yield last_m last_m = m if not last_m: continue assert last_m is m last_s = s if last_m: yield last_m

en het drukt correct af:

helloworld

de resulterende finditer_chained () accepteert een bestand-achtig object omdat een bestand een iterable van regels is, alsof re.finditer () wordt toegepast op het hele bestand. En het werkt zonder het hele bestand in het geheugen te lezen.



+