Reader类实现中的文件读取错误



我正在尝试为我自己喜欢的编程语言实现一个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

以前,我为Readerread函数使用了一个非常简单的占位符。它基本上使用istreambuf_titerator 复制了文件中的所有内容

void Reader::read(String const &filename)
{
val_.clear();
success = true; // success flag, true by default
auto file = std::ifstream{filename};
if(!file)
{
log_error(Error{Error::Type::ReadError, "Could not open file"});
success = false;
}
val_.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
// Read entire file into val_
}

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

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

// 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函数的两个版本的输出,两者(显然(具有完全相同的输出。注意,我实际上并没有检查字符串的相等性,但它们看起来是一样的。我试过很多次找出错误的原因,但都失败了。。。。

这是我为阅读器使用的单元测试(使用GoogleTest(:

#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);
}

正如我之前提到的,它在第一个占位符版本中运行得很好,但实际的read函数似乎没有通过测试。。。。奇怪的是,样本文件甚至没有任何注释,这是阅读器读取的样本文件:

Int test = 0;
String test2 = "abcdefgh";
Float test3 = 0.9876;

(是的,确实如此。正如我之前提到的,读者正在阅读的语言不是C++,而是我正在阅读的一种自制语言,但这可能与这个问题无关(。

哦,如果你需要编译它,你需要实现和定义errhandler(errhandler.hh和errhandler.cc(,我也会把它们放在这里:

声明(errhandler.hh(:

// 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(:

// 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标头,其中包含实用程序

// 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

这个问题真的使我在这个项目上停滞不前。帮助我解决它将非常有帮助

所以,评论中的人真的帮助了我,并建议我使用>>从流中读取字符,而不是eof((和get((,因为eof(((不可靠。。但即使这样也没能解决问题。在我通过谷歌搜索自己发现之前,我不得不使用std::noskipws,以使>>运算符不跳过空白,然后它就工作了。谢谢你的帮助,我真的很感激

最新更新