【问题标题】:Problems while opening a .dat file in c++在 C++ 中打开 .dat 文件时出现问题
【发布时间】:2019-08-04 13:43:10
【问题描述】:

所以基本上我试图将一个类保存在 .dat 文件中,但在我的代码中,但它显示此错误 No matching member function for call to 'open' 但我放置了 fstream 标头。不知道是不是我写错了。我使用 Xcode 10。

class memberinformation
{
    string name; //name
    long int phonenumber; // phone number
    int memberid; // member id
    public :
    memberinformation()
    { name="not assigned" ;
        phonenumber=0;
        memberid=0;
    }
    int option3();
    int option2();
    int option1();
    int option4();
};



void wrt_file() //file function
{
    memberinformation k;
    fstream f;
    f.open ("information.dat",ios::app,ios::binary) //this is where I get the error. 
            f.write((char*)&k,sizeof(k));




}

【问题讨论】:

  • 好吧,一方面 f.open() 缺少分号
  • open 需要 2 个参数,你传递 3,我猜你想传递 ios::app | ios::binary 而不是 ios::app,ios::binary 作为第二个参数。阅读序列化方法,你不能将string对象存储到write()的二进制文件中。
  • “但它说这个错误” - 这与.dat 文件有什么关系?什么 .dat 文件?文件扩展名意味着nothing。如果需要,您可以将 GIF 图像存储在名为 .doc 的文件中。你的真正问题是什么?
  • 还要注意,当你成功编译并运行这个程序,然后尝试从文件中读取来恢复你的对象时,鼻恶魔会来咬你。
  • 如果你幸运的话,你会得到一个鼻恶魔的phonenumber。在那种情况下,不要称之为:P

标签: c++ c++17 xcode10


【解决方案1】:

你很幸运被一个简单的错误阻止了。 @Alex44 已经展示了如何摆脱错误:

f.open ("information.dat",ios::app|ios::binary); //this is where I get the error. 

但下面这行更糟糕:

f.write((char*)&k,sizeof(k));

因为编译器不会显示任何错误,而字符串的内容不会保存在文件中。 std::string 是不可复制的,因此,memberinformation 类也不是。所以你不应该尝试将它作为原始字节写入文件。

您应该编写一个写入二进制流的序列化函数(只是一种可能的序列化方式):

  • phonenumber 作为 long int (没问题)
  • memberid 作为 int (没问题)
  • name.size 作为size_t
  • name.dataname.size 字节

【讨论】:

  • 我对此很陌生。我以前写过这样的程序,但它是一个非常旧的 c++ 版本。你能建议或教我如何将课程保存在我认为是数据文件的 .dat 文件中。我的主要目标是创建一个可以保存多个成员信息的程序。
  • @VibhorSagar 我会避免使用数据的二进制表示。它更紧凑但更脆弱(更难调试/理解)。大多数应用程序将序列化为人类可读的格式,除非它们绝对需要速度/空间或两者兼而有之。查看序列化的概念并为您的类定义operator>>operator<< memberinformation
【解决方案2】:

另外两个答案已经回答了:

  1. Why its not compiling
  2. Why its a bad idea to write binary objects

我建议您通过使用流运算符的标准 C++ 技术来序列化对象。这使得写入/读取对象变得微不足道,并且通常使调试问题变得容易。

使用@serge-ballesta 在他的帖子中建议的格式:

class memberinformation
{
    string    name; //name
    long int  phonenumber; // phone number
    int       memberid; // member id
    public :
      // OLD STUFF GOES HERE

    void swap(memberinformation& other) noexcept
    {
         using std::swap;
         swap(name,        other.name);
         swap(phonenumber, other.phonenumber);
         swap(memberid,    other.memberid);
    }
    friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
    {
         return str << data.phonenumber << " "
                    << data.memberid    << " "
                    << data.name.size() << " "
                    << data.name        << " ";
    }
    friend std::istream& operator<<(std::istream& str, memberinformation& data)
    {
         memberinformation tmp;
         std::size_t       nameSize
         if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
             // All sizes were read correctly.
             tmp.name.resize(nameSize);
             if (str.ignore(1).read(&tmp.name[0], nameSize)) {
                 // ignored the space and read the name correctly.
                 // So now we update the "data" object
                 tmp.swap(data);
             }
         }
         return str;
    }
};

现在在您的代码中:

int main()
{
    memberinformation   object;

    std::cout << object;
    std::cin  >> object;

    std::ofstream  file("Data.dat");
    file << object;
}

【讨论】:

  • 我声明了其他函数来接收类的信息。你也能告诉我,如果每次我调用该函数在类中输入数据,它会保存它并像多个成员的多个数据一样存储它。谢谢
  • 另外,如果我决定设置一个搜索成员的功能,你建议我怎么做?
  • @VibhorSagar 我会编写一个循环来将所有"memberinformation" 加载到vector&lt; memberinformation&gt; 中,然后您可以简单地循环遍历向量来搜索它。如果您想要更复杂的搜索,您可以使用std::map&lt;&gt; 而不是std::vector&lt;&gt;,但这是您前面几个步骤。您可以在codereview.stackexchange.com 上查看任何工作代码
【解决方案3】:

你错过了一个分号,你需要“按位或”你的标志:

void wrt_file() //file function
{
    memberinformation k;
    fstream f;
    f.open ("information.dat",ios::app|ios::binary); //this is where I get the error. 
    ...
}

【讨论】:

    【解决方案4】:

    以上答案解决了您最初的问题。我再讲两个。

    首先,您可能应该在方法结束时使用 f.close()。让它退出范围并从那里清理可能完全没问题,但我个人认为这很丑陋,我不会指望它。

    其次,除非有充分的理由,否则我不会将数据存储为二进制文件。它不会是便携式的。上面的 Serge 提出了一种序列化方法。我会考虑另一种方法。我会以人类可读的形式(例如 JSON)写入文件。是的,这需要更多的工作,但是...

    -如果你改变你的类,你的旧文件仍然可以读取 - 它们可跨环境移植 -您实际上可以查看它们并很容易理解它们包含的内容

    所以 Serge 的上述建议并不可怕,但我会选择更现代的序列化/反序列化风格。

    请注意,您的 f.write 不起作用,因为您的对象包含其他对象,您不知道它们在后台是如何工作的。例如,该字符串几乎可以肯定不能以您尝试的方式转储。另外,您不仅在转储数据。

    另外,你应该打印 sizeof(k)。您可能会发现它有趣的信息。尝试考虑每个字节。您可以 printf the sizeof(k.name) 来帮助您解决一些问题。

    我几乎肯定这样做的信息会让您感到惊讶,但我自己并没有真正做到这一点,因为我永远不会尝试原始内存复制 C++ 对象,而这实际上就是您想要做的。

    【讨论】:

    • 默认的排序标准是投票,但根据我的排序方式,“上面的答案”可能会有所不同;)
    • 关于close当流对象超出范围且通常不直接调用时,该函数由basic_ofstream的析构函数调用。
    • 我认为手动调用 close() 是不好的做法,这就是析构函数的全部意义所在。
    • 应该阅读:stackoverflow.com/q/748014/14065 你提出了异常。那是个很好的观点。因为close() 会产生异常。 BUT 析构函数将调用 close 丢弃任何生成的异常(对于 std::fstream 对象)。所以最好的方法仍然是让 RAII 处理它(它做正确的事情)。我唯一会争辩的是,当你有一个交互式用户界面并且提供该信息会做一些有用的事情时。
    • 是的,在多种语言中使用相同的模式是一个坏习惯(你应该停止这样做)。您应该学习如何使用它应该使用的每种语言。将 Java 或 C# 的规则应用于 C++ 会使您使用这样的不良模式。 PS 垃圾收集是可怜的资源管理(它远不如基于 C++ 范围的管理)。
    猜你喜欢
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    • 2010-10-12
    • 2019-09-14
    • 2021-03-27
    • 2020-04-22
    • 1970-01-01
    相关资源
    最近更新 更多