Buscar..


Observaciones

El constructor predeterminado de std::istream_iterator construye un iterador que representa el final de la secuencia. Por lo tanto, std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), .... significa copiar desde la posición actual en ifs hasta el final.

Corrientes de cuerda

std::ostringstream es una clase cuyos objetos se parecen a un flujo de salida (es decir, puede escribir en ellos a través del operator<< ), pero en realidad almacena los resultados de escritura y los proporciona en forma de un flujo.

Considere el siguiente código corto:

#include <sstream>
#include <string>                                                                                                                          

using namespace std;

int main()
{
    ostringstream ss;
    ss << "the answer to everything is " << 42;
    const string result = ss.str(); 
}   

La línea

ostringstream ss;

crea tal objeto. Este objeto se manipula primero como un flujo regular:

ss << "the answer to everything is " << 42;

Después de eso, sin embargo, el flujo resultante se puede obtener de esta manera:

const string result = ss.str();

(el result la cadena será igual a "the answer to everything is 42" ).

Esto es principalmente útil cuando tenemos una clase para la cual se ha definido la serialización de flujo y para la que queremos una forma de cadena. Por ejemplo, supongamos que tenemos alguna clase

class foo 
{   
    // All sort of stuff here.
};  

ostream &operator<<(ostream &os, const foo &f);

Para obtener la representación de cadena de un objeto foo ,

foo f;

podríamos usar

ostringstream ss; 
ss << f;
const string result = ss.str();        

Entonces el result contiene la representación de cadena del objeto foo .

Leyendo un archivo hasta el final.

Leyendo un archivo de texto línea por línea

Una forma adecuada de leer un archivo de texto línea por línea hasta el final generalmente no está clara en la documentación de ifstream . Consideremos algunos errores comunes cometidos por los programadores principiantes de C ++ y una forma adecuada de leer el archivo.

Líneas sin caracteres de espacios en blanco.

En aras de la simplicidad, supongamos que cada línea del archivo no contiene símbolos de espacios en blanco.

ifstream tiene el operator bool() , que devuelve verdadero cuando una secuencia no tiene errores y está lista para leer. Además, ifstream::operator >> devuelve una referencia al flujo mismo, de modo que podemos leer y verificar EOF (así como errores) en una línea con una sintaxis muy elegante:

std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
    std::cout << s << std::endl;
}

Líneas con caracteres de espacio en blanco.

ifstream::operator >> lee la secuencia hasta que ifstream::operator >> cualquier carácter de espacio en blanco, por lo que el código anterior imprimirá las palabras de una línea en líneas separadas. Para leer todo hasta el final de la línea, use std::getline lugar de ifstream::operator >> . getline devuelve la referencia al hilo con el que trabajó, por lo que está disponible la misma sintaxis:

while(std::getline(ifs, s)) {
    std::cout << s << std::endl;
}

Obviamente, std::getline también debe usarse para leer un archivo de una sola línea hasta el final.

Leyendo un archivo en un búfer a la vez

Finalmente, leamos el archivo desde el principio hasta el final sin detenerse en ningún carácter, incluidos los espacios en blanco y las nuevas líneas. Si sabemos que el tamaño exacto del archivo o el límite superior de la longitud es aceptable, podemos cambiar el tamaño de la cadena y luego leer:

s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    s.begin());

De lo contrario, necesitamos insertar cada carácter al final de la cadena, por lo que std::back_inserter es lo que necesitamos:

std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::back_inserter(s));

Alternativamente, es posible inicializar una colección con datos de flujo, usando un constructor con argumentos de rango de iterador:

std::vector v(std::istreambuf_iterator<char>(ifs),
    std::istreambuf_iterator<char>());

Tenga en cuenta que estos ejemplos también son aplicables si ifs se abre como archivo binario:

std::ifstream ifs("1.txt", std::ios::binary);

Copiando arroyos

Un archivo se puede copiar en otro archivo con secuencias e iteradores:

std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::ostream_iterator<char>(ofs));
ofs.close();

o redirigido a cualquier otro tipo de flujo con una interfaz compatible. Por ejemplo, el flujo de red Boost.Asio:

boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
    std::ostream_iterator<char>(stream));
stream.close();

Arrays

Como los iteradores pueden considerarse como una generalización de los punteros, los contenedores STL en los ejemplos anteriores pueden reemplazarse con matrices nativas. Aquí es cómo analizar números en una matriz:

int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);

Tenga cuidado con el desbordamiento del búfer, ya que las matrices no se pueden redimensionar sobre la marcha después de que se asignaron. Por ejemplo, si el código anterior se alimenta con un archivo que contiene más de 100 números enteros, intentará escribir fuera de la matriz y se ejecutará en un comportamiento indefinido.

Imprimiendo colecciones con iostream

Impresión básica

std::ostream_iterator permite imprimir el contenido de un contenedor STL en cualquier flujo de salida sin ciclos explícitos. El segundo argumento del constructor std::ostream_iterator establece el delimitador. Por ejemplo, el siguiente código:

std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));

imprimirá

1 ! 2 ! 3 ! 4 !

Tipo implícito de reparto

std::ostream_iterator permite emitir implícitamente el tipo de contenido del contenedor. Por ejemplo, sintonicemos std::cout para imprimir valores de punto flotante con 3 dígitos después del punto decimal:

std::cout << std::setprecision(3);
std::fixed(std::cout);

e instanciar std::ostream_iterator con float , mientras que los valores contenidos permanecen int :

std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));

por lo que el código de arriba rinde

1.000 ! 2.000 ! 3.000 ! 4.000 !

A pesar de std::vector hold int s.

Generación y transformación.

std::generate funciones std::generate , std::generate_n y std::transform proporcionan una herramienta muy poderosa para la manipulación de datos sobre la marcha. Por ejemplo, tener un vector:

std::vector<int> v = {1,2,3,4,8,16};

podemos imprimir fácilmente el valor booleano de "x es par" para cada elemento:

std::boolalpha(std::cout); // print booleans alphabetically
std::transform(v.begin(), v.end(), std::ostream_iterator<bool>(std::cout, " "),
[](int val) {
    return (val % 2) == 0;
});

o imprimir el elemento cuadrado:

std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
    return val * val;
});

Impresión de N números al azar delimitados por espacios:

const int N = 10;
std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);

Arrays

Al igual que en la sección sobre la lectura de archivos de texto, casi todas estas consideraciones pueden aplicarse a matrices nativas. Por ejemplo, imprimamos valores cuadrados de una matriz nativa:

int v[] = {1,2,3,4,8,16};
std::transform(v, std::end(v), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
    return val * val;
});

Análisis de archivos

Análisis de archivos en contenedores STL

istream_iterator s son muy útiles para leer secuencias de números u otros datos analizables en contenedores STL sin bucles explícitos en el código.

Usando tamaño de contenedor explícito:

std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
    v.begin());

o con la inserción de iterador:

std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
    std::back_inserter(v));

Tenga en cuenta que los números en el archivo de entrada se pueden dividir por cualquier número de caracteres de espacio en blanco y líneas nuevas.

Análisis de tablas de texto heterogéneas

Como istream::operator>> lee el texto hasta que aparezca un símbolo de espacio en blanco, se puede usar en la condición while para analizar tablas de datos complejas. Por ejemplo, si tenemos un archivo con dos números reales seguidos por una cadena (sin espacios) en cada línea:

1.12 3.14 foo
2.1 2.2 barr

se puede analizar de esta manera:

std::string s;
double a, b;
while(ifs >> a >> b >> s) {
    std::cout << a << " " << b << " " << s << std::endl;
}

Transformación

Cualquier función de manipulación de rangos puede usarse con rangos std::istream_iterator . Uno de ellos es std::transform , que permite procesar datos sobre la marcha. Por ejemplo, leamos valores enteros, multiplíquelos por 3.14 y almacenemos el resultado en un contenedor de punto flotante:

std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
    return val * 3.14;
});


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow