2009-08-24 13 views
18

tengo que leer en los archivos de datos que se parecen a esto:Cómo leer los números desde un archivo ASCII (C++)

* SZA: 10.00 
2.648 2.648 2.648 2.648 2.648 2.648 2.648 2.649 2.650 2.650 
2.652 2.653 2.652 2.653 2.654 2.654 2.654 2.654 2.654 2.654 
2.654 2.654 2.654 2.655 2.656 2.656 2.657 2.657 2.657 2.656 
2.656 2.655 2.655 2.653 2.653 2.653 2.654 2.658 2.669 2.669 
2.667 2.666 2.666 2.664 2.663 2.663 2.663 2.662 2.663 2.663 
2.663 2.663 2.663 2.663 2.662 2.660 2.656 2.657 2.657 2.657 
2.654 2.653 2.652 2.651 2.648 2.647 2.646 2.642 2.641 2.637 
2.636 2.636 2.634 2.635 2.635 2.635 2.635 2.634 2.633 2.633 
2.633 2.634 2.634 2.635 2.637 2.638 2.637 2.639 2.640 2.640 
2.639 2.640 2.640 2.639 2.639 2.638 2.640 2.640 2.638 2.639 
2.638 2.638 2.638 2.638 2.637 2.637 2.637 2.634 2.635 2.636 
2.637 2.639 2.641 2.641 2.643 2.643 2.643 2.642 2.643 2.642 
2.641 2.642 2.642 2.643 2.645 2.645 2.645 2.645 

¿Cuál sería la forma más elegante para leer este archivo en una matriz de flotadores?

Sé cómo leer cada línea en una cadena, y sé cómo convertir la cadena en flotante usando atof(). ¿Pero cómo hago el resto más fácilmente?

He oído hablar de los buffers de cadenas, ¿me puede ayudar esto?

Respuesta

11

Dado que esto está etiquetado como C++, la forma más obvia sería utilizar transmisiones. De la parte superior de mi cabeza, algo como esto podría hacer:

std::vector<float> readFile(std::istream& is) 
{ 
    char chdummy; 
    is >> std::ws >> chdummy >> std::ws; 
    if(!is || chdummy != '*') error(); 
    std::string strdummy; 
    std::getline(is,strdummy,':'); 
    if(!is || strdummy != "SZA") error(); 

    std::vector<float> result; 
    for(;;) 
    { 
    float number; 
    if(!is>>number) break; 
    result.push_back(number); 
    } 
    if(!is.eof()) error(); 

    return result; 
} 

Por qué float, por cierto? Por lo general, double es mucho mejor.

Edición, ya que se cuestionó si el retorno de una copia de la vector es una buena idea:

Para una primera solución, sin duda me hago lo obvio. La función es leyendo un archivo en un vector, y lo más obvio para una función es devolver su resultado. Si esto da como resultado una desaceleración notoria depende de muchas cosas (el tamaño del vector, la frecuencia con la que se llama a la función y de dónde, desde el punto de vista de la velocidad del disco, si el compilador puede aplicar el RVO). No quisiera estropear la solución obvia con una optimización, pero si el perfil muestra que esto es lento, el vector debería pasarse por referencia no constante.

(También tenga en cuenta que C++ 1x con soporte rvalue, con suerte pronto estará disponible por medio de un compilador cerca de usted, hará que esta discusión sea discutible, ya que evitará que el vector se copie al regresar de la función.)

+0

El bucle genérico "leer todos los flotadores" sería 'número flotante; while (es >> número) result.push_back (número); ' – sth

+0

Aunque el suyo es, por supuesto, equivalente. – sth

+0

@sth: De hecho, eso es más escueto, aunque no me gusta que la variable 'number' salga del ciclo. – sbi

2

me gustaría hacer algo como esto:

std::ifstream input("input.txt"); 
std::vector<float> floats; 
std::string header; 
std::getline(input, header); // read in the "* SZA: 10.00" line 
if(header_is_correct(header)) { 
    float value; 
    // while we could successfully read in a float from the file... 
    while(input >> value) { 
     // store it in the vector. 
     floats.push_back(value); 
    } 
} 

NOTA:header_is_correct(header) es sólo un ejemplo, tendrá que aplicar ninguna comprobación de errores para que la primera línea no manualmente.

+0

¿por qué el voto a favor? He probado esto y lee correctamente cada flotador del archivo en un vector. –

18

El String Toolkit Library (Strtk) tiene la siguiente solución a su problema:

#include <iostream> 
#include <string> 
#include <deque> 
#include <iterator> 

#include "strtk.hpp" 

int main() 
{ 
    std::deque<float> flist; 
    strtk::for_each_line("file.txt", 
         [&flist](const std::string& line) 
         { strtk::parse(line," ",flist); } 
         ); 
    std::copy(flist.begin(),flist.end(), 
       std::ostream_iterator<float>(std::cout,"\t")); 
    return 0; 
} 

Más ejemplos se pueden encontrar en C++ String Toolkit (StrTk) Tokenizer.

+0

interesante, aunque debe tener en claro que esto es solo para compiladores C++ 0x. –

+18

Muy cierto, pero la lambda se puede colocar fácilmente en un predicado de estilo struct. Pensé para el estilo y las futuras referencias (admitámoslo dentro de 1-2 años, el código anterior y lo mismo será la norma) que sería una buena idea tener una opinión diferente sobre cómo se pueden hacer las cosas. –

+12

Me gustó esto. Buen uso de las nuevas lambdas, incluso si esta no puede ser la respuesta. –

2

solución simple algoritmos usando STL:

#include <vector> 
#include <iostream> 
#include <string> 
#include <iterator> 

struct data 
{ 
    float first; // in case it is required, and assuming it is 
       // different from the rest 
    std::vector<float> values; 
}; 

data read_file(std::istream& in) 
{ 
    std::string tmp; 
    data d; 
    in >> tmp >> tmp >> d.first; 
    if (!in) throw std::runtime_error("Failed to parse line"); 

    std::copy(std::istream_iterator<float>(in), std::istream_iterator<float>(), 
     std::back_inserter<float>(d.values)); 

    return data; 
} 

Si realmente necesita usar una matriz, primero debe asignarlo (ya sea dinámica o estática, si se conoce el tamaño) y luego se puede utilizar la misma copia algoritmo

// parsing the first line would be equivalent 
float data[128]; // assuming 128 elements known at compile time 
std::copy(std::istream_iterator<float>(is), std::istream_iterator<float>(), 
     data); 

Pero yo recomendaría usar std :: vector incluso en este caso, si usted necesita para pasar los datos a una función que toma una matriz siempre puede pasarlo como un puntero al primer elemento:

void f(float* data, int size); 
int main() 
{ 
    std::vector<float> v; // and populate 
    f(&v[0], v.size()); // memory is guaranteed to be contiguous 
} 
Cuestiones relacionadas