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.