아이디어
- csv 문서를 파싱해
https://jartlife.tistory.com/53
- stringstream 으로부터
https://jartlife.tistory.com/55
- tuple 을 입력받는다.
https://jartlife.tistory.com/56
database 클래스 인터페이스
- template < typename ... TupleElems >
class database< TupleElems... >
설명 : TupleElems... 를 템플릿 인자로 취하는 std::tuple 들의 나열이다.
- template < typename Str_t >
database( const Str_t file_path )
설명 : 주어진 경로의 파일(.csv)을 열어 데이터베이스를 입력받는다.
예시- 튜플의 형식이 int, int 인 "mycsvfile.csv" 입력받기
database< int, int >{ "mycsvfile.csv" }; - 튜플의 형식이 A, B, C 인 "ABCcsv.csv" 입력받기
database< A, B, C >{ "ABCcsv.csv" };
- 튜플의 형식이 int, int 인 "mycsvfile.csv" 입력받기
- decltype( auto ) get( const size_t row )
설명 : row 번째 행의 std::tuple 을 반환한다.
호출한 database 객체의 const 여부에 따라 반환되는 std::tuple 의 const 여부가 결정된다.
예시- 데이터베이스의 0번째 행( 시작 행 ) 가져오기
auto tup{ db.get( 0 ) }; - 데이터베이스의 15번째 행 가져오기
auto row{ db.get( 15 ) }; - 데이터베이스의 마지막 행 가져오기
auto tuple{ db.get( db.size() - 1 ) };
- 데이터베이스의 0번째 행( 시작 행 ) 가져오기
- const size_t size() const
설명 : 데이터베이스의 행의 갯수를 반환한다.
예시- 데이터베이스의 행의 갯수 얻어오기
const auto num_rows = db.size(); - 데이터베이스의 마지막 행 가져오기
auto row{ db.get( db.size() - 1 ) };
- 데이터베이스의 행의 갯수 얻어오기
- template < typename Str_t >
database 클래스 구현
// database.h
#ifndef _database #define _database
// ...
#include <vector>
#include <sstream>
#include <tuple>
#include "TMP.h"
#include "keyword.h"
// ...
template < typename... TupleElems >
class database NONCOPYABLE
{
public:
template < typename Str_t >
database( const Str_t file_path )
{
auto parsed_csv{ _csv::csv_read( file_path ) };
impl.reserve( 0x20 );
for ( const auto& row : parsed_csv )
{
std::tuple< TupleElems... > tp;
std::stringstream ss;
for ( const auto& field : row )
{
ss << field << ' ';
}
ss >> tp;
impl.push_back( std::move( tp ) );
}
impl.shrink_to_fit();
}
// row begins with 0
decltype( auto ) get( const size_t row ) const NOASSIGNMENT
{
return impl.at( row );
}
// row begins with 0
decltype( auto ) get( const size_t row ) NOASSIGNMENT
{
return impl.at( row );
}
// num of rows
const size_t size() const { return impl.size(); }
database() = delete;
database( const database& other ) = delete;
database& operator=( const database& other ) = delete;
private:
std::vector< std::tuple< TupleElems... > > impl;
};
#endif
|
cs |
get() 은 database 객체가 const 일 경우와 const 가 아닐 경우에 대해 오버로드되어있다.
std::vector 의 at() 도 const 와 const 가 아닐 경우에 대해서 오버로드되어 있으므로, 별다른 구분 없이 std::vector 의 at() 을 부르면 호출 객체의 const 여부에 맞게 요소가 const 화 되어 반환된다.
더보기
// database.h + TMP.h
#ifndef _database
#define _database
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <tuple>
#include "TMP.h"
#include "keyword.h"
namespace _csv
{
HELPER auto csv_read_row( std::istream& in, char delimiter )
{
std::stringstream ss;
bool inquotes = false;
bool packs = false;
std::vector< std::string > row;
while (in.good())
{
char c = in.get();
switch (c)
{
case L'"':
if ( !inquotes ) // begin quote char
{
inquotes = true;
}
else
{
if ( in.peek() == '"' ) // 2 consecutive quotes resolve to 1
ss << static_cast<char>( in.get() );
else // end quote char
inquotes = false;
}
break;
case L'{':
if ( !packs )
{
packs = true;
}
else
{
throw std::ios_base::failure{ "csv_read_row : '{' duplicated" };
}
break;
case L'}':
if ( !packs )
{
throw std::ios_base::failure{ "csv_read_row : '{' must be followed by '}'" };
}
else
{
packs = false;
}
break;
case L'\r': case L'\n':
if ( !inquotes && !packs )
{
if ( in.peek() == '\n' )
{
in.get();
}
row.push_back( ss.str() );
row.shrink_to_fit();
return row;
}
break;
default:
if ( c == delimiter && !inquotes )
{
row.push_back( ss.str() );
ss.str( "" );
}
else
{
ss << c;
}
break;
}
}
throw std::ios_base::failure{ "csv_read_row : '\\n', '\\r' not found" };
}
template < typename Str_t >
auto csv_read( const Str_t file_path )
{
std::ifstream in{ file_path };
std::vector< std::vector< std::string > > parsed;
parsed.reserve( 0x20 );
if ( in.fail() )
{
throw std::ios_base::failure{ "csv_read : cannot open file" };
}
while ( in.good() )
{
parsed.push_back( _csv::csv_read_row( in, ',' ) );
}
parsed.shrink_to_fit();
return parsed;
}
}
template < typename ... Elems, size_t ... Idx >
HELPER std::istream& istreamget_impl( std::istream& is, std::tuple< Elems... >& tup, const std::index_sequence< Idx... > )
{
int dummy[ sizeof...( Elems ) ] = { ( is >> std::get< Idx >( tup ), 0 )... };
return is;
}
template < typename ... Elems >
std::istream& operator>>( std::istream& is, std::tuple< Elems... >& tup )
{
istreamget_impl( is, tup, std::make_index_sequence< sizeof...( Elems ) >() );
return is;
}
template < typename... TupleElems >
class database NONCOPYABLE
{
public:
template < typename Str_t >
database( const Str_t file_path )
{
auto parsed_csv{ _csv::csv_read( file_path ) };
impl.reserve( 0x20 );
for ( const auto& row : parsed_csv )
{
std::tuple< TupleElems... > tp;
std::stringstream ss;
for ( const auto& field : row )
{
ss << field << ' ';
}
ss >> tp;
impl.push_back( std::move( tp ) );
}
impl.shrink_to_fit();
}
// row begins with 0
decltype( auto ) get( const size_t row ) const NOASSIGNMENT
{
return impl.at( row );
}
// row begins with 0
decltype( auto ) get( const size_t row ) NOASSIGNMENT
{
return impl.at( row );
}
// num of rows
const size_t size() const { return impl.size(); }
database() = delete;
database( const database& other ) = delete;
database& operator=( const database& other ) = delete;
private:
std::vector< std::tuple< TupleElems... > > impl;
};
#endif
|
cs |
테스트
더보기
// database.h
// ...
#include <iostream>
void test_database()
{
database<int, int, int, int, std::string> db{ "testcsv.csv" };
for ( int i = 0; i < db.size(); ++i )
{
std::cout << db.get( i ) << std::endl;
}
}
|
cs |
// database.h
// ... #include <iostream> void test_database()
{
database<int, int, int, int, std::string> db{ "testcsv.csv" };
for ( int i = 1; i <= db.size(); ++i )
{
auto tuple = db.get( i - 1 );
std::cout << i << " 행 1번째 열 : " << std::get<0>( tuple ) << std::endl;
std::cout << i << " 행 2번째 열 : " << std::get<1>( tuple ) << std::endl;
std::cout << i << " 행 3번째 열 : " << std::get<2>( tuple ) << std::endl;
std::cout << i << " 행 4번째 열 : " << std::get<3>( tuple ) << std::endl;
std::cout << i << " 행 5번째 열 : " << std::get<4>( tuple ) << std::endl;
}
}
|
cs |
'Project > Win32API로 던그리드 모작' 카테고리의 다른 글
메모리 풀, 오브젝트 풀 개념 (0) | 2021.08.25 |
---|---|
게임 저장 구현 - 구조체를 파일로 저장하기 (0) | 2021.08.24 |
std::iostream 으로 std::tuple 입출력하기 (0) | 2021.08.23 |
std::stringstream 을 통해 문자열을 값으로 (0) | 2021.08.23 |
코드 리팩토링 (0) | 2021.08.22 |