viața unui om de știință de calculator

mai devreme astăzi, I-am spus un coleg de muncă care re.finditer () în Python este de obicei modul preferat de a tokeniza intrarea, mai degrabă decât rularea manuală a unui lexer dintr-un iterabil de caractere. El a spus că este nevoie de citirea întregului fișier în memorie. La fața locului, am spus sheepishly că puteți citi, probabil, linie cu linie și apel re.finditer () în mod repetat, în cazul în care simbolul regexp nu trece limita liniei.
este adevărat, dar nu este un răspuns grozav. Se pare că construirea unui re înlănțuit.finditer () dintr-un iterabil de siruri de caractere este posibil, deși nu destul de simplu.
prima încercare aici demonstrează ideea principală: de la re.finditer () meciurile nu se suprapun, după ce toate meciurile sunt epuizate, următoarea re.finditer () ar trebui să reia din restul șirului de neegalat.

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

linia cu comentariul ##1 poate fi un punct de dispută. Dacă re.finditer () nu a dat nici un meci, decizia de punere în aplicare Aici este de a se debarasa S în întregime presupunând că nimic în ea s-ar potrivi vreodată, dar s-ar putea argumenta că un potențial meci ar putea fi trecerea limita șir de intrare. Dacă acest lucru ar fi schimbat la last += s, atunci ar funcționa dacă potrivirea potențială traversează limita de intrare, în detrimentul creșterii nelimitate a memoriei dacă o perioadă lungă de intrare nu a dat niciodată nicio potrivire. Creșterea nelimitată este necesară pentru că asta înseamnă că o potrivire potențială este mai mare decât dimensiunea bucății de intrare, deci pentru a obține potrivirea completă, trebuie să creștem dinamic dimensiunea bucății, ceea ce facem aici.
o altă problemă este că ultimul meci ar putea fi un meci parțial care continuă în șirul următor. Dacă îl cedăm naiv, apelantul va primi o potrivire parțială ruptă. Pentru a ilustra acest lucru, să presupunem că intrarea este chunked în 8 siruri de caractere:

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

se imprimă:

helloworld

cuvântul „lume” este rupt în două meciuri din greșeală din cauza limita șir. Pentru a remedia acest lucru, trebuie doar să amânăm ultimul meci și să repornim următoarea re.finditer () de la începutul ultimului meci. Codul complet de lucru devine:

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

și imprimă corect:

helloworld

rezultatul finditer_chained() acceptă un obiect asemănător unui fișier, deoarece un fișier este un iterabil de linii, ca și cum ar fi re.finditer () se aplică întregului fișier. Și funcționează fără a citi întregul fișier în memorie.



+