Question:
Every time we try to insert a record in a binary file whose fields contain text, we try to give it a fixed size using a "C-style" character array.
Why is this necessary and why can't we do it with the predefined string
data type?
// Aquí os dejo un ejemplo más ilustrativo:
#include <iostream>
#include <fstream>
/* "#include <string>" no lo usamos al no estar permitido introducir
en nuestro fichero cadenas de caracteres "al estilo C++". */
using namespace std;
const int LIMITE = 15;
typedef struct
{
char campoTexto[LIMITE]; // Cadena de caracteres "al estilo C".
char campoTexto2[LIMITE];
} tRegistro;
tRegistro reg;
ofstream fBin;
int main() // Solo trataremos de introducir el registro en el fichero:
{
reg = {"hola", "lector"}; // Inicializamos el registro...
fBin.open("ejemplo.dat", ios::out | ios::binary);
if (!fBin.fail() && fBin.is_open())
{
fBin.write((char *) ®, sizeof(reg));
fBin.close();
} else cout << "Se ha producido un error. Intentelo de nuevo...";
return 0;
}
In addition, I also have doubts with this sentence:
fBin.write((char *) ®, sizeof(reg));
Why do we need to pass a pointer to the memory address of our register and not the register itself?
Answer:
Why is this necessary and why can't we do it with the predefined data type "string"?
You are making use of the class method ostream.write()
( ofstream
inherits methods from ostream
) of its own volition:
ostream& write (const char* s, streamsize n);
Therefore you use:
fBin.write((char *) ®, sizeof(reg));
But you could make use of the string
<<
operator , which allows you to add the contents of the string to an ostream
:
ostream& operator<< (ostream& os, const string& str);
So you could use instead:
fBin << reg.campoTexto << reg.campoTexto2;
Having defined reg
as string
:
typedef struct
{
string campoTexto;
string campoTexto2;
} tRegistro;
Also, this time it will write only the character string and not the padding characters until it reaches LIMITE
characters (15), so you may want to add a separator between both strings and a trailing carriage return:
fBin << reg.campoTexto << ", " << reg.campoTexto2 << std::endl;
In your code you are forcing the writing of 30 octets of data to the file, just the size of the structure, by filling the "gaps" between the two words with null characters ( \0
).
Why can't we just do fBin << reg;
?
We can't do that because your structure hasn't implemented what to do with the <<
operator, so if you try to use that construct it will tell you that there is no such operator implementation between something of type std::ofstream
and something of type tRegistro
:
pr.cpp:26:7: error: no match for 'operator<<' (operand types are 'std::ofstream' {aka 'std::basic_ofstream'} and 'tRegister')
To make it work that way you have to define the behavior with the <<
operator like this:
std::ostream& operator <<(std::ostream& entrada, const tRegistro& a)
{
return entrada << a.campoTexto << ", " << a.campoTexto2 << std::endl;
}
Also, you should change the structure definition to this:
struct tRegistro
{
string campoTexto;
string campoTexto2;
};
So now you can do:
fBin << reg;
Without malfunctioning or causing errors.
Full code used to test this code:
#include <iostream> #include <fstream> #include <string> using namespace std; struct tRegistro { string campoTexto; string campoTexto2; }; tRegistro reg; ofstream fBin; /* Aquí implementamos el comportamiento de la estructura con el operador << */ std::ostream& operator <<(std::ostream& entrada, const tRegistro& a) { return entrada << a.campoTexto << ", " << a.campoTexto2 << std::endl; } int main() // Solo trataremos de introducir el registro en el fichero: { reg = {"hola", "lector"}; // Inicializamos el registro... fBin.open("ejemplo.dat", ios::out | ios::binary); if (!fBin.fail() && fBin.is_open()) { /* Ahora podemos enviar al archivo directamente la estructura */ fBin << reg; fBin.close(); } else { cout << "Se ha producido un error. Intentelo de nuevo..."; } return 0; }