簡體   English   中英

使用STL算法與容器(字符*除外)之間的C ++二進制文件I / O

[英]C++ binary file I/O to/from containers (other than char *) using STL algorithms

我正在嘗試使用STL復制算法對二進制文件I / O進行簡單測試,以將數據復制到容器和二進制文件中以及從容器和二進制文件復制數據。 見下文:

 1 #include <iostream>
 2 #include <iterator>
 3 #include <fstream>
 4 #include <vector>
 5 #include <algorithm>
 6 
 7 using namespace std;
 8
 9 typedef std::ostream_iterator<double> oi_t;
10 typedef std::istream_iterator<double> ii_t;
11 
12 int main () {
13
14   // generate some data to test
15   std::vector<double> vd;
16   for (int i = 0; i < 20; i++)
17   {
18     double d = rand() / 1000000.0;
19     vd.push_back(d);
20   }
21 
22   // perform output to a binary file
23   ofstream output ("temp.bin", ios::binary);
24   copy (vd.begin(), vd.end(), oi_t(output, (char *)NULL));
25   output.close();
26 
27   // input from the binary file to a container
28   std::vector<double> vi;
29   ifstream input ("temp.bin", ios::binary);
30   ii_t ii(input);
31   copy (ii, ii_t(), back_inserter(vi));
32   input.close();
33 
34   // output data to screen to verify/compare the results
35   for (int i = 0; i < vd.size(); i++)
36     printf ("%8.4f  %8.4f\n", vd[i], vi[i]);
37 
38   printf ("vd.size() = %d\tvi.size() = %d\n", vd.size(), vi.size());
39   return 0;
40 }

結果輸出如下,並且有兩個問題afaik:

1804.2894  1804.2985
846.9309    0.9312
1681.6928    0.6917
1714.6369    0.6420
1957.7478    0.7542
424.2383    0.2387
719.8854    0.8852
1649.7605    0.7660
596.5166    0.5171
1189.6414    0.6410
1025.2024    0.2135
1350.4900    0.4978
783.3687    0.3691
1102.5201    0.5220
2044.8978    0.9197
1967.5139    0.5114
1365.1805    0.1815
1540.3834    0.3830
304.0892    0.0891
1303.4557    0.4600
vd.size() = 20  vi.size() = 20

1)每次從二進制數據中讀取double都會丟失小數點前的信息。 2)數據在小數點后三位(或更早的位置)處被處理,並且引入了一些任意錯誤。

請任何幫助將不勝感激。 (我希望有人指出我以前的帖子,因為我在搜索中很短)

對於問題1)您需要指定一個分隔符(例如空格)。 非小數部分固定在前一個數字的小數部分。 在C ++中,強制轉換和使用NULL通常是錯誤的。 應該是一個提示;)

copy (vd.begin(), vd.end(), oi_t(output, " ")); 

對於問題2)

#include <iomanip>
output << setprecision(9);

使用std :: copy()寫入二進制數據。
我會這樣做:

template<typename T>
struct oi_t: public iterator<output_iterator_tag, void, void, void, void>
{
  oi_t(std::ostream& str)
    :m_str(str)
  {}
  oi_t& operator++()   {return *this;}  // increment does not do anything.
  oi_t& operator++(int){return *this;}
  oi_t& operator*()    {return *this;}  // Dereference returns a reference to this
                                       // So that when the assignment is done we
                                       // actually write the data from this class
  oi_t& operator=(T const& data)
  {
    // Write the data in a binary format
    m_str.write(reinterpret_cast<char const*>(&data),sizeof(T));
    return *this;
  }

  private:
    std::ostream&   m_str;
};

因此,對std :: copy的調用是:

copy (vd.begin(), vd.end(), oi_t<double>(output));

輸入迭代器稍微復雜些,因為我們必須測試流的結尾。

template<typename T>
struct ii_t: public iterator<input_iterator_tag, void, void, void, void>
{
  ii_t(std::istream& str)
    :m_str(&str)
  {}
  ii_t()
    :m_str(NULL)
  {}
  ii_t& operator++()   {return *this;}  // increment does nothing.
  ii_t& operator++(int){return *this;}
  T& operator*()
  {
    // On the de-reference we actuall read the data into a local //// static ////
    // Thus we can return a reference
    static T result;
    m_str->read(reinterpret_cast<char*>(&result),sizeof(T));
    return result;
  }
  // If either iterator has a NULL pointer then it is the end() of stream iterator.
  // Input iterators are only equal if they have read past the end of stream.
  bool operator!=(ii_t const& rhs)
  {
      bool lhsPastEnd  = (m_str == NULL)     || (!m_str->good());
      bool rhsPastEnd  = (rhs.m_str == NULL) || (!rhs.m_str->good());

      return !(lhsPastEnd && rhsPastEnd);
  } 

  private:
    std::istream*   m_str;
};

現在,讀取輸入的調用為:

ii_t<double> ii(input);
copy (ii, ii_t<double>(), back_inserter(vi));

您可以使用Tristram指出的setprecision設置精度,是否需要定界符。 請參閱cppreference以查看operator=功能。 沒有設置格式,因此您需要在輸出上進行設置:

ofstream output ("temp.bin", ios::binary);
output.flags(ios_base::fixed);  //or output << fixed;
copy(vd.begin(), vd.end(), oi_t(output, " "));
output.close();

我傾向於使用fixed來消除精度問題。 在很多情況下,有人認為“我們永遠不需要超過5位數字”,因此他們在每個地方都對精度進行了硬編碼。 這些都是必須糾正的昂貴錯誤。

我為二進制I / O提出了一個更好的設計。 基本方法是具有三種方法: size_on_stream, load_from_buffer,store_to_buffer 這些進入接口類,以便所有支持二進制I / O的類都繼承它。

size_on_stream方法返回在流上傳輸的數據的大小。 通常,這不包括填充字節。 這應該是遞歸的,以便類在其所有成員上調用該方法。

load_from_buffer方法的引用傳遞給指向緩沖區的指針( unsigned char * & )。 該方法從緩沖區加載對象的數據成員,在每個成員之后增加指針(或在所有成員之后增加一次)。

store_to_buffer方法將數據存儲到給定的緩沖區中並遞增指針。

客戶端調用size_on_stream來確定所有數據的大小。 動態分配此大小的緩沖區。 指向此緩沖區的另一個指針傳遞到store_to_buffer以將對象的成員存儲到緩沖區中。 最后,客戶端使用二進制寫入(fwrite or std::ostream::write)將緩沖區傳輸到流。

此技術的一些好處是:打包,抽象和塊I / O。 對象將其成員打包到緩沖區中。 客戶端隱藏了寫入緩沖區的過程。 客戶端可以使用塊I / O功能,該功能始終比傳輸單個成員更有效。

這種設計也更便於攜帶,因為物品可以照顧Endianess。 有一個簡單的方法,由讀者自己決定。

我已經擴展了此概念以合並POD(普通舊數據)類型,這是讀者的練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM