2012-12-29 60 views

回答

0

我最終自己寫了。只是想分享給別人。

它可能不是最優的,但我有一些困難,找到C++代碼,模仿C#的BinaryReader在 & 的BinaryWriter類。所以我創建了一個處理讀取和寫入的類。

快速兩點需要注意:

1)「BM」僅僅是我的課的前綴。

2)BMLogging是一個輔助類,簡單地做:

cout << "bla bla bla" << endl; 

所以,你可以無視BMLogging的電話,我讓他們在哪些方面我們可以向用戶發出警告的案件。

下面的代碼:

#include <iostream> 
#include <fstream> 

using namespace std; 

// Create the macro so we don't repeat the code over and over again. 
#define BMBINARY_READ(reader,value) reader.read((char *)&value, sizeof(value)) 

enum BMBinaryIOMode 
{ 
    None = 0, 
    Read, 
    Write 
}; 

class BMBinaryIO 
{ 
    // the output file stream to write onto a file 
    ofstream writer; 
    // the input file stream to read from a file 
    ifstream reader; 
    // the filepath of the file we're working with 
    string filePath; 
    // the current active mode. 
    BMBinaryIOMode currentMode; 

public: 
    BMBinaryIO() 
    { 
     currentMode = BMBinaryIOMode::None; 
    } 

    // the destructor will be responsible for checking if we forgot to close 
    // the file 
    ~BMBinaryIO() 
    { 
     if(writer.is_open()) 
     { 
      BMLogging::error(BMLoggingClass::BinaryIO, "You forgot to call close() after finishing with the file! Closing it..."); 
      writer.close(); 
     } 

     if(reader.is_open()) 
     { 
      BMLogging::error(BMLoggingClass::BinaryIO, "You forgot to call close() after finishing with the file! Closing it..."); 
      reader.close(); 
     } 
    } 

    // opens a file with either read or write mode. Returns whether 
    // the open operation was successful 
    bool open(string fileFullPath, BMBinaryIOMode mode) 
    { 
     filePath = fileFullPath; 

     BMLogging::info(BMLoggingClass::BinaryIO, "Opening file: " + filePath); 

     // Write mode 
     if(mode == BMBinaryIOMode::Write) 
     { 
      currentMode = mode; 
      // check if we had a previously opened file to close it 
      if(writer.is_open()) 
       writer.close(); 

      writer.open(filePath, ios::binary); 
      if(!writer.is_open()) 
      { 
       BMLogging::error(BMLoggingClass::BinaryIO, "Could not open file for write: " + filePath); 
       currentMode = BMBinaryIOMode::None; 
      } 
     } 
     // Read mode 
     else if(mode == BMBinaryIOMode::Read) 
     { 
      currentMode = mode; 
      // check if we had a previously opened file to close it 
      if(reader.is_open()) 
       reader.close(); 

      reader.open(filePath, ios::binary); 
      if(!reader.is_open()) 
      { 
       BMLogging::error(BMLoggingClass::BinaryIO, "Could not open file for read: " + filePath); 
       currentMode = BMBinaryIOMode::None; 
      } 
     } 

     // if the mode is still the NONE/initial one -> we failed 
     return currentMode == BMBinaryIOMode::None ? false : true; 
    } 

    // closes the file 
    void close() 
    { 
     if(currentMode == BMBinaryIOMode::Write) 
     { 
      writer.close(); 
     } 
     else if(currentMode == BMBinaryIOMode::Read) 
     { 
      reader.close(); 
     } 
    } 

    bool checkWritabilityStatus() 
    { 
     if(currentMode != BMBinaryIOMode::Write) 
     { 
      BMLogging::error(BMLoggingClass::BinaryIO, "Trying to write with a non Writable mode!"); 
      return false; 
     } 
     return true; 
    } 

    // Generic write method that will write any value to a file (except a string, 
    // for strings use writeString instead). 
    void write(void *value, size_t size) 
    { 
     if(!checkWritabilityStatus()) 
      return; 

     // write the value to the file. 
     writer.write((const char *)value, size); 
    } 

    // Writes a string to the file 
    void writeString(string str) 
    { 
     if(!checkWritabilityStatus()) 
      return; 

     // first add a \0 at the end of the string so we can detect 
     // the end of string when reading it 
     str += '\0'; 

     // create char pointer from string. 
     char* text = (char *)(str.c_str()); 
     // find the length of the string. 
     unsigned long size = str.size(); 

     // write the whole string including the null. 
     writer.write((const char *)text, size); 
    } 

    // helper to check if we're allowed to read 
    bool checkReadabilityStatus() 
    { 
     if(currentMode != BMBinaryIOMode::Read) 
     { 
      BMLogging::error(BMLoggingClass::BinaryIO, "Trying to read with a non Readable mode!"); 
      return false; 
     } 

     // check if we hit the end of the file. 
     if(reader.eof()) 
     { 
      BMLogging::error(BMLoggingClass::BinaryIO, "Trying to read but reached the end of file!"); 
      reader.close(); 
      currentMode = BMBinaryIOMode::None; 
      return false; 
     } 

     return true; 
    } 

    // reads a boolean value 
    bool readBoolean() 
    { 
     if(checkReadabilityStatus()) 
     { 
      bool value = false; 
      BMBINARY_READ(reader, value); 
      return value; 
     } 

     return false; 
    } 

    // reads a character value 
    char readChar() 
    { 
     if(checkReadabilityStatus()) 
     { 
      char value = 0; 
      BMBINARY_READ(reader, value); 
      return value; 
     } 
     return 0; 
    } 

    // read an integer value 
    int readInt() 
    { 
     if(checkReadabilityStatus()) 
     { 
      int value = 0; 
      BMBINARY_READ(reader, value); 
      return value; 
     } 
     return 0; 
    } 

    // read a float value 
    float readFloat() 
    { 
     if(checkReadabilityStatus()) 
     { 
      float value = 0; 
      BMBINARY_READ(reader, value); 
      return value; 
     } 
     return 0; 
    } 

    // read a double value 
    double readDouble() 
    { 
     if(checkReadabilityStatus()) 
     { 
      double value = 0; 
      BMBINARY_READ(reader, value); 
      return value; 
     } 
     return 0; 
    } 

    // read a string value 
    string readString() 
    { 
     if(checkReadabilityStatus()) 
     { 
      char c; 
      string result = ""; 
      while((c = readChar()) != '\0') 
      { 
       result += c; 
      } 
      return result; 
     } 
     return ""; 
    } 
}; 

編輯:我取代以上這些所有的讀/寫方法:(更新的使用代碼以及)

// Generic write method that will write any value to a file (except a string, 
// for strings use writeString instead) 
template<typename T> 
void write(T &value) 
{ 
    if(!checkWritabilityStatus()) 
     return; 

    // write the value to the file. 
    writer.write((const char *)&value, sizeof(value)); 
} 

// Writes a string to the file 
void writeString(string str) 
{ 
    if(!checkWritabilityStatus()) 
     return; 

    // first add a \0 at the end of the string so we can detect 
    // the end of string when reading it 
    str += '\0'; 

    // create char pointer from string. 
    char* text = (char *)(str.c_str()); 
    // find the length of the string. 
    unsigned long size = str.size(); 

    // write the whole string including the null. 
    writer.write((const char *)text, size); 
} 

// reads any type of value except strings. 
template<typename T> 
T read() 
{ 
    checkReadabilityStatus(); 

    T value; 
    reader.read((char *)&value, sizeof(value)); 
    return value; 
} 

// reads any type of value except strings. 
template<typename T> 
void read(T &value) 
{ 
    if(checkReadabilityStatus()) 
    { 
     reader.read((char *)&value, sizeof(value)); 
    } 
} 

// read a string value 
string readString() 
{ 
    if(checkReadabilityStatus()) 
    { 
     char c; 
     string result = ""; 
     while((c = read<char>()) != '\0') 
     { 
      result += c; 
     } 
     return result; 
    } 
    return ""; 
} 

// read a string value 
void readString(string &result) 
{ 
    if(checkReadabilityStatus()) 
    { 
     char c; 
     result = ""; 
     while((c = read<char>()) != '\0') 
     { 
      result += c; 
     } 
    } 
} 

這是如何使用它來寫:

string myPath = "somepath to the file"; 
BMBinaryIO binaryIO; 
if(binaryIO.open(myPath, BMBinaryIOMode::Write)) 
{ 
    float value = 165; 
    binaryIO.write(value); 

    char valueC = 'K'; 
    binaryIO.write(valueC); 

    double valueD = 1231.99; 
    binaryIO.write(valueD); 

    string valueStr = "spawnAt(100,200)"; 
    binaryIO.writeString(valueStr); 
    valueStr = "helpAt(32,3)"; 
    binaryIO.writeString(valueStr); 

    binaryIO.close(); 
} 

這裏是你將如何使用它來閱讀:

string myPath = "some path to the same file"; 
if(binaryIO.open(myPath, BMBinaryIOMode::Read)) 
{ 
    cout << binaryIO.read<float>() << endl; 
    cout << binaryIO.read<char>() << endl; 

    double valueD = 0; 
    binaryIO.read(valueD); // or you could use read<double() 
    cout << valueD << endl; 

    cout << binaryIO.readString() << endl; 
    cout << binaryIO.readString() << endl; 

    binaryIO.close(); 
} 

編輯2:你甚至可以寫/ 1線看整體結構:

struct Vertex { 
    float x, y; 
}; 

Vertex vtx; vtx.x = 2.5f; vtx.y = 10.0f; 

// to write it 
binaryIO.write(vtx); 

// to read it 
Vertex vtxRead; 
binaryIO.read(vtxRead); // option 1 
vtxRead = binaryIO.read<Vertex>(); // option 2 

希望我的代碼很清楚。

+1

1)'BMBINARY_READ'並不需要是一個宏時,它可以是一個函數模板,這應該是優選的。 '模板空隙BMBinaryRead(標準:: istream的&讀者,T&值){reader.read((字符*)值,的sizeof(值)); }' –

+1

2)使用一個名稱空間來代替前綴。 –

+0

打開閱讀然後寫,你處於一種奇怪的狀態。閱讀器功能應該是帶有字符串規範的模板。你毫無意義地拋棄const,停止使用C風格的轉換。 c_str已經是空的,但理論上也可以包含嵌入的空值 - 這是格式化設計要求嗎?因爲你可以做得更好。將pascal樣式表示爲254個字符,然後255表示2字節編碼長度,-1再次編碼但長度更長,等等。對於短字符串,讀取速度更快,並支持2^64長度的字符串。 – Yakk

0

I subclassed ifstream and ofstreamibfstream and obfstream。我做了一個輔助類,它可以檢測我正在編譯/運行的機器的字節順序。然後,我添加了一個ibfstreamobfstream的標誌,指示原始類型中的字節是否應該翻轉。這些類也有方法來讀取/寫入原始類型和這種類型的數組,以根據需要翻轉字節順序。最後,我默認爲這些類設置ios::binary

我經常在一個小端機器上工作,想寫大端文件,反之亦然。這被用在一個程序中,它使用各種格式的3D圖形文件進行了大量的I/O操作。

0

I subclassed ifstream and ofstreamibfstream and obfstream。我創建了一個能夠檢測我正在編譯/運行的機器的序列號的類。然後,我添加了一個ibfstreamobfstream的標誌,指示原始類型中的字節是否應該翻轉。這些類也有方法來讀取/寫入原始類型和這種類型的數組,以根據需要翻轉字節順序。

我經常在一個小端機器上工作,想寫大端文件,反之亦然。這在一個程序中使用了很多I/O,包括各種格式的3D圖形文件。