early today, I told a work colleague that re.o finditer () em Python é geralmente a forma preferida de tokenizar a entrada, em vez de rolar manualmente um lexer de um iterável de caracteres. Ele disse que é preciso ler todo o ficheiro em memória. In the spot, I sheeply said that you can probably re line by line and call re.finditer () repetidamente se o token regexp não atravessa o limite da linha.Isso é verdade, mas não é uma grande resposta. Acontece que construir um re acorrentado.finditer () a partir de um iterável de strings é possível, embora não muito simples.
a primeira tentativa aqui demonstra a ideia principal: desde re.finditer () matches are non-overlapping, after all matches are exhausted, the next re.o finditer () deverá continuar a partir do resto da cadeia não compensada.
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
a linha com o comentário ##1
pode ser um ponto de discórdia. Sim.finditer () não tinha rendido qualquer jogo, a decisão de implementação aqui é descartar s inteiramente assumindo que nada nele iria coincidir, mas poderia-se argumentar que um jogo potencial poderia estar cruzando o limite de string de entrada. Se isto fosse alterado para last += s
, então funcionaria se a correspondência potencial cruzasse o limite de entrada, à custa do crescimento da memória ilimitada se uma longa extensão da entrada nunca rendesse qualquer correspondência. O crescimento ilimitado é necessário porque isso significa que um potencial jogo é maior do que o tamanho do bloco de entrada, então, a fim de obter o jogo completo, precisamos aumentar dinamicamente o tamanho do bloco, que é o que estamos fazendo aqui.
outro problema é que a última partida pode ser uma partida parcial que continua na próxima string. Se o entregarmos ingenuamente, a pessoa que ligou terá uma correspondência parcial partida. Para ilustrar isso, suponha que a entrada é chunked em 8 strings de caracteres:
def main(): for m in finditer_chained_bad(r'\w+', ): print m.group(0)
imprime:
helloworld
a palavra “mundo” é quebrada em duas partidas por engano devido ao limite de string. Para corrigir isso, só precisamos adiar a última partida e reiniciar o re seguinte.finditer () do início da última partida. The full working code becomes:
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
And it correctly prints:
helloworld
the resulting finditer_chained() accepts a file-like object because a file is an iterable of lines, as if re.finditer () é aplicado a todo o arquivo. E funciona sem ler todo o arquivo em memória.