【问题标题】:File output inconsistency between GCC and MSVCGCC 和 MSVC 之间的文件输出不一致
【发布时间】:2016-06-18 23:19:54
【问题描述】:

考虑以下类,它是一个简单的文件加载类,用于通过在构造过程中将数据加载到内存中来操作小文本文件。

#include <fstream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>

class file_loader {
public:
    /**
     * \brief Constructs a file_loader instance using a given filename. Caches the file contents
     *        into the internal cached storage container.
     *
     * \param _filename Name/directory of file to load.
     * \param _max_line_length Optional, approximate maximum length of file lines.
     */
    file_loader(const std::string& _filename, std::size_t _max_line_length = static_cast<std::size_t>(256)) : fs(_filename), filename(_filename) {
        cache_contents(_max_line_length);
    }
    /**
     * \brief Deleted copy construction, copy constructing is forbidden.
     */
    file_loader(const file_loader& _other) = delete;
    /**
     * \brief Move constructor, moves a given file_loader instance to this leaving the
     *        parameterised instance in a valid but unspecified state.
     *
     * \param _other rvalue reference to a file_loader instance to move.
     */
    file_loader(file_loader&& _other) : fs(std::move(_other.fs)), filename(std::move(_other.filename)),
        cached_contents_vec(std::move(_other.cached_contents_vec)) { }

    /**
     * \brief Writes all changes made to the internal cached storage to the 
     *        filestream, overwriting the current contents of the file.
     */
    void write_changes() {
        fs.close();
        fs.open(filename);
        fs.clear();
        write_cache();
    }
    /**
     * \brief Reads a given line of the internal cached storage. This internal
     *        store is guaranteed to always be up to date, no call to write_changes
     *        is required to maintain consistency.
     *
     * \param _n Line number to read.
     * \return const reference to std::string instance given by _n'th line.
     * \throws Throws std::out_of_range exception if _n exceeds lines in internal cached storage.
     */
    const std::string& read_line(std::size_t _n) const {
        if (_n >= cached_contents_vec.size())
            throw std::out_of_range("File: " + filename + " does not have " + std::to_string(_n) + " lines.");
        return cached_contents_vec[_n];
    }

    /**
     * \brief Overwrites a given line of the internal cached storage such that
     *        the next call to write_changes will update the file contents.
     * 
     * \param _n Line number to overwrite.
     * \param _str std::string instance to overwrite current line contents with.
     * \throws Throws std::out_of_range exception if _n exceeds lines in internal cached storage.
     */
    void overwrite_line(std::size_t _n, const std::string& _str) {
        if (_n >= cached_contents_vec.size())
            throw std::out_of_range("File: " + filename + " does not have " + std::to_string(_n) + " lines.");
        cached_contents_vec[_n] = _str;
    }
    /**
     * \brief Erases a given line of the internal cached storage such that
     *        the next call to write_changes will update the file contents.
     *
     * \param _n Line number to erase.
     * \return Iterator to next valid position in internal cached storage container.
     */
    auto erase_line(std::size_t _n) {
        return cached_contents_vec.erase(cached_contents_vec.begin()+_n);
    }

    /**
     * \brief Deleted copy assignment operator, copy assignment is forbidden.
     */
    file_loader& operator=(const file_loader& _other) = delete;
    /**
     * \brief Move assignment operator, uses move-semantics to move the parameterised
     *        file_loader instance to this. Instance being moved is left in a
     *        valid but unspecified state.
     *
     * \param _other rvalue reference to file_loader instance.
     */
    file_loader& operator=(file_loader&& _other) {
        // check for self-assignment
        if (this != &_other) {
            fs = std::move(_other.fs);
            filename = std::move(_other.filename);
            cached_contents_vec = std::move(_other.cached_contents_vec);
        }
        return *this;
    }
private:
    std::fstream fs;
    std::string filename;
    std::vector<std::string> cached_contents_vec;   // internal cached storage container

    /**
     * \brief Caches contents of files into the internal cached storage container.
     *
     * \param _max_line_length Approximate maximum line length of the file, used for performance improvements.
     */
    void cache_contents(std::size_t _max_line_length) {
        std::string line_str;
        // reserve space for performance
        line_str.reserve(_max_line_length);
        while (std::getline(fs, line_str)) {
            cached_contents_vec.push_back(line_str);
        }
    }

    /**
     * \brief Convenience method for writing a std::vector<std::string> to an std::ostream instance.
     *
     * \param _os Instance of std::ostream. 
     * \param _vec Instance of std::vector<std::string> to write to _os.
     * \return Reference to _os modified with data of _vec.
     */
    void write_cache() {
        for (const auto& x : cached_contents_vec) {
            fs << x << "\n";
        }
    }

};

通过GCC (6.1.0)MSVC (2015) 执行时,使用此类似乎会产生不同的结果。假设我有以下文本文件 (test.txt):

cheese
ham
eggs
beef
bacon

还有以下main

int main(void) {
    file_loader fl("test.txt");
    fl.overwrite_line(3, "chicken");
    fl.write_changes();
}

MSVC 中,它产生了我预期的结果:

cheese
ham
eggs
chicken
bacon

但是在GCC (6.1.0) 我得到:

cheese
ham
eggs
chickenbacon

换句话说,后者似乎将被覆盖的行和下面的行“连接”到cached_contents_vec 的单个条目中,而前者将条目分开(据我所知,它们应该是在这案例)。

有什么想法吗?我还应该提到,在file_loader::write_changes() 中,MSVC 不需要此方法中的两个初始行来覆盖以前的文件内容,而 GCC 则需要。

【问题讨论】:

  • 使用std::endl 而不是'\n'
  • @πάνταῥεῖ &lt;&lt; std::endl 等同于 &lt;&lt; '\n' &lt;&lt; std::flush。它产生相同的输出。
  • 不,没有这样的差异。 '\n' 已经是您似乎认为 endl 的可移植换行抽象。
  • 我的猜测是,GCC 附带的标准库实现不会在输出时将 \n 转换为 \r\n(而我知道 MSVC 库会这样做)。换句话说,GCC 构建的程序产生 Unix 风格的行尾,而 MSVC 构建的程序产生 Windows 风格的行尾。我敢打赌,GCC 输出中的“鸡”和“培根”之间有一个换行符,但无论您使用什么文本查看器(记事本,有机会吗?)都不会单独将换行视为行尾指示符。
  • 有什么问题?培根鸡肉听起来很好吃。

标签: c++ gcc visual-c++


【解决方案1】:

正如@IgorTandetnik 评论的那样,这里的问题在于 GCC 产生 Unix 风格的行尾,而 MSVC 产生基于 Windows 的行尾。在简单的文本编辑器(例如记事本)中查看输出时,来自 GCC 的 Unix 风格的输出不会被视为行尾指示符,而在“更好”的编辑器(例如 notepad++)中打开时,它会被正确处理,从而产生相同的输出两种情况。

【讨论】:

    猜你喜欢
    • 2021-08-22
    • 1970-01-01
    • 2014-04-11
    • 2016-08-18
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多