2011-12-04 14 views
7

Come faccio a farlo usando gli algoritmi STL?copy_n o fino a 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); 

Note buf.size() < 2048.

ad es. cosa succederà se faccio quanto segue e il file è più piccolo di 2048 byte?

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

Cosa succede se il file è più grande? Il resto viene scartato o è necessario un iteratore all'inizio della parte che è ancora da leggere? – pmr

+0

Ifstream viene scartato una volta letto il numero richiesto di byte. – ronag

+0

Questo apre la possibilità di avere un iteratore separato invece di un algoritmo. – pmr

risposta

1

Come dice la documentazione, std::copy_n() copierà esattamenten articoli. Continuerà a leggere oltre la fine della sequenza a cui si riferisce l'iteratore. Non sono sicuro di quello che dice lo standard su istreambuf_iterator<>, però. Probabilmente è un comportamento indefinito, ma è probabile che gli stream producano molte copie di eof() oltre la fine. Questo potrebbe portare a molti rifiuti quando ci sono meno di 2048 byte disponibili.

In ogni caso, se si desidera copiare in modo affidabile fino an oggetti, avrete bisogno di scrivere la propria funzione:

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; 
} 

Alcune persone potrebbero utilizzare std::iterator_traits<> invece di un parametro di template in più per forza lo stesso tipo di distanza dell'iteratore.

+0

+1 Per l'algoritmo di iterazione generico e la menzione di 'std :: iterator_traits'. –

+0

'out' dovrebbe probabilmente essere un argomento modello separato. – pmr

+0

@ pmr: Infatti. Senza di ciò, non funzionerà per il caso d'uso pubblicato nella domanda (ad esempio usando 'std :: back_inserter()'). Modificherò il codice. –

0

È possibile utilizzare uno speciale back_insert_iterator che scarta le operazioni in base a un predicato.

Questo codice è stato preso spudoratamente dall'implementazione GCC di stdlib e adattato. Una versione C++ 03 dovrebbe richiedere solo un Container::const_reference nel compito.

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

Non prenderlo personalmente, ma, eww. –

+0

@BenjaminLindley Nah, mai. Ho solo la pena di cercare l'approccio opposto alla modifica dell'algoritmo. Il risultato non è nemmeno molto generico in quanto funziona sul contenitore. Mi piacerebbe davvero avere una versione di 'generate' basata su un valore' Maybe'. Ciò renderebbe davvero facile. – pmr

+0

Questo non usa affatto il predicato. Potrebbe funzionare se il predicato memorizza un puntatore allo stream, in modo che possa controllare eof. L'argomento del valore non darà molte informazioni. – UncleBens