2011-10-07 18 views
7

Próbuję zrobić mały program, który przetwarza pliki INI, do użycia w późniejszym projekcie, najpierw zmniejszając jego rozmiar po załadowaniu do pamięci. ZatemUsuwanie pustych elementów z wektora ciągów znaków

gdzie VLINE jest wektor zawierający zawartość pliku

for (unsigned int i = 0; i < vLine.size(); i++) 
{ 
    if (!vLine[i].find(';', 0)) 
    { 
     vLine[i].erase(); 
    } 
} 

Upon drukowania VLINE, będę z lewej przestrzeni, gdzie kiedyś istniały wiersz rozpoczynający się średnikiem, takich jak

1.  
2. property 
3. property 
4. 
5. property 

Użycie polecenia resize() powoduje usunięcie ostatniego elementu z listy zamiast usuwania pustych części. Ten sam problem istnieje, gdy usuwam linie, które zawierają tylko spacje z wymazaniem().

Czy można usunąć te puste elementy, zachowując kolejność vLine?

(Przeproszenia niestosowania iteratorami w tym.)

+0

Re: (Przepraszam za nie za pomocą iteratorów w tym.) - Dlaczego nie używać iteratorów? –

+0

Brak wiedzy na temat prawidłowego użytkowania. Wydaje się (wydawało się, że?) Można to zrobić bez nich. – JGrey

+1

Możesz usunąć linie z wektora zamiast po prostu skasować zawartość ciągu (jak to robi 'vLine [i] .erase()', nie wywoływać 'vLine.erase()', ponieważ to usuwa z 'wektora' Następnie przepisz plik. Idiomatycznym sposobem na to jest C++ [idiotka-usuń idiom] (http://en.wikipedia.org/wiki/Erase-remove_idiom), ale chciałbyś użyć 'std :: remove_if 'from' 'używać warunkowego – birryree

Odpowiedz

8

to:

vLine[i].erase(); 

nie usuwa vLine[i] z wektora. Wyrażenie vLine[i] zwraca odniesienie do elementu o indeksie i. Zakładając, że vLine jest typu std::vector<std::string>, wywołanie funkcji erase() faktycznie wywołuje string::erase() na elemencie, a nie vector::erase() na wektorze. Wszystko, co robisz, powoduje, że ten konkretny element jest pusty.

Co prawdopodobnie chcesz coś takiego:

vLine.erase(vLine.begin() + i); 

To faktycznie usuwa element z wektorem. Teraz powoduje to unieważnienie wszystkich obecnych iteratorów w wektorze, a indeksy nie będą już poprawne. Jest to sytuacja, w której naprawdę trzeba użyć iteratorów.

std::vector<std::string>::iterator i = vLine.begin(); 
while(i != vLine.end()) 
{ 
    if(i->find(';', 0) != std::string::npos) 
    { 
     i = vLine.erase(i); 
    } 
    else 
    { 
     ++i; 
    } 
} 

Ale jest jeszcze łatwiejszy sposób, aby to zrobić: użyć standardowego algorytmu std::remove_if() z funktora następnie wywołać vLine.erase().

struct HasSemicolon 
{ 
    bool operator()(const std::string& s) 
    { 
     return s.find(';', 0) != std::string::npos; 
    } 
}; 

// ... 

vLine.erase(std::remove_if(vLine.begin(), vLine.end(), HasSemicolon()), vLine.end()); 

Jeśli możesz użyć kompilatora C++ 11, możesz również użyć wyrażeń lambda, aby być jeszcze bardziej zwięzłym.

+0

Bardzo zobowiązany, In Silico. Poświęcę czas, aby nauczyć się iteratorów i odpowiednio zmienić moją aplikację – JGrey

+0

Czy mógłbyś dodać przykład używając lambd C++ 11? aby to zrozumieć przez większą część godziny, ale jestem okropny w czytaniu C++ doc. –

+0

@QPaysTaxes http://en.cppreference.com/w/cpp/algorithm/remove ma przykład z lambdą. – arekolek

4

pomocą usuwanego/usuwania-idiomu korzystnie lambda z C++ 11:

foo.erase(std::remove_if(foo.begin(), foo.end(), 
         [](const std::string& s) 
         { return s.find(';', 0); })); 
7

Problemem jest logiki do usuwania elementów. Gdy natkniesz się na element o indeksie i, który chcesz usunąć, wyczyścisz jego wartość, ale nie usuniesz go z wektora.

Norma i prosty sposób, aby robić to, co chcesz zrobić, to std::remove_if:

vLine.erase(
    std::remove_if(
     vLine.begin(), 
     vLine.end(), 
     [](std::string const& s) { return s.size() != 0 && s.front() == ';'; }), 
    vLine.end()); 
Powiązane problemy