1

我正在寻找一种将浮点数/整数/字符串写入文件并将它们作为浮点数/整数/字符串读取的方法。(基本上读/写为 ios::binary)。

4

3 回答 3

0

最后是我自己写的。只是想与他人分享。

它可能没有经过优化,但我很难找到模仿 C# 的BinaryReaderBinaryWriter类的 C++ 代码。所以我创建了一个同时处理读写的类。

快速注意事项:

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

希望我的代码足够清晰。

于 2012-12-29T01:09:04.877 回答
0

我继承了ifstreamand ofstream:ibfstreamobfstream. 我做了一个小助手类,可以检测我正在编译/运行的机器的字节顺序。ibfstream然后我为and添加了一个标志obfstream,指示是否应该翻转原始类型中的字节。这些类还具有读取/写入基本类型和此类类型的数组的方法,必要时会翻转字节顺序。最后,我ios::binary默认为这些类设置。

我经常在一个 little-endian 机器上工作,并想编写 big-endian 文件,反之亦然。这被用在一个对各种格式的 3D 图形文件进行大量 I/O 的程序中。

于 2012-12-29T05:13:40.963 回答
0

我继承了ifstreamand ofstream:ibfstreamobfstream. 我做了一个类来检测我正在编译/运行的机器的字节顺序。ibfstream然后我为and添加了一个标志obfstream,指示是否应该翻转原始类型中的字节。这些类还具有读取/写入基本类型和此类类型的数组的方法,必要时会翻转字节顺序。

我经常在一个 little-endian 机器上工作,并想编写 big-endian 文件,反之亦然。这被用在一个程序中,该程序对各种格式的 3D 图形文件进行了大量 I/O。

于 2012-12-29T05:57:50.123 回答