2011-12-04 10 views
7

¿Cómo haré esto usando algoritmos STL?copy_n o hasta 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); 

Nota buf.size() < 2048.

p. Ej. ¿Qué pasará si hago lo siguiente, y el archivo es más pequeño que 2048 bytes?

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

¿Qué ocurre si el archivo es más grande? ¿El resto está descartado o necesitas un iterador al comienzo de la parte que aún queda por leer? – pmr

+0

Ifstream se descarta una vez que se ha leído el número requerido de bytes. – ronag

+0

Esto abre la posibilidad de tener un iterador separado en lugar de un algoritmo. – pmr

Respuesta

1

Al igual que la documentación dice, std::copy_n() copiará exactamente n artículos. Seguirá leyendo más allá del final de la secuencia a la que se refiere el iterador. Sin embargo, no estoy seguro de lo que dice el estándar sobre istreambuf_iterator<>. Probablemente sea un comportamiento indefinido, pero es probable que las secuencias produzcan muchas copias de eof() después del final. Esto podría llevar a una gran cantidad de basura cuando hay menos de 2048 bytes disponibles.

En cualquier caso, si quiere copiar confiablemente hastan artículos, tendrá que escribir su propia función:

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

Algunas personas podrían utilizar std::iterator_traits<> en lugar de un parámetro de plantilla adicional para forzar el mismo tipo de distancia que el iterador.

+0

+1 Para el algoritmo de iterador genérico y la mención de 'std :: iterator_traits'. –

+0

'out' debería ser probablemente un argumento de plantilla separado. – pmr

+0

@pmr: De hecho. Sin eso, no funcionará para el caso de uso publicado en la pregunta (por ejemplo, usando 'std :: back_inserter()'). Editaré el código. –

0

Puede usar un back_insert_iterator especial que descarta operaciones basadas en un predicado.

Este código ha sido descaradamente tomado de la implementación de GCC de stdlib y adaptado. Una versión de C++ 03 solo debería requerir un Container::const_reference en la tarea.

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

No tome esto personalmente, pero, eww. –

+0

@BenjaminLindley Nah, nunca. Solo pensé que valía la pena buscar el enfoque opuesto para modificar el algoritmo. El resultado ni siquiera es muy general ya que funciona en el contenedor. Realmente me gustaría tener una versión de 'generar' basada en un valor' Maybe'. Eso haría que esto sea realmente fácil. – pmr

+0

Esto no usa el predicado en absoluto. Puede funcionar si el predicado almacena un puntero a la secuencia, para que pueda verificar si eof. El argumento de valor no dará mucha información. – UncleBens