2017-05-27 12 views
6

Ogromne plik danych plain-text

czytam ogromny plik na kawałki przy użyciu Pythona. Następnie stosuję wyrażenie regularne w tym kawałku. Na podstawie znacznika identyfikatora chcę wyodrębnić odpowiednią wartość. Ze względu na wielkość porcji brakuje danych na granicach porcji.Python: regex mecz w poprzek granic plików fragmentach

Wymagania:

  • Plik musi być odczytywane w kawałkach.
  • Wielkość porcji musi być mniejsza niż lub równa 1 GiB.


przykład kodu Pythona

identifier_pattern = re.compile(r'Identifier: (.*?)\n') 
with open('huge_file', 'r') as f: 
    data_chunk = f.read(1024*1024*1024) 
    m = re.findall(identifier_pattern, data_chunk) 


przykłady danych Fragment

Dobre: ​​ liczba znaczników równoważne liczby wartości

Identyfikator: wartość
Identyfikator: wartość
Identyfikator: wartość
Identyfikator: wartość


Ze względu na wielkość porcji, można się różnych problemów brzegowych, jak wymienione poniżej. Trzeci identyfikator zwraca niepełną wartość, "v" zamiast "wartość". Następny fragment zawiera "alue". Powoduje to brak danych po analizie.

Źle: wartość identyfikatora niekompletne

Identyfikator: wartość
Identyfikator: wartość
Identyfikator: v


Jak rozwiązać problemy brzegowe kawałek takiego?

+0

Może znajdziesz swoją odpowiedź tutaj: [Python strumień regex parse] (https://stackoverflow.com/questions/4634376/python-regex-parse-stream) –

+0

Również tutaj: [wyrażenie regularne na potoku zamiast ciąg znaków?] (https://stackoverflow.com/questions/13004359/regular-expression-on-in-instead-of-string) –

+0

Ponieważ twój wzór pojawia się na granicy linii, może mógłbyś po prostu czytać linię na raz i dopasowywanie na linii zamiast porcji. – Himanshu

Odpowiedz

3

Zakładając, to jest twój dokładny problemu można chyba tylko dostosować swój regex i przeczytać wiersz po wierszu (co nie załaduje cały plik do pamięci):

import re 
matches = [] 
identifier_pattern = re.compile(r'Identifier: (.*?)$') 
with open('huge_file') as f: 
    for line in f: 
     matches += re.findall(identifier_pattern, line) 

print("matches", matches) 
+0

Dobre rozwiązanie do obsługi niewielkich ilości pamięci. Plik nie jest oparty na liniach, jak sugeruje przedstawiony przykład. Nie określiłem tego wymogu jednoznacznie. Musiałem wyraźnie określić, że plik musi być odczytywany w porcjach. W pewnym sensie muszę znaleźć rozwiązanie na granicy kawałka, unikając przypadkowego podwójnego liczenia. – JodyK

0

mam rozwiązanie bardzo podobny do Jacka odpowiedzieć:

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

m = [] 
with open('huge_file', 'r') as f: 
    for line in f: 
     m.extend(identifier_pattern.findall(line)) 

można użyć innej części regex API mieć taki sam wynik:

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

m = [] 
with open('huge_file', 'r') as f: 
    for line in f: 
     pattern_found = identifier_pattern.search(line) 
     if pattern_found: 
      value_found = pattern_found.group(0) 
      m.append(value_found) 

które mogliśmy uprościć stosując generator expression i list comprehension

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

with open('huge_file', 'r') as f: 
    patterns_found = (identifier.search(line) for line in f) 
    m = [pattern_found.group(0) 
     for pattern_found in patterns_found if pattern_found] 
+0

Zgadzam się, że są to dobre rozwiązania dla plików liniowych. Zakładając, że mamy ścisły stan, w którym "musimy" odczytać plik w porcjach: czy istnieje możliwe rozwiązanie problemu z obszarem granicy porcji? – JodyK

+0

Te przykłady zostały oparte na Twoim przykładzie. Ale czy dla każdej iteracji można zachować kilka ostatnich znaków z poprzedniej części, w której wzór mógł się pojawić? – EvensF

+0

Nie miałem jasności w wymaganiach dotyczących kawałków. Twoja propozycja zbliża się do podejścia Andriya. Sądzę, że jest to najbliższy sposób rozwiązania tego problemu. Obawiam się, że nie jest możliwe zrobienie pewnego rodzaju wyprzedzenia w następnej porcji lub w ukryciu w poprzednim fragmencie. Podejście "linia po linii" eliminuje korzyści związane z przetwarzaniem wielokrotnym, które można uzyskać przy dużych porcjach. – JodyK

1

Jeśli plik jest linia oparte, obiekt file jest leniwy generator linii, to załadować plik do pamięci linii przez linię (w kawałkach), w oparciu o które można użyć:

import re 
matches = [] 
for line in open('huge_file'): 
    matches += re.findall("Identifier:\s(.*?)$", line) 
+1

To naprawdę świetne rozwiązanie dla plików liniowych. Czy istnieje również rozwiązanie, w którym plik nie jest oparty na liniach i gdzie "musisz" przeczytać fragmenty? – JodyK

2

można kontrolować klocek formowania i mieć go blisko 1024 * 1024 * 1024, w który Sprawa unikać brakujących części:

import re 


identifier_pattern = re.compile(r'Identifier: (.*?)\n') 
counter = 1024 * 1024 * 1024 
data_chunk = '' 
with open('huge_file', 'r') as f: 
    for line in f: 
     data_chunk = '{}{}'.format(data_chunk, line) 
     if len(data_chunk) > counter: 
      m = re.findall(identifier_pattern, data_chunk) 
      print m.group() 
      data_chunk = '' 
    # Analyse last chunk of data 
    m = re.findall(identifier_pattern, data_chunk) 
    print m.group() 

Alternativelly, można przejść dwa razy w ciągu tego samego pliku z innego punktu początkowego read (po raz pierwszy od: 0, po raz drugi z max długości dopasowanej ciąg zebrane podczas pierwszej iteracji) przechowuj wyniki jako słowniki, gdzie key=[start position of matched string in file], to położenie będzie takie samo dla każdej iteracji, więc nie będzie problemu z łączeniem wyników, jednak myślę, że dokładniejsze byłoby scalanie według pozycji początkowej i długości dopasowanego ciągu.

Powodzenia!

+0

To bardzo sprytne podejście, najbliższe temu, co chcę. Nie myślałem o tym w ten sposób. Odczytywanie w oparciu o linie będzie jednak stanowić nowe wyzwanie dla fragmentów wieloprocesowych. Dlatego wolałbym metodę f.read() i karmienie fragmentów w celu oddzielenia procesów. Synchronizacja z linii po linii będzie bardzo kosztownym procesem międzyprocesowym. – JodyK

+0

@JodyK dzięki za komentarz, masz rację, zaktualizowałem odpowiedź przy użyciu alternatywnego podejścia –