2017-10-26 6 views
0

私は非常にゆっくりとC++が非常に型に基づいた機密性の高い言語であることに気付いたので、今日はほとんどプログラミングを進めませんでした。というのも、構造体のメンバの1つがstring型であれば、クラス。私が知るまで、これは当てはまりませんでした。私は最初に私のポイントを証明します。なぜ文字列クラスのメンバが含まれている場合、バイナリファイルに構造体を追加するのを拒否しますか?

このコードは動作しません。

#include <iostream> 
#include <string> 
#include <fstream> 
using namespace std; 

// function Headers: 
void Receive_Information(fstream& personal_data); 
void Display_Information(fstream& personal_data); 

struct Info 
{ 
    string name; // if this is char name[50]; ... it works perfectly. 
    float weight; 
    char grade; 
}; 

int main() 
{ 
    fstream personal_data; 
    // Get two people from the stream: 
    Receive_Information(personal_data); 
    Receive_Information(personal_data); 

    // Now display their information from the binary file. 
    Display_Information(personal_data); 


    return 0; 
} 

void Receive_Information(fstream& personal_data) 
{ 
    personal_data.open("personal_data.dat", ios::out | ios::app | ios::binary); // open a binary file for appending. 

    Info person; // This structure variable is written to the binary file. 
    cout << "What is the person's name: "; 
    getline(cin, person.name); // change to cin.getline(person.name, 50) when defined as a character array 
    cout << "\nWhat is this individual's weight: "; 
    cin >> person.weight; 
    cout << "\nWhat letter grade (A, B, C, D, F) did s(he) get on the last exam: "; 
    cin >> person.grade; 

    // write the structure variable to file. 
    personal_data.write(reinterpret_cast<char *>(&person), sizeof(person)); 

    cin.ignore(); 

    personal_data.close(); // close the file after writing to it. 

    cout << endl; 
} 

void Display_Information(fstream& personal_data) 
{ 
    Info person_out; // This is the structure variable we get from opening the binary file. 

    personal_data.open("personal_data.dat", ios::in | ios::binary); // Open the binary file to read from. 

    personal_data.read(reinterpret_cast<char *>(&person_out), sizeof(person_out)); // Read the first line of the file. 

    while(personal_data) // While I haven't reached the end of the file keep reading in the information. 
    { 
     cout << "The weight of the person is " << person_out.weight << endl; 

     cout << "The person's name is " << person_out.name << endl; // This can't be printed because it's a string. 

     cout << "The grade this individual received on the last exam is " << person_out.grade << endl; 

     personal_data.read(reinterpret_cast<char *>(&person_out), sizeof(person_out));// keep pulling information from the file. 

     cout << endl; 
    } 

    personal_data.close(); 
} 

を私はこの出力を取得、その後つもりのように私はプログラムを実行する場合は、次の関数Display_Information()は意図的に表示するように命じていること

What is the person's name: Generic Me 

What is this individual's weight: 180 

What letter grade (A, B, C, D, F) did s(he) get on the last exam: A 

What is the person's name: Suspicious You 

What is this individual's weight: 200 

What letter grade (A, B, C, D, F) did s(he) get on the last exam: C 

The weight of the person is 180 
The person's name is // it's no coincidence that it fails here. 
Process returned 255 (0xFF) execution time : 25.193 s 
Press any key to continue. 

をお知らせ最初に重み付けし、次に文字列型の名前を返します。私は構造のメンバーがバイナリファイルから読み込むことができるということを証明するためにこれを行いましたが、変数の1つが文字列になるまでプログラムは異常終了します。

私の仮説は、文字列型のメンバの1つであれば、バイナリファイルに構造体を簡単に書き込むことができないということです。私が文字配列に名前を変更すると私の主張を証明するために、すべてが機能します。

だから混乱を取り除くために、私は手動でバイナリファイルpersonal_data.datを削除してから、ちょうどこれら二つのマイナーな変更を行うプログラムを再実行します...

私は

struct Info 
{ 
    string name; // if this is char name[50]; ... it works perfectly. 
    float weight; 
    char grade; 
}; 
を変更しますこれに

struct Info 
{ 
    char name[50]; // A character array which can hold about 50 characters. 
    float weight; 
    char grade; 
}; 

そしてReceive_Information機能で、私はこの変更されます。

をこの

cin.getline(person.name, 50); 

、すべてが魔法のように動作しますへ

getline(cin, person.name); 

!! どのように完璧に動作するか見てみましょう!

What is the person's name: Generic Me 

What is this individual's weight: 180 

What letter grade (A, B, C, D, F) did s(he) get on the last exam: A 

What is the person's name: Suspicious You 

What is this individual's weight: 200 

What letter grade (A, B, C, D, F) did s(he) get on the last exam: C 

The weight of the person is 180 
The person's name is Generic Me 
The grade this individual received on the last exam is A 

The weight of the person is 200 
The person's name is Suspicious You 
The grade this individual received on the last exam is C 


Process returned 0 (0x0) execution time : 12.998 s 
Press any key to continue. 

だから今、私は構造内の文字列についての私のポイントを証明したことを、バイナリに追加するときの構造内の文字列データメンバと私のオリジナルのプログラムを動作させるために(文字を使用せずに)行うことができるものがありますファイル。ちなみに、構造変数personは文字列メンバを持つバイナリファイルに書き込んで正常に読み込めることも知っていましたが、複数の構造を追加しようとすると、あなたのプログラムはあまり好きではありません。かなり奇妙な。

思考。

+0

「シリアライゼーション」について読んでください。 'std :: string'には、バイナリファイルに保存しているもの(ポインタ)の文字列の内容へのポインタが含まれています(無駄です)。 – Mat

+0

'std :: string'はポインタを内部に保持する複雑な構造体です(' char [] '配列とは異なります)。あなたはガベージ(メモリは動的でOS制御下にある)を指しているので、ポインタを保存してそれを再び読むことはできません。あなたがしようとしていることは最初から間違っているので、 'reinterpret_cast'を使用してシリアライズ/デシリアライズしないでください。実際には 'reinterpret_cast'を全く使わないでください。 – freakish

+0

その行 'personal_data.write(reinterpret_cast (人)、sizeof(人));'あなたが思っていることを最も確かにしていません。なぜあなたは 'write'を使っていますか?そして実際にはそれはどういうことだと思いますか? – user463035818

答えて

4

Do を使用すると、シリアライズ/デシリアライズまたはストリング化が行われます。 reinterpret_castは非常に危険なツールであり、非常に特殊な状況でのみ使用する必要があります。問題に対処する正しい方法は、Infoクラスにoperator<<operator>>を指定することです。例:

std::ostream& operator<<(std::ostream& os, const Info& p) 
{ 
    os << name; 
    os << weight; 
    os << grade; 
    return os; 
} 

reinterpret_cast<T>文字通り、それはTであるかのようにメモリ位置と行動を見て、コンパイラを要求します。 std::stringのようなクラスは複雑で、リソースを所有し、その内部の他のメモリ位置へのポインタを格納します。 std::stringのバイトを書き込もうとすると、文字がstd::stringインスタンスに格納されていない可能性があるため、ガベージが発生します。

+0

Woah。これはかなり先進的ですが、かなり簡単です。クール! – xyz123

0

std :: stringは別のメモリへのポインタを格納するクラスです実際の文字列が格納されている場所。 std :: stringをfileに書き出すと、文字列自体ではなく、ポインタを表すバイトだけが書き込まれます。

関連する問題