如何在C ++中读取/写入数据文件

时间:2014-05-13 05:39:39

标签: c++ binary fstream

我在使用.read(reinterpret_cast(& x),sizeof(x))命令从二进制文件(* .dat)读取时遇到问题,但总是存在关于文件存在的错误文件存在或已成功创建时。这是代码:

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


 struct x{
  char name[10],pass[10];
 };


 int main()
 {
   x x1,x2;
   fstream inout;
   inout.open("test.dat" ,ios::binary);
   if(!inout)
   {
      cout<<"Error";
      exit(1);
   }
   cout<<"Enter your name:";
   cin>>x1.name;
   inout.write(reinterpret_cast <const char*> (&x1.name), sizeof(x1));
   cout<<"Enter your name:";
   cin>>x1.pass;
   inout.write(reinterpret_cast <const char*> (&x1.pass), sizeof(x1));
   while(inout.read(reinterpret_cast <char*> (&x2.name), sizeof(x1)))
   {
      cout<<x2.name;//here is my problem cannot read!!
   }
   inout.close();
 }

4 个答案:

答案 0 :(得分:0)

在写入操作后使用std:flush

// ... Write x1.name and x1.pass

inout << std::flush;

// ... Read x2.name in while loop.

inout.close();

答案 1 :(得分:0)

输出到文件时出现问题。 首先,您将struct x1写入仅填充名称字段的文件

inout.write(reinterpret_cast <const char*> (&x1.name), sizeof(x1));

之后:

inout.write(reinterpret_cast <const char*> (&x1.pass), sizeof(x1));

您从x1.pass的地址开始写入,但是您正在编写sizeof(x1)字节。 sizeof(x1)在这里是20但是从x1.pass的开头到结构的末尾只有10个字节,所以你要将10个字节的未知数据从堆栈写入你的文件。 因此,这是您的文件可能不包含您希望包含的内容的第一件事。

接下来的事情是,在写完数据之后,流就位于文件的末尾,您尝试从那里读取。您必须将位置移回流的开头才能读取您刚编写的内容。例如使用:

inout.seekg(std::ios::beg);

答案 2 :(得分:0)

如果您对同一个流进行读写操作,则更倾向于使用刷新或文件定位功能。

MSDN says

  

当使用basic_fstream对象执行文件I / O时,虽然底层缓冲区包含单独指定的读写位置,但当前输入和当前输出位置连在一起,因此,读取一些数据会移动输出位置

GNU Stdlib

  

如您所见,'+'请求可以同时执行输入和输出的流。使用此类流时,必须在从读取切换到写入时调用fflush(请参阅流缓冲)或文件定位功能(如fseek)(请参阅文件定位),反之亦然。否则,内部缓冲区可能无法正确清空。

答案 3 :(得分:0)

从输入流中读取原始C风格的数组并不像对operator>>()的简单调用那样惯用。您还必须通过跟踪为缓冲区分配的字节和读入缓冲区的字节来防止缓冲区溢出。

可以使用输入流方法getline()读取缓冲区。以下示例显示了对x1.name的提取;对于x1.path

也是如此
if (std::cin.getline(x1.name, sizeof(x1.name))) {

}

第二个参数是要读取的最大字节数。它很有用,因为流不会写入传递分配的数组边界。接下来要做的就是将其写入文件:

if (std::cin.getline(x1.name, sizeof(x1.name))) {
    inout.write(reinterpret_cast<char*>(&x1.name), std::cin.gcount());
}

std::cin.gcount()是从输入流中读取的字符数。它是sizeof(x1.name)更可靠的替代方法,因为它返回写入的字符数,而不是分配的字符数。

现在,双向文件流有点棘手。他们以正确的方式进行协调。如其他答案中所述,双向文件流(或std::fstream s)共享输入和输出的联合缓冲区。标记输入和输出序列中位置的位置指示器都受到可能发生的任何输入和输出操作的影响。因此,文件流位置必须被移动&#34;在执行输入之前返回。这可以通过致电seekg()seekp()来完成。要么就足够了,因为正如我所说,位置指标是相互约束的:

if (std::cin.getline(x1.pass, sizeof(x1.pass))) {
    inout.write(reinterpret_cast<char*>(&x1.pass), std::cin.gcount());
    inout.seekg(0, std::ios_base::beg);
}

注意在提取到x1.pass后如何完成此操作。我们无法在x1.name之后执行此操作,因为我们会在第二次调用write()时覆盖该流。


正如您所看到的,提取到原始C风格的数组并不漂亮,您必须管理比您应该做的更多的事情。幸运的是,C ++使用标准字符串类std::string来解决问题。使用它来实现更高效的I / O:

同时生成namepass标准C ++字符串(std::string)而不是原始C数组。这允许您将大小作为第二个参数传递给read()write()来电:

#include <string>

struct x {
    std::string name;
    std::string pass;
};

// ...
if (std::cin >> x1.name) {
    inout.write(x1.name.data(), x1.name.size());
}

if (std::cin >> x1.pass) {
    inout.write(x1.name.data(), x1.name.size());
    inout.seekg(0, std::ios_base::beg);
}

std::string允许我们利用其动态特性及其维持缓冲区大小的能力。我们不再需要使用getline(),而是现在只需拨打operator>>()if()支票即可。

以前这是不可能的,但是现在我们正在使用std::string,我们也可以结合两种提取来实现以下目标:

if (std::cout << "Enter your name: " && std::cin >> x1.name &&
    std::cout << "Enter your pass: " && std::cin >> x1.pass) {
    inout.write(x1.name.data(), x1.name.size());
    inout.write(x1.pass.data(), x1.pass.size());
    inout.seekg(0, std::ios_base::beg);
}

最后,最后一次提取就是这样:

while (inout >> x2.name)
{
    std::cout << x2.name;
}