2011-12-04 11 views
7

Jak to zrobić, używając algorytmów STL?copy_n lub do eof?

std::ifstream file(filename); 

std::vector<unsigned char> buf; 
for(auto file_it = std::istreambuf_iterator<char>(file); file_it != std::istreambuf_iterator<char>() && buf.size() < 2048; ++file_it) 
    buf.push_back(*file_it); 

Uwaga buf.size() < 2048.

np. co się stanie, jeśli zrobię co następuje, a plik będzie mniejszy niż 2048 bajtów?

std::copy_n(std::istreambuf_iterator<char>(file), 2048, std::back_inserter(buf)); 
+0

Co stanie się, jeśli plik będzie większy? Czy reszta jest odrzucona, czy potrzebujesz iteratora na początku części, która wciąż jest do przeczytania? – pmr

+0

Ifstream jest usuwany po przeczytaniu wymaganej liczby bajtów. – ronag

+0

To otwiera możliwość posiadania oddzielnego iteratora zamiast algorytmu. – pmr

Odpowiedz

1

Jak dokumentacji mówi std::copy_n() skopiuje dokładnien przedmiotów. Będzie kontynuował czytanie poza końcem sekwencji, do której odnosi się iterator. Nie jestem jednak pewien, co standard mówi o istreambuf_iterator<>. Prawdopodobnie jest to niezdefiniowane zachowanie, ale strumienie najprawdopodobniej przy końcu wygenerują wiele kopii eof(). Może to prowadzić do wielu śmieci, gdy dostępnych jest mniej niż 2048 bajtów.

W każdym razie, jeśli chcesz skopiować niezawodnie don przedmiotów, trzeba napisać własną funkcję:

template<typename I1, typename I2, typename size_type> 
I copy_upto_n (I1 begin, I1 end, size_type n, I2 out) 
{ 
    for (size_type i=0; (i < n) && (begin != end); ++i) 
    { 
     *out++ = *begin++; 
    } 
    return out; 
} 

Niektórzy ludzie mogą używać std::iterator_traits<> zamiast dodatkowym parametrem szablonu do wymuszać ten sam typ odległości co iterator.

+0

+1 Dla ogólnego algorytmu iteratora i wzmianki o 'std :: iterator_traits'. –

+0

'out' powinno prawdopodobnie być oddzielnym argumentem szablonu. – pmr

+0

@pmr: Rzeczywiście. Bez tego nie będzie działać dla przypadku użycia zamieszczonego w pytaniu (np. Używając 'std :: back_inserter()'). Będę edytować kod. –

0

Można użyć specjalnego back_insert_iterator, który odrzuca operacje na podstawie predykatu.

Ten kod został bezwstydnie wzięty z implementacji GCC stdlib i dostosowany. Wersja C++ 03 powinna wymagać tylko Container::const_reference w zadaniu.

template<typename Container, typename Predicate> 
class discarding_back_inserter 
    : public iterator<output_iterator_tag, void, void, void, void> 
{ 
    Container* container; 
    Predicate p; 
public: 
    typedef Container   container_type; 

    explicit 
    back_insert_iterator(Container& x, Predicate p) : container(&__x), p(p) { } 

    back_insert_iterator& 
    operator=(const typename Container::value_type& value) 
    { 
     if(*container) 
     container->push_back(__value); 
     return *this; 
    } 

    back_insert_iterator& 
    operator=(typename _Container::value_type&& value) 
    { 
     if(*container) 
     container->push_back(std::move(__value)); 
     return *this; 
    } 

    back_insert_iterator& 
    operator*() 
    { return *this; } 

    back_insert_iterator& 
    operator++() 
    { return *this; } 

    back_insert_iterator 
    operator++(int) 
    { return *this; } 
}; 
+1

Nie bierz tego osobiście, ale, eww. –

+0

@BenjaminLindley Nie, nigdy. Po prostu jednak warto poszukać odwrotnego podejścia do modyfikacji algorytmu. Wynik nie jest nawet bardzo ogólnym celem, ponieważ działa na pojemniku. Naprawdę chciałbym mieć wersję 'generate' opartą na wartości' Maybe'. To by było naprawdę łatwe. – pmr

+0

To nie używa predykatu w ogóle. Może działać, jeśli predykat zapisał wskaźnik do strumienia, aby mógł sprawdzić, czy eof. Argument wartości nie daje wiele informacji. – UncleBens