2011-06-28 13 views
8

Chciałbym przeczytać plik na ciąg. Szukam różnych sposobów, jak to zrobić skutecznie.Jak korzystać z istream ze stringami

Korzystanie stały rozmiar bufora * char

Otrzymałem answer z Tonym co tworzy bufor A 16 kb i czyta w tym buforze i dołącza do bufora nie ma nic więcej do czytania. Rozumiem, jak to działa i znalazłem to bardzo szybko. Nie rozumiem, że w komentarzach tej odpowiedzi mówi się, że w ten sposób wszystko kopiuje się dwa razy. Ale jak rozumiem, dzieje się to tylko w pamięci, nie z dysku, więc jest prawie niezauważalne. Czy problem polega na tym, że kopiuje z bufora na ciąg w pamięci?

Korzystanie istreambuf_iterator

The other answer otrzymałem wykorzystuje istreambuf_iterator. Kod wygląda pięknie i minimalnie, ale jest bardzo powolny. Nie wiem, dlaczego tak się dzieje. Dlaczego te iteratory są takie powolne?

Korzystanie memcpy()

Dla this question dostałam komentarze, które powinno się używać memcpy(), ponieważ jest to najszybsza metoda naturalnej. Ale w jaki sposób mogę użyć memcpy() z łańcuchem i obiektem ifstream? Czy ifstream nie powinien pracować z własną funkcją odczytu? Dlaczego używanie przenośności memcpy() jest niemożliwe? Szukam rozwiązania zgodnego z VS2010 i GCC. Dlaczego funkcja memcpy() nie będzie z nimi współpracować?

+ Czy jest jakaś inna skuteczna metoda?

Co polecasz, z której powłoki korzystam, w przypadku małych plików binarnych o wielkości 10 MB, <?

(nie chciałem podzielić to pytanie w części, jak ja jestem bardziej zainteresowany w porównaniu między inny sposób w jaki mogę odczytać ifstream na sznurku)

+0

Komentarz memcpy() odnosi się do odczytu za pomocą pliku mapowanego w pamięci, a nie do odczytu za pomocą istream. Plik mapowany w pamięci nie jest przenośny, ponieważ zależy od interfejsu API systemu operacyjnego. – Dikei

+0

Kiedy mierzysz wydajność, robisz to w trybie zwolnienia lub debugowania? Czy włączasz optymalizacje? Czy sprawdzanie iteratora jest wyłączone? Domyślnie studio wizualne ma niestandardowy iterator sprawdzający, czy może to zaszkodzić wydajności. – luke

+0

możliwy duplikat [jak wstępnie przydzielić pamięć dla obiektu std :: string] (http://stackoverflow.com/questions/3303527/how-to-pre-allocate-memory-for-a-stdstring-object/ 3304059 # 3304059)? Być może najbardziej dokładny duplikat jaki widziałem. Całe pierwsze zdanie jest praktycznie identyczne (jedyna różnica to "muszę ..." a "chciałbym ...") –

Odpowiedz

7

zdarza się tylko w pamięci, a nie z dysku, więc jest prawie niezauważalny

To jest rzeczywiście poprawna. Mimo to rozwiązanie, które tego nie robi, może być szybsze.

Dlaczego te iteratory są takie wolne?

Kod jest powolny, nie ze względu na iteratory, ale dlatego, że łańcuch nie wiedzieć, ile pamięci przeznaczyć: na istreambuf_iterator s może być wykonywany tylko raz, więc ciąg jest w zasadzie zmuszony do wykonywania powtarzających się powiązań, wynikający z pamięci reallocations, które są bardzo powolne.

Moja ulubiona-liner, z another answer jest strumieniowe bezpośrednio z bufora bazowego:

string str(static_cast<stringstream const&>(stringstream() << in.rdbuf()).str()); 

W ostatnich platformach będzie to rzeczywiście wstępnie przydzielić bufor. Będzie jednak nadal powodować nadmiarową kopię (od stringstream do końcowego ciągu).

+1

Sprawdzałem tylko różne rozwiązania, a twoje jest około 8 razy szybsze niż wszystkie oparte na iteratorze. Bardzo dobry. –

3

Najbardziej ogólnym sposobem byłoby prawdopodobnie odpowiedź za pomocą istreambuf_iterator:

std::string s((std::istreambuf_iterator<char>(source)), 
       (std::istreambuf_iterator<char>())); 

Chociaż dokładna wydajność jest bardzo uzależnione od realizacji, to wysoce nieprawdopodobne, że jest najszybszym rozwiązaniem.

Ciekawą alternatywą byłoby:

std::istringstream tmp; 
tmp << source.rdbuf(); 
std::string s(tmp.str()); 

To może być bardzo szybki, jeśli realizacja ma zrobić dobrą robotę na operator<< używasz iw jaki sposób wzrasta łańcuch zasięgu istringstream. Niektóre wcześniejsze implementacje (a może i nowsze) również były bardzo złe.

Zasadniczo wydajność przy użyciu std::string zależy od tego, w jaki sposób skuteczna implementacja polega na rosnącym łańcuchu; implementacja nie może określić, jak duża powinna być początkowo.Może chcesz porównać pierwszy algorytm przy użyciu tego samego kodu z std::vector<char> zamiast std::string, czy można zrobić dobre oszacowanie maksymalnej wielkości , korzystając reserve, czy coś takiego:

std::string s(expectedSize, '\0'); 
std::copy(std::istreambuf_iterator<char>(source), 
      std::istreambuf_iterator<char>(), 
      s.begin()); 

memcpy nie może odczytany z pliku i przy dobrym kompilatorze nie będzie tak szybki jak przy użyciu (z tymi samymi typami danych).

Mam tendencję do używania drugiego rozwiązania, powyżej, z << na rdbuf(), ale to częściowo ze względów historycznych; Przyzwyczaiłem się do tego przy pomocy (używając istrstream), zanim STL zostanie dodany do standardowej biblioteki . W tym celu możesz eksperymentować z istrstream i wstępnie przydzielonym buforem (zakładając, że możesz znaleźć odpowiedni rozmiar bufora dla ).

+0

Jeśli strumień źródłowy jest dostępny, możesz uzyskać jego rozmiar, wykonując 'source. seekg (0, std :: ios_base :: end); std :: streampos pos = source.tellg(); source.seekg (0, std :: ios_base :: beg); '. Następnie, jeśli 'source' jest nadal OK i' pos! = - 1', 'pos' będzie np. Wielkością pliku. Używałem tego w przeszłości. – sbi

+0

@sbi To będzie działać na większości implementacji Uniksa, ale nie na Windows, przynajmniej jeśli plik zostanie otwarty w trybie tekstowym. I nie gwarantuje się nawet kompilacji. –

+0

@ James: Czy możesz rozwinąć? Wiem, że użyłem go w wieloplatformowej aplikacji i myślę, że działało na Win32, OSX, BSD, Linux, Solaris i kilka innych. – sbi

Powiązane problemy