【问题标题】:I'd like to use ifstream and ofstream in C++ to mimic C#'s BinaryReader / BinaryWriter functionality我想在 C++ 中使用 ifstream 和 ofstream 来模仿 C# 的 BinaryReader / BinaryWriter 功能
【发布时间】:2012-12-29 01:09:04
【问题描述】:

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

【问题讨论】:

    标签: c++ ifstream ofstream binaryreader binarywriter


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我将ifstreamofstream 子类化为:ibfstreamobfstream。我做了一个类来检测我正在编译/运行的机器的字节顺序。然后我为ibfstreamobfstream 添加了一个标志,指示是否应该翻转原始类型中的字节。这些类还具有读取/写入基本类型和此类类型的数组的方法,根据需要翻转字节顺序。

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

      【讨论】:

        【解决方案3】:

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

        它可能没有经过优化,但我很难找到模仿 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
        

        希望我的代码足够清晰。

        【讨论】:

        • 1) BMBINARY_READ 不需要是宏,可以是函数模板,应该是首选。 template&lt;typename T&gt; void BMBinaryRead(std::istream&amp; reader, T&amp; value){ reader.read((char*)&amp;value, sizeof(value)); }
        • 2) 使用命名空间而不是前缀。
        • 打开读取然后写入,您处于一种奇怪的状态。阅读器函数应该是带有字符串规范的模板。你毫无意义地抛弃了 const,停止使用 C 风格的强制转换。 c_str 已经以空值终止,但理论上也可以包含嵌入的空值——这种格式是设计要求吗?因为你可以做得更好。将 pascal 样式说成 254 个字符,然后 255 表示 2 字节编码长度,再用 -1 编码但长度更长,等等。就像短字符串一样紧凑,读取速度更快,并支持 2^64 长度的字符串。
        • @BenjaminLindley:感谢您的注意,我将编辑我的答案以发布新方法,以取代以前的 readBool/Int/...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多