C++
C ++ Streams
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;
});