resueltos - libreria fstream c++
Cómo leer los números de un archivo ASCII(C++) (4)
Necesito leer los archivos de datos que se ven así:
* 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 de leer este archivo en una serie de flotadores?
Sé cómo leer cada línea en una cadena, y sé cómo convertir la cadena a flotar usando atof()
. ¿Pero cómo hago el resto lo más fácil?
He escuchado acerca de los amortiguadores de cadenas, ¿podría esto ayudarme?
Dado que esto está etiquetado como C ++, la forma más obvia sería utilizar secuencias. Desde lo alto 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, el double
es mucho mejor.
Editar , ya que se cuestionó si devolver una copia del vector
es una buena idea:
Para una primera solución, ciertamente haría lo obvio. La función es leer un archivo en un vector
, y lo más obvio para una función es devolver su resultado. Si esto produce una desaceleración notable depende de muchas cosas (el tamaño del vector, la frecuencia con la que se llama la función y desde dónde, la velocidad del disco desde la que se lee, si el compilador puede aplicar RVO). No quisiera estropear la solución obvia con una optimización, pero si el perfilado demuestra que esto es lento, el vector debe pasarse por referencia no constante.
(También tenga en cuenta que C ++ 1x con soporte de valores, con suerte pronto estará disponible por medio de un compilador cerca de usted, hará que esta discusión sea discutible, ya que evitará que se copie el vector al regresar de la función).
Haría 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 solo un ejemplo, tendrá que implementar cualquier comprobación de errores para esa primera línea manualmente allí.
La biblioteca de String Toolkit (Strtk) tiene la siguiente solución para 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;
}
Se pueden encontrar más ejemplos en C ++ String Toolkit (StrTk) Tokenizer .
Solución simple utilizando algoritmos 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 asignarla (ya sea de forma dinámica o estática si conoce el tamaño) y luego puede usar el mismo algoritmo de copia
// 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 recomendaría usar std :: vector incluso en este caso, si necesita pasar los datos a una función que toma una matriz, siempre puede pasarlos 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
}