2011-01-20 19 views
5

Mam mały problem podczas korzystania z list.Lista C++ usuwa powtarzające się ciągi znaków

Co mam: Czytam linie z czatu, gdzie pojawiają się nowe linie tekstu. Zawsze pobierają ostatnie 20 linii z pola, a następnie chcę je porównać do wszystkich linii, które wcześniej pobrałem. Jeśli zostanie odkryta nowa linia, zostanie ona wysłana do zewnętrznej funkcji, która rozdzieli linię w celu dalszego przetwarzania. Zanim użyłem tablic i wektorów, ale lista wydaje się być lepszym sposobem na zrobienie tego.

Mój pomysł: Mam jedną listę o nazwie usedlines, która zawiera wszystkie stare używane linie. Lista pobranych linii zawiera najnowsze 20 linii pobranych z czatu.

Nie Po prostu chcę zapętlić obie z nich, aby sprawdzić, czy pobrane linie zawierają nową linię, która nie była wcześniej widoczna. Po pętli resztki w pobranych liniach są przenoszone do następnej funkcji.

Problem: Kiedy przełączyłem pętlę na tę pętlę, po chwili dostaję błąd. Czemu? Bonus: Czy ktoś ma lepszy pomysł na rozwiązanie tego zadania?

typedef list<string> LISTSTR; 
LISTSTR::iterator f; 
LISTSTR::iterator u; 
LISTSTR fetchedlines;     
LISTSTR usedLines;     



fetchedlines.insert(fetchedlines.end(), "one"); 
fetchedlines.push_back("two"); 
fetchedlines.push_back("three"); 
fetchedlines.push_back("four"); 
fetchedlines.push_back("three"); 

usedLines.push_back("three"); 
usedLines.push_back("blää"); 
usedLines.push_back("lumpi"); 
usedLines.push_back("four"); 


for (u = usedLines.begin(); u != usedLines.end(); u++) 
{ 
for (f = fetchedlines.begin(); f != fetchedlines.end(); f++) 
    { 
    if(*u==*f) 
    fetchedlines.remove(*f); 
    } 

} 
+2

Sprawdź 'std :: set',' std :: remove_if' i 'std :: set_intersection', aby uzyskać szybsze rozwiązanie. –

Odpowiedz

2

Nie wolno modyfikować listy (lub prawie żadnego innego kontenera) podczas jej iteracji. To jest twój bezpośredni problem.

Bardziej interesującym problemem jest to, dlaczego robisz to w ten sposób. Czy nie ma sposobu na uzyskanie kolejnych liczb na liniach, czy może znaczników czasu, więc mógłbyś je po prostu porównać?

+0

Myślałem o takich rzeczach, ale nie ma numerów linii ani znaczników czasu w wierszach, które czytałem ... Myślałem o zmianie cechy .unique listy w taki sposób, że jeśli znajdzie duplikaty, nie tylko kasuje "do dużo "element, ale także zły bliźniak ... – Lumpi

+0

" Nigdy nie modyfikuj listy (lub prawie żadnego innego pojemnika) podczas iteracji. " Przedstawię tę radę w mojej małej niebieskiej książce z uwagami na temat C++. Dzięki – Lumpi

5

Wywołanie fetchedlines.remove(*f) powoduje unieważnienie iteratora.

EDIT:

Możliwym rozwiązaniem problemu jesteś mającego jest zamiast po prostu iteracyjne usedLines i usunąć wszystkie elementy fetchedlines które są zawarte.

for (u = usedLines.begin() u != usedLines.end(); u++) 
    fetchedLines.remove(*u); 

//Process all of fetchedLines 
+0

Cholera, to brzmi elegancko! Dzięki za pomysł, spróbuję ;-) – Lumpi

+0

Są szybsze rozwiązania niż te, takie jak sugestie larsmans, ale powinno to rozwiązać przynajmniej problem. – James

+0

OK, działa w ten sposób !! Nadal jestem trochę utknięty w myśleniu w tablicy, więc przyjrzę się sugestiom Larshana. Dziękuję wszystkim za pchnięcie we właściwym kierunku. – Lumpi

2

Usuwasz element z fetchedlines podczas iteracji na nim.

Oto dlaczego otrzymujesz zły wskaźnik.

+0

Brzmi logicznie ... Więc najpierw muszę przejrzeć całą rzecz i zapamiętać, które elementy chcę później usunąć (po pętli przez całą rzecz)?! – Lumpi

+0

To nie jest seksowny sposób robienia tego. Zobacz odpowiedź Goza lub Jamesa ... Są sexy. –

0

Ponieważ * f jest iteratorem wskazującym na element, który właśnie usunąłeś.

Spróbuj wykonać następujące czynności:

if(*u==*f) 
{ 
    LISTSTR::iterator t = f;; 

    f--; 
    fetchedlines.remove(*t); 
} 

jako bok usuń przeszukuje listy do czegoś, co pasuje do danych wskazywanego przez iterator f. Jeśli chcesz proste pozbycie się danych wskazał jesteś lepiej robi

f = fetchedlines.erase(f); 
f--; 
3

off Powodem są coraz błędu jest to, że fetchedlines.remove (* f) modyfikuje fetchedlines, a jeśli był to ostatni element , wówczas dla przyrostów pętli zbyt daleko

spróbować czegoś takiego:

for (u = userLines.begin(); u != usedLines.end(); ++u) 
{ 
    for (f = fetchedlines.begin(); f != fetchedlines.end();) 
    { 
     if (*u == *f) 
     { 
      f = fetchedlines.erase (f); 
     } 
     else 
     { 
      ++f; 
     } 
    } 
} 

(to oczywiście nie zwracając czy jest to dobry sposób, aby rozwiązać problem)

0

Można to zrobić za pomocą list::remove_if i wyrażenia lambda. Ta metoda jest nadal dwiema zagnieżdżonymi pętlami, ale są one ukryte w wywołaniach funkcji. Może to być wystarczająco szybkie dla małych list, ale nie skaluje się zbyt dobrze. Mogłoby to być znacznie szybsze, gdyby dane zostały posortowane lub gdybyś użył zamówionego kontenera.

fetchedLines.remove_if([&](std::string &str) 
{ 
    return std::find(usedLines.begin(), usedLines.end(), str) != usedLines.end(); 
}); 
Powiązane problemy