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 default data type string
?
// 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?
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 fromostream
) of your own volition:Therefore you use:
But you could make use of the operator
<<
,string
which allows you to add the content of the string to aostream
:So you could use instead:
Having defined
reg
asstring
:Also, this time it will write only the character string and not the padding characters up to
LIMITE
characters (15), so you may want to add a separator between both strings and a trailing carriage return:In your code you are forcing the writing of 30 octets of data to the file, just the size of the structure, by filling
\0
the "gaps" between the two words with null characters ( ).Why can't we do it directly
fBin << reg;
?We can't do that because your struct 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 typestd::ofstream
and something of typetRegistro
:To make it work that way you have to define the behavior with the operator
<<
as follows:Also, you should change the structure definition to this:
So now you can do:
Without malfunctioning or causing errors.
Full code used to test this code:
With the code you can already understand the question, thanks for posting it.
To explain in more detail, let's solve this other question first.
What you are doing there is serializing the data. This is like taking a memory snapshot at that moment, which you will restore later.
The way you do it is by re-interpreting the memory your register is in (in this case you should use
reinterpret_cast
, not a "C-style" cast and you should cast to a constant pointer, not a normal one) as an array of characters .Now understanding this, we return to the main question. The class
std::string
in a super simplified form 1 may look something like this:What happens when you re-interpret the memory the same as before? That instead of "taking a picture" of the contents of
string
it, you will take a picture of the pointer that indicates where the data is, and its size. That's whystd::string
it can't be used if you try to serialize the data that way.So the one you can't use
std::string
is the result of the method you use to serialize, which is to literally copy the memory your object occupies, and re-interpret it as an array of characters.1: It actually has other attributes, for example a buffer for SSO, but that's not relevant in this case, so we'll omit it.