W konkretnym przypadku pliku, jeśli potrafisz zmapuj plik za pomocą mmap
, a jeśli pracujesz z bajtów zamiast kodu Unicode, możesz nakarmić plik mapowany w pamięci na re
, jakby był testem bytowym, a to zadziała. Ogranicza się to przestrzenią adresową, a nie pamięcią RAM, więc 64-bitowa maszyna z 8 GB pamięci RAM może dobrze odwzorować pamięć na plik o wielkości 32 GB.
Jeśli możesz to zrobić, to naprawdę fajna opcja. Jeśli nie możesz, musisz zwrócić się do opcji Messiera.
The 3rd-Party regex
moduł (nie re
) oferuje częściowe wsparcie mecz, który może być użyty do budowy strumieniowe wsparcia ... ale to niechlujny i ma wiele zastrzeżeń. Rzeczy takie jak lookbehinds i ^
nie będą działały, dopasowania na szerokość zero będą trudne do uzyskania, i nie wiem, czy będą one poprawnie współpracować z innymi zaawansowanymi funkcjami regex
, a oferty re
nie. Mimo to wydaje się, że jest to najbardziej zbliżone do kompletnego rozwiązania.
Jeśli zdasz partial=True
do regex.match
, regex.fullmatch
, regex.search
lub regex.finditer
, wówczas oprócz zgłoszenia kompletne mecze, regex
będzie również zgłosić rzeczy, które mogłyby być mecz, jeśli dane zostały przedłużone:
In [10]: regex.search(r'1234', '12', partial=True)
Out[10]: <regex.Match object; span=(0, 2), match='12', partial=True>
It Zgłasza częściowe dopasowanie zamiast pełnego dopasowania, jeśli więcej danych może zmienić wynik meczu, więc na przykład regex.search(r'[\s\S]*', anything, partial=True)
zawsze będzie częściowym dopasowaniem.
Dzięki temu można zachować przesuwane okno danych do dopasowania, przedłużając je po naciśnięciu końca okna i odrzucając zużyte dane od samego początku. Niestety, wszystko, co mogłoby zostać zagubione przez znikające dane od początku łańcucha, nie będzie działać, więc lookbehinds, ^
, \b
i \B
są obecnie niedostępne. Dopasowania o zerowej szerokości również wymagają ostrożnej obsługi. Oto dowód koncepcji, która używa przesuwanego okna nad plikiem lub obiektem podobnym do pliku:
import regex
def findall_over_file_with_caveats(pattern, file):
# Caveats:
# - doesn't support^or backreferences, and might not play well with
# advanced features I'm not aware of that regex provides and re doesn't.
# - Doesn't do the careful handling that zero-width matches would need,
# so consider behavior undefined in case of zero-width matches.
# - I have not bothered to implement findall's behavior of returning groups
# when the pattern has groups.
# Unlike findall, produces an iterator instead of a list.
# bytes window for bytes pattern, unicode window for unicode pattern
# We assume the file provides data of the same type.
window = pattern[:0]
chunksize = 8192
sentinel = object()
last_chunk = False
while not last_chunk:
chunk = file.read(chunksize)
if not chunk:
last_chunk = True
window += chunk
match = sentinel
for match in regex.finditer(pattern, window, partial=not last_chunk):
if not match.partial:
yield match.group()
if match is sentinel or not match.partial:
# No partial match at the end (maybe even no matches at all).
# Discard the window. We don't need that data.
# The only cases I can find where we do this are if the pattern
# uses unsupported features or if we're on the last chunk, but
# there might be some important case I haven't thought of.
window = window[:0]
else:
# Partial match at the end.
# Discard all data not involved in the match.
window = window[match.start():]
if match.start() == 0:
# Our chunks are too small. Make them bigger.
chunksize *= 2
Jest to sprzeczne z ideą regex. – SilentGhost
@SlientGhost: Niekoniecznie. Możesz chcieć parsować niektóre (nieskończone) strumienie za pomocą wyrażeń regularnych, zawsze dopasowując na bieżącym początku strumienia i zwracając dopasowania jako iterator (i pochłaniając tylko znaki dopasowane ze strumienia). – MartinStettner
@MartinStettner: Cóż, mógłbyś, gdyby był to automatyczny teoretyczny matcher bez backrefs (i kilka innych rzeczy, takich jak ograniczenia z wyprzedzeniem). Tak długo, jak RE może kompilować się do pojedynczego skończonego automatu (NFA lub DFA), może dopasowywać rzeczy w jednym przejściu, więc może obsłużyć plamienia pasujące do nieskończonego strumienia. (Ale Python używa PCRE, który nie jest automatyczny-teoretyczny i który potrzebuje wszystkich bajtów wcześniej). –