5.6. Buscar en iostreams

Cada tipo de iostream tiene el concepto de donde está el 'siguiente' carácter que proviene de (si es un istream) o que va hacia (si es un ostream). En algunas situaciones, puede querer mover la posición en este stream. Puede hacer esto usando dos modelos: uno usa una localización absoluta en el stream llamada streampos; el segundo trabaja como las funciones fseek() de la librería estándar de C para un fichero y se mueve un número dado de bytes desde el principio, final o la posición actual en el fichero.

El acercamiento de streampos requiere que primero llame una función 'tell':( tellp() para un ostream o tellg() para un istream. (La 'p' se refiere a 'put pointer' y la 'g' se refiere a 'get pointer'). Esta función retorna un streampos que puede usar después en llamadas a seekp() para un ostream o seekg() para un ostream cuando usted quiere retornar a la posición en el stream.

La segunda aproximación es una búsqueda relativa y usa versiones sobrecargadas de seekp() y seekg(). El primer argumento es el número de carácteres a mover: puede ser positivo o negativo. El segundo argumento es la dirección desde donde buscar:

ios::beg

Desde el principio del stream

ios::cur

Posición actual del stream

ios::end

Desde el principio del stream

Aquí un ejemplo que muestra el movimiento por un fichero, pero recuerde, no esta limitado a buscar en ficheros como lo está con stdio de C. Con C++, puede buscar en cualquier tipo de iostream (aunque los objetos stream estándar, como cin y cout, lo impiden explícitamente):

//: C04:Seeking.cpp
// Seeking in iostreams.
#include <cassert>
#include <cstddef>
#include <cstring>
#include <fstream>
#include "../require.h"
using namespace std;

int main() {
  const int STR_NUM = 5, STR_LEN = 30;
  char origData[STR_NUM][STR_LEN] = {
    "Hickory dickory dus. . .",
    "Are you tired of C++?",
    "Well, if you have,",
    "That's just too bad,",
    "There's plenty more for us!"
  };
  char readData[STR_NUM][STR_LEN] = {{ 0 }};
  ofstream out("Poem.bin", ios::out | ios::binary);
  assure(out, "Poem.bin");
  for(int i = 0; i < STR_NUM; i++)
    out.write(origData[i], STR_LEN);
  out.close();
  ifstream in("Poem.bin", ios::in | ios::binary);
  assure(in, "Poem.bin");
  in.read(readData[0], STR_LEN);
  assert(strcmp(readData[0], "Hickory dickory dus. . .")
    == 0);
  // Seek -STR_LEN bytes from the end of file
  in.seekg(-STR_LEN, ios::end);
  in.read(readData[1], STR_LEN);
  assert(strcmp(readData[1], "There's plenty more for us!")
    == 0);
  // Absolute seek (like using operator[] with a file)
  in.seekg(3 * STR_LEN);
  in.read(readData[2], STR_LEN);
  assert(strcmp(readData[2], "That's just too bad,") == 0);
  // Seek backwards from current position
  in.seekg(-STR_LEN * 2, ios::cur);
  in.read(readData[3], STR_LEN);
  assert(strcmp(readData[3], "Well, if you have,") == 0);
  // Seek from the begining of the file
  in.seekg(1 * STR_LEN, ios::beg);
  in.read(readData[4], STR_LEN);
  assert(strcmp(readData[4], "Are you tired of C++?")
    == 0);
} ///:~

Listado 5.8. C04/Seeking.cpp


Este programa escribe un poema a un fichero usando un stream de salida binaria. Como reabrimos como un ifstream, usamos seekg() para posicionar el 'get pointer'. Como puede ver, puede buscar desde el principio o el final del archivo o desde la posición actual del archivo. Obviamente, debe proveer un número positivo para mover desde el principio del archivo y un número negativo para mover hacia atrás.

Ahora que ya conoce el streambuf y como buscar, ya puede entender un método alternativo (aparte de usar un objeto fstream) para crear un objeto stream que podrá leer y escribir en un archivo. El siguiente código crea un ifstream con banderas que dicen que es un fichero de entrada y de salida. Usted no puede escribir en un ifstream, así que necesita crear un ostream con el buffer subyacente del stream:

ifstream in("filename", ios::in | ios::out);
ostream out(in.rdbuf());

Debe estar preguntándose que ocurre cuando usted lee en uno de estos objetos. Aqui tiene un ejemplo:

//: C04:Iofile.cpp
// Reading & writing one file.
#include <fstream>
#include <iostream>
#include "../require.h"
using namespace std;

int main() {
  ifstream in("Iofile.cpp");
  assure(in, "Iofile.cpp");
  ofstream out("Iofile.out");
  assure(out, "Iofile.out");
  out << in.rdbuf(); // Copy file
  in.close();
  out.close();
  // Open for reading and writing:
  ifstream in2("Iofile.out", ios::in | ios::out);
  assure(in2, "Iofile.out");
  ostream out2(in2.rdbuf());
  cout << in2.rdbuf();  // Print whole file
  out2 << "Where does this end up?";
  out2.seekp(0, ios::beg);
  out2 << "And what about this?";
  in2.seekg(0, ios::beg);
  cout << in2.rdbuf();
} ///:~

Listado 5.9. C04/Iofile.cpp


Las primeras cinco líneas copian el código fuente de este programa en un fichero llamado iofile.out y después cierra los ficheros. Esto le da un texto seguro con el que practicar. Entonces, la técnica antes mencionada se usa para crear dos objetos que leen y escriben en el mismo fichero. En cout << in2.rebuf(), puede ver como puntero 'get' es inicializado al principio del fichero. El puntero 'put', en cambio, se coloca en el final del fichero para que 'Where does this end up' aparezca añadido al fichero. No obstante, si el puntero 'put' es movido al principio con un seekp(), todo el texto insertado sobreescribe el existente. Ambas escrituras pueden verse cuando el puntero 'get' se mueve otra vez al principio con seekg(), y el fichero se muestra. El fichero es automáticamente guardado cuando out2 sale del ámbito y su destructor es invocado.