C++ 读取器类实现中的文件读取错误

C++ 读取器类实现中的文件读取错误,c++,C++,我正在尝试为我自己的爱好编程语言实现一个Reader类。读取器的工作非常简单,读取源文件并删除注释 以下是读取器类的定义: // reader.hh // Contains Reader Class specifications #ifndef PHI_SRC_FRONTEND_READER_HH #define PHI_SRC_FRONTEND_READER_HH #include "errhandler.hh" class Reader { public: Reader() =

我正在尝试为我自己的爱好编程语言实现一个
Reader
类。读取器的工作非常简单,读取源文件并删除注释

以下是
读取器
类的定义:

// reader.hh
// Contains Reader Class specifications

#ifndef PHI_SRC_FRONTEND_READER_HH
#define PHI_SRC_FRONTEND_READER_HH

#include "errhandler.hh"

class Reader
{
public:
    Reader() = default;

    auto val() const -> String const & { return val_; }

    void read(String const &filename);

    explicit operator bool() const { return success; }
    auto operator!() const -> bool { return !success; }

    friend auto operator==(Reader const &lhs, Reader const &rhs) -> bool
    {
        return lhs.val_ == rhs.val_;
    }

    friend auto operator!=(Reader const &lhs, Reader const &rhs) -> bool
    {
        return lhs.val_ != rhs.val_;
    }

    friend auto operator<<(std::ostream &stream, Reader const &read) -> std::ostream &
    {
        return stream << read.val_;
    }

private:
    String val_;
    bool success;
};

#endif
这很好,通过了单元测试,我也手动检查了输出,这也很好

但这只是一个占位符,实际的
读者需要删除注释。这让我实现了这一点:

// reader.cc
// Contains Reader Class Implementation
// Work In Progress, placeholders being used for now

#include <fstream>
#include <sstream>
#include "reader.hh"

void Reader::read(String const &filename)
{
    val_.clear();
    success = true; // success flag, true by default

    auto inStringLiteral = false;
    // Variable to determine if the reader is currently reading a string literal
    // (enclosed in double quotes)
    // In order to not mistake '//' inside literals as comments"

    auto file = std::ifstream{filename};

    if(!file)
    {
        log_error(Error{Error::Type::ReadError, "Cannot open file: " + filename});
        success = false;
        return;
    }

    for (unsigned char c; file >> c; )
    {
        // ASCII characters only use 7 bits, which is up to 127
        // So value of an ascii char must be lesser than 128
        if (c < 128)
        {
            if(c == '"')
            {
                inStringLiteral = !inStringLiteral; // flip the value of the boolean
            }

            if(!inStringLiteral && c == '/')
            {
                // If we're not inside a string literal enclosed in quotes, and find a backslash
                // Peek at the next character to check if it is a backslash
                // In case two consecutive backslashes are found, treat it as a comment and
                // ignore everything until the end of line
                if(file >> c)
                {
                    if(c == '/')
                    {
                        // keep reading until a newline is found
                        while(file >> c && c != '\n')
                        {
                        }
                    }

                    else
                    {
                        c = '/';
                        file.unget();
                    }
                }

                else
                {
                    c = '/';
                }
            }

            val_ += c;
        }

        else
        {
            log_error(Error{Error::Type::ReadError, "Unrecognized character(s) found in file: " + filename});
            success = false;
            return;
        }
    }
}
正如我前面提到的,它在第一个占位符版本中运行得非常好,但是实际的
read
函数似乎没有通过测试。。。。奇怪的是,示例文件甚至没有任何注释,以下是读者阅读的示例文件:

(是的,这是字面意思。正如我之前提到的,读者阅读的语言不是C++,而是我正在阅读的自制语言,但这可能与这个问题无关)。 哦,如果您需要编译这个,您需要了解errhandler(errhandler.hh和errhandler.cc)的实现和定义,我也将它们放在这里:

声明(errhandler.hh):

//errhandler.hh
//包含Phi错误处理规范
//虽然基本上已经完成,但仍有可能进行一些小的修改
#ifndef PHI_SRC_前端_ERRHANDLER_HH
#定义PHI_SRC_前端_ERRHANDLER_HH
#包括
#包括“utils.hh”
类错误
{
公众:
枚举类类型:无符号字符
{
ReadError、LexError、ParseError、SemanticerError、解释器Error
};
Error()=删除;
错误(Type类型,String const&val类型):Type类型(Type类型),val类型(val类型){
自动类型()常量->类型{返回类型}
auto val()const->String{return val_;}
友元自动运算符错误&{返回错误[索引];}
自动运算符[](大小索引)常量->错误常量&{返回错误[索引];}
自动开始()->迭代器{返回错误。开始();}
自动结束()->迭代器{返回错误。结束();}
自动cbegin()常量->常量迭代器{返回错误.cbegin();}
auto cend()const->const_迭代器{return errors.cend();}
auto rbegin()->reverse_迭代器{返回错误.rbegin();}
自动rend()->反向迭代器{返回错误。rend();}
自动crbegin()->const_reverse_迭代器{返回错误。crbegin();}
auto crend()->const_reverse_迭代器{return errors.crend();}
友邦汽车运营商(基地);
底部变窄\u底部=静态\u投射(目标);
如果(基数==缩小的基数)
回报目标;
抛出(错误的\u狭窄\u投射)((字符串()+“从类型缩小对话无效”+
typeid(Target.name()+”到type“+typeid(Base.name()).c_str());
}
#恩迪夫

这个问题确实阻碍了我在项目上的进展。帮助我解决这个问题将非常有帮助

因此,评论中的人确实帮助了我,并建议我使用>>来读取流中的字符,而不是eof()和get()。。但即使这样也没有解决问题。在我通过谷歌搜索自己找到答案之前,我不得不使用std::noskipws,以使>>操作符不跳过空格,然后它就工作了。谢谢你的帮助,我真的很感激

我可以推荐一种叫做调试的技术吗。我们的目标是直观地检查看起来相同但不相关的字符串字节。可能相关:@rustyx我会这样做,但即使这样,也可能无法解释为什么它没有通过单元测试。我不明白为什么代码会有不同的输出,它们应该做完全相同的事情,因为文件甚至没有comments@IgorTandetnik哦,哇,我不知道,我现在要试试,非常感谢你的通知me@uneven_mark这是一个原始字符串,我使用的是原始字符串的分隔符。。。。
// reader.cc
// Contains Reader Class Implementation
// Work In Progress, placeholders being used for now

#include <fstream>
#include <sstream>
#include "reader.hh"

void Reader::read(String const &filename)
{
    val_.clear();
    success = true; // success flag, true by default

    auto inStringLiteral = false;
    // Variable to determine if the reader is currently reading a string literal
    // (enclosed in double quotes)
    // In order to not mistake '//' inside literals as comments"

    auto file = std::ifstream{filename};

    if(!file)
    {
        log_error(Error{Error::Type::ReadError, "Cannot open file: " + filename});
        success = false;
        return;
    }

    for (unsigned char c; file >> c; )
    {
        // ASCII characters only use 7 bits, which is up to 127
        // So value of an ascii char must be lesser than 128
        if (c < 128)
        {
            if(c == '"')
            {
                inStringLiteral = !inStringLiteral; // flip the value of the boolean
            }

            if(!inStringLiteral && c == '/')
            {
                // If we're not inside a string literal enclosed in quotes, and find a backslash
                // Peek at the next character to check if it is a backslash
                // In case two consecutive backslashes are found, treat it as a comment and
                // ignore everything until the end of line
                if(file >> c)
                {
                    if(c == '/')
                    {
                        // keep reading until a newline is found
                        while(file >> c && c != '\n')
                        {
                        }
                    }

                    else
                    {
                        c = '/';
                        file.unget();
                    }
                }

                else
                {
                    c = '/';
                }
            }

            val_ += c;
        }

        else
        {
            log_error(Error{Error::Type::ReadError, "Unrecognized character(s) found in file: " + filename});
            success = false;
            return;
        }
    }
}
#include <gtest/gtest.h>
#include "frontend/reader.hh"

TEST(ReaderTest, BaseTestCase)
{
    auto TestReader = Reader{};
    auto const ExpectedOutput = String{
R"delim(Int test = 0;
String test2 = "abcdefgh";
Float test3 = 0.9876;
)delim"};

    TestReader.read("TestFiles/ReaderTest_BaseTestCase.phi");

    ASSERT_FALSE(!TestReader);

    ASSERT_EQ(TestReader.val(), ExpectedOutput);
    // If Reader Base Test Case fails, no need to continue next tests
}

TEST(ReaderTest, Should_Fail_When_FileDoesNotExist)
{
    auto TestReader = Reader{};
    TestReader.read("Non_existent_test_file.txt");
    EXPECT_TRUE(!TestReader);
}
Int test = 0;
String test2 = "abcdefgh";
Float test3 = 0.9876;
    // errhandler.hh
    // Contains Phi Error Handling specifications
    // Mostly complete, minor changes still might be made though

    #ifndef PHI_SRC_FRONTEND_ERRHANDLER_HH
    #define PHI_SRC_FRONTEND_ERRHANDLER_HH

    #include <iostream>
    #include "utils.hh"

    class Error
    {
    public:
        enum class Type : unsigned char
        {
            ReadError, LexError, ParseError, SemanticError, InterpretError
        };

        Error() = delete;
        Error(Type type__, String const &val__) : type_(type__), val_(val__) {}

        auto type() const -> Type { return type_;  }
        auto val() const -> String { return val_; }

        friend auto operator<<(std::ostream &stream, Error const &error) -> std::ostream&
        {
            return stream << error.val();
        }

    private:
        Type type_;
        String val_;
    };

    class ErrorLog
    {
    public:
        using iterator = Vector<Error>::iterator;
        using const_iterator = Vector<Error>::const_iterator;
        using reverse_iterator = Vector<Error>::reverse_iterator;
        using const_reverse_iterator = Vector<Error>::const_reverse_iterator;

        void push(Error const &error) { errors.push_back(error); }
        void pop() { errors.pop_back(); }

        auto size() const -> Size { return errors.size(); }

        auto operator[](Size index) -> Error& { return errors[index]; }
        auto operator[](Size index) const -> Error const& { return errors[index]; }

        auto begin() -> iterator { return errors.begin(); }
        auto end() -> iterator { return errors.end(); }

        auto cbegin() const -> const_iterator { return errors.cbegin(); }
        auto cend() const -> const_iterator { return errors.cend(); }

        auto rbegin() -> reverse_iterator { return errors.rbegin(); }
        auto rend() -> reverse_iterator { return errors.rend(); }

        auto crbegin() -> const_reverse_iterator { return errors.crbegin(); }
        auto crend() -> const_reverse_iterator { return errors.crend(); }

        friend auto operator<<(std::ostream &stream, ErrorLog const &error_log) -> std::ostream&
        {
            for (Size i = 0; i < error_log.size(); i++)
                stream << error_log[i];

            return stream;
        }

    private:
        Vector<Error> errors;
    };

    void log_error(Error const &error);
    void show_errors(std::ostream& stream);

    extern ErrorLog errlog;
    // The global error log to be used by every part of the Phi system
    // To be declared in main()

    #endif
// errhandler.cc
// Contains Phi Error Handling implementation
// Work In Progress, placeholders are temporarily being used

#include "errhandler.hh"

void log_error(Error const& error)
{
    errlog.push(error);
}

void show_errors(std::ostream& stream)
{
    stream << errlog;
}
// utils.hh
// Contains globally used utility functions, templates, using declarations, etc.

#ifndef PHI_SRC_UTILS_HH
#define PHI_SRC_UTILS_HH

#include <string>
#include <vector>
#include <limits>

#define UNUSED(x) (void)x

template <typename T>
using Vector = std::vector<T>;
using String = std::string;
using Size = std::size_t;

class bad_narrow_cast : public std::bad_cast
{
public:
    bad_narrow_cast(const char* message) : what_(message)
    {
    }

    char const *what() { return what_; }

private:
    char const *what_;
};

template <typename Target, typename Base> static inline
typename std::enable_if<std::numeric_limits<Target>::is_specialized,
Target>::type narrow_cast(Base const &base)
{
    if(base > static_cast<Base>(std::numeric_limits<Target>::max()) ||
        base < static_cast<Base>(std::numeric_limits<Target>::min()))
    {
        throw(bad_narrow_cast((String() + "Invalid narrowing conversation from type " +
        typeid(Target).name() + " to type " + typeid(Base).name()).c_str()));
    }

    return static_cast<Target>(base);
}

template <typename Target, typename Base> static inline
typename std::enable_if<!std::numeric_limits<Target>::is_specialized,
Target>::type narrow_cast(Base const &base)
{
    Target target = static_cast<Target>(base);
    Base narrowed_base = static_cast<Base>(target);

    if (base == narrowed_base)
        return target;

    throw(bad_narrow_cast((String() + "Invalid narrowing conversation from type " +
    typeid(Target).name() + " to type " + typeid(Base).name()).c_str()));
}

#endif