コンテンツにスキップ

BinaryFileReader

  • std::ifstream を使って、バイナリファイルを読み込むクラスを実装する。
BinaryFileReader.hpp
#pragma once
#include <memory> // std::shared_ptr, std::addressof
#include <string_view> // std::string_view
#include <string> // std::string
#include <type_traits> // std::is_trivially_copyable_v
#include "Common.hpp"

namespace seccamp
{
	/// @brief バイナリファイルを読み込むクラス
	class BinaryFileReader
	{
	public:

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

		/// @brief ファイルをオープンします。
		/// @param path ファイルパス
		[[nodiscard]]
		explicit BinaryFileReader(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 ファイルのサイズ(バイト)を返します。
		/// @return ファイルのサイズ(バイト)。ファイルがオープンされていない場合は 0
		[[nodiscard]]
		int64 size() const noexcept;

		/// @brief ファイルからデータを読み込みます。
		/// @param data 読み込んだデータを格納するバッファ
		/// @param size データのサイズ(バイト)
		/// @return 読み込んだバイト数
		int64 read(void* data, size_t size);

		/// @brief ファイルからデータを読み込みます。
		/// @tparam T 読み込むデータの型
		/// @param data 読み込んだデータを格納する変数
		/// @return 読み込んだバイト数
		template <class T>
			requires std::is_trivially_copyable_v<T>
		int64 read(T& data)
		{
			return read(std::addressof(data), sizeof(T));
		}

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

	private:

		class Impl;

		std::shared_ptr<Impl> m_pImpl;
	};
}
BinaryFileReader.cpp
#include <fstream> // std::ifstream
#include "BinaryFileReader.hpp"
#include "FileSystem.hpp"

namespace seccamp
{
	namespace
	{
		/// @brief ファイルのサイズ(バイト)を取得します。
		/// @param file ファイル
		/// @return ファイルのサイズ(バイト)
		[[nodiscard]]
		static int64 GetFileSize(std::ifstream& file)
		{
			const int64 currentPos = file.tellg();

			file.seekg(0, std::ios::end);

			const int64 size = file.tellg();

			file.seekg(currentPos);

			return size;
		}
	}

	class BinaryFileReader::Impl
	{
	public:

		Impl() = default;

		~Impl()
		{
			close();
		}

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

		bool open(const std::string_view path)
		{
			if (m_file.is_open())
			{
				close();
			}

			m_file.open(std::string{ path }, std::ios::binary);

			if (m_file.is_open())
			{
				m_fullPath = FileSystem::FullPath(path);
				
				m_size = GetFileSize(m_file);

				return true;
			}
			else
			{
				return false;
			}
		}

		void close()
		{
			m_file.close();

			m_size = 0;

			m_fullPath.clear();
		}

		[[nodiscard]]
		int64 size() const noexcept
		{
			return m_size;
		}

		[[nodiscard]]
		int64 read(void* data, const size_t size)
		{
			m_file.read(static_cast<char*>(data), size);

			return m_file.gcount();
		}

		[[nodiscard]]
		const std::string& fullPath() const noexcept
		{
			return m_fullPath;
		}

	private:

		std::ifstream m_file;

		int64 m_size = 0;

		std::string m_fullPath;
	};

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

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

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

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

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

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

	int64 BinaryFileReader::size() const noexcept
	{
		return m_pImpl->size();
	}

	int64 BinaryFileReader::read(void* data, const size_t size)
	{
		return m_pImpl->read(data, size);
	}

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

追加のチャレンジ

  • int64 getPos() / void setPos(int64 pos) を追加して、読み込み位置を取得・設定できるようにする。
  • int64 read(void* data, int64 pos, size_t size) を追加して、指定した位置からデータを読み込めるようにする。
  • int64 lookahead(void* data, size_t size) を追加して、ファイルの読み込み位置を変更せずにデータを読み込めるようにする。
  • int64 lookahead(void* data, int64 pos, size_t size) を追加して、ファイルの読み込み位置を変更せずに指定した位置からデータを読み込めるようにする。