2009-04-02 21 views
0

私はすでにこのプロジェクトに関連する2種類の質問をしており、この結論に達しました。構造体のサイズをファイルに書き込んだり、それを読み込んだりするのが、これを実行する最善の方法です。ファイルに複数の構造体を読み書きするときにコンパイルエラー

私は在庫を維持できるように宿題を割り当てるプログラムを作成しています。私は同じタイプの複数の構造体をファイルに読み書きする必要があります。

問題は...これは本当に複雑で、私は全面的なプロセスの中で私の頭を包んでいる問題があります。私はたくさんの例を見てきました。私はそれをすべてまとめようとしています。私はコンパイルエラーが発生している...と私はそれらを修正する方法の手掛かりがゼロです。これで私を助けることができたら、私はとても感謝しています...ありがとう。私はそう...今迷ってしまいました

****

うまくいけば、LAST EDIT#3 *************

マイコード:

// Project 5.cpp : main project file. 

#include "stdafx.h" 
#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 
#include <algorithm> 

using namespace System; 
using namespace std; 
#pragma hdrstop 

int checkCommand (string line); 

template<typename Template> 
void readFromFile(Template&); 

template<typename Template> 
void writeToFile(Template&); 

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec); 

template<typename Template> 
void readVector(ifstream& in, vector<Template>& vec); 

struct InventoryItem { 
    string Item; 
    string Description; 
    int Quantity; 
    int wholesaleCost; 
    int retailCost; 
    int dateAdded; 
} ; 


int main(void) 
{ 
    cout << "Welcome to the Inventory Manager extreme! [Version 1.0]" << endl; 

    vector<InventoryItem> structList; 

    ofstream out("data.dat"); 

    writeVector(out, structList); 

    while (1) 
    { 

     string line = ""; 

     cout << endl; 
     cout << "Commands: " << endl; 
     cout << "1: Add a new record " << endl; 
     cout << "2: Display a record " << endl; 
     cout << "3: Edit a current record " << endl; 
     cout << "4: Exit the program " << endl; 
     cout << endl; 
     cout << "Enter a command 1-4: "; 

     getline(cin , line); 


     int rValue = checkCommand(line); 
     if (rValue == 1) 
     { 
      cout << "You've entered a invalid command! Try Again." << endl; 
     } else if (rValue == 2){ 
      cout << "Error calling command!" << endl; 
     } else if (!rValue) { 
      break; 
     } 
    } 


    system("pause"); 

    return 0; 
} 

int checkCommand (string line) 
{ 
    int intReturn = atoi(line.c_str()); 
    int status = 3; 

    switch (intReturn) 
    { 
     case 1: 
      break; 
     case 2: 
      break; 
     case 3: 
      break; 
     case 4: 
      status = 0; 
      break; 
     default: 
      status = 1; 
      break; 
    } 
    return status; 
} 

template <typename Template> 
void readFromFile(Template& t) 
{ 
    ifstream in("data.dat"); 
    readVector(in, t); Need to figure out how to pass the vector structList via a Template 
    in.close(); 
} 

template <typename Template> 
void writeToFile(Template& t) 
{ 
    ofstream out("data.dat"); 
    readVector(out, t); Need to figure out how to pass the vector structList via a Template 
    out.close(); 
} 

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec) 
{ 
    out << vec.size(); 

    for(vector<T>::const_iterator i = vec.begin(); i != vec.end(); ++i) 
    { 
     out << *i; // SUPER long compile error 
    } 
} 

template<typename T> 
vector<T> readVector(ifstream &in) 
{ 
    size_t size; 
    in >> size; 

    vector<T> vec; 
    vec.reserve(size); 

    for(int i = 0; i < size; ++i) 
    { 
     T tmp; 
     in >> tmp; 
     vec.push_back(tmp); 
    } 

    return vec; 
} 

私のコンパイルエラー:

1>.\Project 5.cpp(128) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const InventoryItem' (or there is no acceptable conversion) 
1>  C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)' 
1>  with 

私は今取得しています唯一のエラーです。私はあなたのコードがとても良いと思う。私の新しいコンパイラエラーはSUPERです。私はどこにエラーがあるのか​​を示しました。最後にもう一度私を助けてくれますか?

+0

元のコードを修正して質問を修正するのではなく、回答/フォローアップとして投稿する方がよいと思います。最後に元の回答は完全に相殺され、他の読者には混乱します –

答えて

2

あなたの読み書き機能はバグです。具体的には、あなたの代わりにこのような何かをやっている必要があります。

template<typename T> 
void write(ofstream &out, const T &t) 
{ 
    out << T; 
} 

OLD:bind1stあなたはそれを動作させるためにfunctionalを含ん行う必要があります。

#include <functional> 

の代わりに、これらすべての機能を扱うと、このようなしかし、イテレータに頼る方が良いでしょう:

template<typename T> 
void writeVector(ofstream &out, const vector<T> &vec) 
{ 
    out << vec.size(); 

    for(vector<T>::const_iterator i = vec.begin(); i != vec.end(); ++i) 
    { 
     out << *i; 
    } 
} 

template<typename T> 
vector<T> readVector(ifstream &in) 
{ 
    size_t size; 
    in >> size; 

    vector<T> vec; 
    vec.reserve(size); 

    for(int i = 0; i < size; ++i) 
    { 
     T tmp; 
     in >> tmp; 
     vec.push_back(tmp); 
    } 

    return vec; 
} 

関数をrにしたいEADと書き、あなたのInventoryItemだけでなく、おそらく:

ostream &operator<<(ostream &out, const InventoryItem &i) 
{ 
    out << i.Item << i.Description; // FIXME Read/write strings properly. 
    out << i.Quantity; 
    out << i.wholesaleCost << i.retailCost; 
    out << i.dateAdded; 
} 

istream &operator>>(istream &out, InventoryItem &i) 
{ 
    // Keep in same order as operator<<(ostream &, const InventoryItem &)! 
    in >> i.Item >> i.Description; // FIXME Read/write strings properly. 
    in >> i.Quantity; 
    in >> i.wholesaleCost >> i.retailCost; 
    in >> i.dateAdded; 
} 
+0

私は何をしているのか分かりません。これはC + +と信じられないほど難しいです...ああ、私の神。あなたは私がこの作業を手助けすることができますか?私は単に構造体をfrigginファイルに読み書きできるようにしたいと思っています! @OneShot、右。 – OneShot

+0

私のreadVector関数とwriteVector関数はまさにそれを行います。 – strager

+0

@ OneShotただし、InventoryItem自体を書き込む関数を作成する必要があります。私はその情報で私の答えを更新します。 – strager

1

EDIT:FUDをクリアしようとすると:

bind1stはSTLのfunctionalヘッダの一部です。ブーストが現れる前にSTLが存在した。 C++ 0xでは非一般的なバージョン、すなわちbind(別名boost::bind)が使用されています。詳細については、Annex D.8バインダを参照してください。

は今、本当の問題は、(複数の編集はこれが愚かに見えることがありますが、私は後世のためにこれをしておこう):

write<long>(out, structList.size()); 

これは、問題のある行です。これは、第2のパラメータとしてlongを期待するのに対し、vectorsize()はフードの下でタイプsize_tまたはunsigned intです。タイプミスは

更新があった:

write<size_t>(out, structList.size()); 

次の部分:size_tなくsize_Tを使用

for_each(structList.begin(), structList.end(), bind1st(write<InventoryItem>, out)); 

これはstructListまたは他のいくつかのタイプでなければなりません。またbind1stを使用できるようにするには、functionalを含めます。上に追加:

#include <functional> 

テンプレートbind1stはファンクタを取ります。他のハックがなければ、通常の関数ポインタを渡すことはできません。代わりにboost::bindを使用することができます。または:

他nitpicksため今
for(InventoryItem::iterator i = structList.begin(), f = structList.end(); 
     i != f; ++i) 
    write<InventoryItem>(out, *i); 

何ですか:

#include <String> 
... 
using namespace System; 

はあなたがここで使用しているのか?あなたはSTLの文字列をしたい場合は、含める必要があります。

#include <string> 

void main(void) 

は、標準的な署名ではありません。 I/Oは、事前に定義された挿入/抽出オペレーターと通常はるかに簡単です

int main(void) 

または

int main(int argc, char *argv[]); 

:のいずれかを使用します。あなたは(そして実際にすべきである)を使用することができます:

istream is(...); 
is >> data; 

と同様

ostream os(...); 
os << data; 

注意はまたあなたのreadFromFilewriteToFile機能は、単にvector<InventoryItem>の代わりvectorを使用するように固定する必要があります。

+0

ありがとう、私のコードで何が間違っているのか分かりますか? – OneShot

+0

<< and >>を使用する際の問題は、彼がforeach(私の答えを見てください)を使用していて、自分の構造の読み書き機能を作成していないことです。私はあなたの質問には関係していませんが、あなたの他の提案が好きです... – strager

+0

<< and >>は、size_tのようなプリミティブ型のために働きます。 – dirkgently

2

注:これは、あなたが取得しているコンパイルエラーへの答えではなく、あなたが扱っている持続性の問題の広い視野ではありません。

シリアライズとデシリアライズは、作業が簡単な問題ではありません。私の助言は、ライブラリの学習(boost :: serialization)とそれを使うことに投資することです。彼らはすでにあなたが直面する多くの問題を一度に解決しました。さらに、それらはすでに異なる出力形式(バイナリ、xml、json ...)を持っています

あなたが先に進んで自分自身を実装することを決めたら、まずファイル形式とそれはあなたのすべてのニーズに合っています。それは常に同じ環境で使用されますか?プラットフォームは変わるでしょうか(32/64ビット)?最も簡単な方法でバイナリにするか、人間にとって読めるようにするかを決めることができます。 XML、JSON、その他のより複雑なフォーマットを決める場合は、それを忘れてライブラリを使用してください。

最も簡単な解決策は、バイナリファイルを処理しており、最小のファイルを提供するソリューションです。一方で、アーキテクチャーの変更は非常に賢明です(たとえば、32から64ビットアーキテクチャー/ OSに移行するとします)

フォーマットを決定した後は、ユーザーの一部ではない追加情報を処理する必要がありますオブジェクトは現在は必要ですが、後で取得するためにファイルに挿入する必要があります。次に、最小の部品からより複雑な要素まで作業(およびテスト)を開始します。

もう1つのアドバイスは、最も単純な最も定義された部分で作業を開始し、そこから構築することです。テンプレートを可能な限り避けてください。そして、あるデータ型に対して明確で作業したら、他の型のために一般化する方法に取り組んでください。

免責事項:私は、ブラウザ上で直接コードを書かれているので、多少の誤差は、誤植またはちょうど約何があるかもしれません:)

テキスト

最初の単純なアプローチだけで、テキストを足すされますテキストの表現。利点は、バイナリアプローチよりも移植性があり、コードが短くても簡単ではないということです。結果として得られるファイルは大きくなりますが、ユーザーが読むことができます。

この時点で、テキストの読み込みがiostreamでどのように機能するかを知る必要があります。特に、文字列を読み込もうとすると、セパレータに達するまで文字が読み込まれます。これは、次のコードがあることを意味します。

std::string str; 
std::cin >> str; 

は、最初のスペース、タブ、または行末までしか読み取れません。数値(例としてints)を読み取ると、システムは最初の有効でない桁まですべての有効な桁を読み込みます。つまり、入力12345aの文字はすべて 'a'まで消費されます。

int i; 
std::cin >> i; 

これは後で取得するためにデータを保持する方法に影響するため、これを知る必要があります。

// input: "This is a long Description" 
std::string str; 
std::cin >> str; // Will read 'This' but ignore the rest 

int a = 1; 
int b = 2; 
std::cout << a << b; // will produce '12' 
// input: 12 
int read; 
std::cint >> read; // will read 12, not 1 

出力に挿入して入力を解析するには、セパレータが必要です。サンプルの目的のために、私は '|'キャラクター。テキストフィールドに表示されない文字である必要があります。

要素を分離するだけでなく、いくつかの追加情報(ベクトルのサイズ)を追加することもお勧めします。ベクトルの要素に対しては、異なるセパレータを使用することができます。手動でファイルを読むことができるようにしたい場合は、各項目は、独自のラインで読み取ること

namespace textual { 
    std::ostream & operator<<(std::ostream& o, InventoryItem const & data) 
    { 
     return o << data.Item << "|" << data.Description << "|" << data.Quantity 
     << "|" << data. ...; 
    } 
    std::ostream & operator<<(std::ostream & o, std::vector<InventoryItem> const & v) 
    { 
     o << v.size() << std::endl; 
     for (int i = 0; i < v.size(); ++i) { 
     o << v[i] << std::endl; // will call the above defined operator<< 
     } 
    } 
} 

あるように、あなたには「\ n」をして、入力を分割する必要があります「N \」を使用することができます各要素を取得し、次に '|' InventoryItemを解析するには:

namespace textual { 
    template <typename T> 
    void parse(std::string const & str, T & data) 
    { 
     std::istringstream st(str); // Create a stream with the string 
     st >> data; // use operator>>(std::istream 
    } 

    std::istream & operator>>(std::istream & i, InventoryItem & data) 
    { 
     getline(i, data.Item, '|'); 
     getline(i, data.Description, '|'); 

     std::string tmp; 
     getline(i, tmp, '|'); // Quantity in text 
     parse(tmp, data.Quantity); 
     getline(i, tmp, '|'); // wholesaleCost in text 
     parse(tmp, data. wholesaleCost); 
     // ... 
     return i; 
    } 

    std::istream & operator>>(std::istream & i, std::vector<InventoryItem> & data) 
    { 
     int size; 

     std::string tmp; 
     getline(i, tmp); // size line, without last parameter getline splits by lines 
     parse(tmp, size); // obtain size as string 

     for (int i = 0; i < size; ++i) 
     { 
     InventoryItem data; 
     getline(i, tmp); // read an inventory line 
     parse(tmp, data); 
     }  
     return i; 
    } 
} 

ベクトル読み取り関数では、getline + parseを使用して整数を読み取りました。これは、次のgetline()が最初のInventoryItemを実際に読み込み、サイズの後ろに '\ n'が続くことを保証するためです。

最も重要なコードには、文字列から挿入演算子が定義されている任意の型に変換できる「解析」テンプレートがあります。プリミティブ型、ライブラリ型(たとえば、文字列)、および演算子が定義されているユーザー型を読み取るために使用できます。これを使用してコードの残りの部分を単純化します。バイナリ形式のバイナリ

は、私は考えることができる最も簡単な方法は、のように、ベクターにelemementsの数を書いている(あなたが移行した場合、これはお尻の痛みになりますアーキテクチャを無視して) size_t(サイズは実装に関係なく)、すべての要素が続きます。各要素は、それぞれのメンバのバイナリ表現を出力します。 int型の基本型では、intのバイナリ形式を出力します。文字列については、文字列中の文字数の後に文字列の内容を続けたsize_tを書き込むことに頼ります。

namespace binary 
{ 
    void write(std::ofstream & o, std::string const & str) 
    { 
     int size = str.size(); 
     o.write(&size, sizeof(int)); // write the size 
     o.write(str.c_str(), size); // write the contents 
    } 
    template <typename T> 
    void write_pod(std::ofstream & o, T data) // will work only with POD data and not arrays 
    { 
     o.write(&data, sizeof(data)); 
    } 
    void write(std::ofstream & o, InventoryItem const & data) 
    { 
     write(o, data.Item); 
     write(o, data.Description); 
     write_pod(o, data.Quantity); 
     write_pod(o, data. ... 
    } 
    void write(std::ofstream & o, std::vector<InventoryItem> const & v) 
    { 
     int size = v.size(); 
     o.write(&size, sizeof(size)); // could use the template: write_pod(o, size) 
     for (int i = 0; i < v.size(); ++i) { 
     write(o, v[ i ]); 
     } 
    } 
} 

文字列またはInventoryItemを書き込む関数以外の基本型を書き込むテンプレートに対して、別の名前を選択しました。その理由は、あとで間違ってテンプレートを使用して、ディスクに誤った表現を格納する複雑なタイプ(つまり、文字列を含むUserInfo)を書きたくないからです。ディスクから

検索はかなり類似しているはず:

namespace binary { 
    template <typename T> 
    void read_pod(std::istream & i, T& data) 
    { 
     i.read(&data, sizeof(data)); 
    } 
    void read(std::istream & i, std::string & str) 
    { 
     int size; 
     read_pod(i, size); 
     char* buffer = new char[size+1]; // create a temporary buffer and read into it 
     i.read(buffer, size); 
     buffer[size] = 0; 
     str = buffer; 
     delete [] buffer; 
    } 
    void read(std::istream & i, InventoryItem & data) 
    { 
     read(i, data.Item); 
     read(i, data.Description); 
     read(i, data.Quantity); 
     read(i, ... 
    } 
    void read(std::istream & i, std::vector<InventoryItem> & v) 
    { 
     v.clear(); // clear the vector in case it is not empty 

     int size; 
     read_pod(i, size); 
     for (int i = 0; i < size; ++i) 
     { 
     InventoryItem item; 
     read(i, item); 
     v.push_back(item); 
     } 
    } 
} 

このアプローチを使用するために、STD ::はistreamとstd :: ostreamにはバイナリモードでオープンする必要があります。

int main() 
{ 
    std::ifstream persisted("file.bin", ios:in|ios::binary); 
    std::vector<InventoryItem> v; 
    binary::read(persisted, v); 

    // work on data 

    std::ofstream persist("output.bin", ios::out|ios::binary); 
    binary::write(persist, v); 
} 

すべてのエラーチェックは読者の練習として残して:)

あなたは、コードのどの部分にどんな質問がある場合は、単に尋ねます。

+0

うわー、これを見つけました。これは単に素晴らしいです。あなたはこれをする必要はありませんでしたが、あなたはそれを行うためにあなたの日から時間をかけました。私ができることはありがとうございます。私は本当にそれを意味することを知っている。 – OneShot

関連する問題