2015-06-04 10 views
7

Wciąż uczę się Pythona, a mam pytanie, którego nie byłem w stanie rozwiązać. Mam bardzo długi ciąg (miliony linii), który chciałbym podzielić na mniejszą długość łańcucha na podstawie określonej liczby wystąpień ogranicznika.Python - Dzielenie dużego ciągu przez liczbę wystąpień separatora

Na przykład:

ABCDEF 
// 
GHIJKLMN 
// 
OPQ 
// 
RSTLN 
// 
OPQR 
// 
STUVW 
// 
XYZ 
// 

W tym przypadku chciałbym dzielone na podstawie „//” i powrócić ciąg wszystkich liniach przed n-tego wystąpienia separatorem.

Więc wejście od podziału ciąg przez // by 1 zwróciłby:

ABCDEF 

wejście od podziału ciąg przez // przez 2 zwróciłby:

ABCDEF 
// 
GHIJKLMN 

wejściem dzielenie przez // łańcuch o 3 zwróciłby:

ABCDEF 
// 
GHIJKLMN 
// 
OPQ 

i tak dalej ... jednak, Długość pierwotnego ciągu 2 milionów linii wydawała się być problemem, gdy próbowałem po prostu podzielić cały łańcuch i "//" i po prostu pracować z poszczególnymi indeksami. (Otrzymałem błąd pamięci) Być może Python nie może obsłużyć tylu linii w jednym ułamku? Więc nie mogę tego zrobić.

Szukam sposobu, w którym nie muszę rozdzielać całego łańcucha na sto tysięcy indeksów, gdy potrzebuję tylko 100, ale zamiast tego zaczynam od początku do pewnego momentu, zatrzymaj się i wróć wszystko przed nim, co, jak zakładam, może być również szybsze? Mam nadzieję, że moje pytanie jest tak jasne, jak to tylko możliwe.

Czy istnieje prosty lub elegancki sposób, aby to osiągnąć? Dzięki!

+2

Dlaczego nie używasz generatora do czytania pierwszych n pozycji, dopóki nie przeczytasz wymaganej liczby ograniczników "//"? w ten sposób przeczytasz tylko to, czego potrzebujesz –

+0

Dzięki, przyjrzę się także generatorom, ponieważ nie znam ich. – Indie

+0

Pokaż, jaki kod wypróbowałeś do tej pory. –

Odpowiedz

1

Jeśli chcesz pracować z plikami zamiast ciągów w pamięci, oto kolejna odpowiedź.

Ta wersja jest napisana jako funkcja, która czyta linie i natychmiast je wypisuje, dopóki nie zostanie znaleziona określona liczba ograniczników (brak dodatkowej pamięci do przechowywania całego ciągu znaków).

def file_split(file_name, delimiter, n=1): 
    with open(file_name) as fh: 
     for line in fh: 
      line = line.rstrip() # use .rstrip("\n") to only strip newlines 
      if line == delimiter: 
       n -= 1 
       if n <= 0: 
        return 
      print line 

file_split('data.txt', '//', 3) 

Można to wykorzystać, aby zapisać dane wyjściowe do nowego pliku tak:

python split.py > newfile.txt 

Z trochę dodatkowej pracy, można użyć argparse przekazać parametry do programu.

+0

To faktycznie działa idealnie i nie ma problemu z obsługą pliku 2 milionów linii. Dziękuję Ci! – Indie

0

Na przykład:

i = 0 
    s = "" 
    fd = open("...") 
    for l in fd: 
     if l[:-1] == delimiter: # skip last '\n' 
      i += 1 
     if i >= max_split: 
      break 
     s += l 
    fd.close() 
0

Jako bardziej efektywny sposób można odczytać jodeł N linie rozdzielone przez separator, więc jeśli jesteś pewien, że wszystkie linie są dzielone przez separator można użyć itertools.islice zrobić praca:

from itertools import islice 
with open('filename') as f : 
    lines = islice(f,0,2*N-1) 
0

metoda, która przychodzi mi do głowy, kiedy czytam Twoje pytanie używa pętli for gdzie można pociąć na kilka znaków (na przykład 100 nazwałeś) i iterację podłańcuch.

thestring = "" #your string 
steps = 100 #length of the strings you are going to use for iteration 
log = 0 
substring = thestring[:log+steps] #this is the string you will split and iterate through 
thelist = substring.split("//") 
for element in thelist: 
    if(element you want): 
     #do your thing with the line 
    else: 
     log = log+steps 
     # and go again from the start only with this offset 

Teraz możesz przejść przez wszystkie elementy, przechodząc przez cały łańcuch 2 milionów (!) Linii.

najlepszą rzeczą do zrobienia jest rzeczywiście zrobić rekurencyjną funkcję z tego (jeśli to, co chcesz):

thestring = "" #your string 
steps = 100 #length of the strings you are going to use for iteration 

def iterateThroughHugeString(beginning): 
    substring = thestring[:beginning+steps] #this is the string you will split and iterate through 
    thelist = substring.split("//") 
    for element in thelist: 
     if(element you want): 
      #do your thing with the line 
     else: 
      iterateThroughHugeString(beginning+steps) 
      # and go again from the start only with this offset 
0

Ponieważ uczysz Python byłoby wyzwaniem modelować kompletne rozwiązanie dynamiczne. Oto pojęcie, jak możesz go wymodelować.

Uwaga: Poniższy fragment kodu działa tylko dla plików, które są/są w podanym formacie (patrz pytanie "Dla instancji" w pytaniu). Stąd jest to rozwiązanie statyczne.

num = (int(input("Enter delimiter: ")) * 2) 
with open("./data.txt") as myfile: 
    print ([next(myfile) for x in range(num-1)]) 

Teraz masz pomysł, możesz użyć dopasowywania wzorów i tak dalej.