コンテンツにスキップ

TextFileReader

  • BinaryFileReader クラスを活用して、テキストファイルからテキストを std::string として読み込むクラスを実装する。
  • UTF-8 でエンコードされたテキストを想定すればよい。
TextFileReader.hpp
#pragma once
#include <memory> // std::shared_ptr
#include <string_view> // std::string_view
#include <string> // std::string
#include "Common.hpp"

namespace seccamp
{
	/// @brief テキストファイルを読み込むクラス
	class TextFileReader
	{
	public:

		/// @brief デフォルトコンストラクタ
		[[nodiscard]]
		TextFileReader();

		/// @brief ファイルをオープンします。
		/// @param path ファイルパス
		[[nodiscard]]
		explicit TextFileReader(std::string_view path);

		/// @brief ファイルがオープンされているかを返します。
		/// @return オープンされている場合 true, それ以外の場合は false
		[[nodiscard]]
		bool isOpen() const noexcept;

		/// @brief ファイルがオープンされているかを返します。
		/// @return オープンされている場合 true, それ以外の場合は false
		[[nodiscard]]
		explicit operator bool() const noexcept;

		/// @brief ファイルをオープンします。すでにオープンされている場合はクローズしてから再オープンします。
		/// @param path ファイルパス
		/// @return オープンに成功した場合 true, それ以外の場合は false
		bool open(std::string_view path);

		/// @brief ファイルをクローズします。
		void close();

		/// @brief ファイルから 1 行読み込みます。
		/// @param line 読み込んだ文字列の格納先
		/// @return 読み込みに成功した場合 true, それ以外の場合は false
		bool readLine(std::string& line);

		/// @brief ファイルの絶対パスを返します。
		/// @return ファイルの絶対パス。ファイルがオープンされていない場合は空文字列
		[[nodiscard]]
		const std::string& fullPath() const noexcept;

	private:

		class Impl;

		std::shared_ptr<Impl> m_pImpl;
	};
}
TextFileReader.cpp
#include "TextFileReader.hpp"
#include "BinaryFileReader.hpp"

namespace seccamp
{
	class TextFileReader::Impl
	{
	public:

		Impl() = default;

		~Impl()
		{
			close();
		}

		[[nodiscard]]
		bool isOpen() const noexcept
		{
			return m_binaryFileReader.isOpen();
		}

		bool open(const std::string_view path)
		{
			return m_binaryFileReader.open(path);
		}

		void close()
		{
			m_binaryFileReader.close();
		}

		bool readLine(std::string& line)
		{
			line.clear();

			int32 readBytes = 0;

			for (;;)
			{
				char c = '\0';

				// もし 1 バイト読み込めなかった場合
				if (m_binaryFileReader.read(&c, 1) == 0)
				{
					if (readBytes == 0)
					{
						// EOF
						return false;
					}
					else
					{
						break;
					}
				}

				++readBytes;

				if (c == '\r')
				{
					// CR の場合は無視
					continue;
				}
				else if (c == '\n')
				{
					// LF の場合は一行読み込み完了
					break;
				}

				line.push_back(c);
			}

			return true;
		}

		const std::string& fullPath() const noexcept
		{
			return m_binaryFileReader.fullPath();
		}

	private:

		BinaryFileReader m_binaryFileReader;
	};

	TextFileReader::TextFileReader()
		: m_pImpl{ std::make_shared<Impl>() } {}

	TextFileReader::TextFileReader(const std::string_view path)
		: TextFileReader{} // 移譲コンストラクタ
	{
		m_pImpl->open(path);
	}

	bool TextFileReader::isOpen() const noexcept
	{
		return m_pImpl->isOpen();
	}

	TextFileReader::operator bool() const noexcept
	{
		return m_pImpl->isOpen();
	}

	bool TextFileReader::open(const std::string_view path)
	{
		return m_pImpl->open(path);
	}

	void TextFileReader::close()
	{
		m_pImpl->close();
	}

	bool TextFileReader::readLine(std::string& line)
	{
		return m_pImpl->readLine(line);
	}

	const std::string& TextFileReader::fullPath() const noexcept
	{
		return m_pImpl->fullPath();
	}
}

追加のチャレンジ

  • UTF-8 BOM にも対応する。
  • BinaryFileReader の追加のチャレンジを実装すると、BOM のチェックを open() 時に簡単に行える。
  • 一定のサイズのバッファを用意してテキストを読み込むことでオーバーヘッドを抑える実装も考えられる。