Files
2026-06-01 12:46:52 +02:00

290 lines
4.9 KiB
C++

#pragma once
#include <string>
#include <vector>
template< typename char_t >
class csv_traits
{
public:
bool is_field_term( char_t check_char ) const;
bool is_enclosed_term( char_t check_char ) const;
bool is_line_term( char_t check_char ) const;
};
template<>
class csv_traits< char >
{
public:
enum { EOF_CHAR = 0 };
typedef std::string string_t;
typedef char char_t;
explicit csv_traits( const char_t* field = ",", char_t enclosed = '"', char_t line = '\n' )
: field_( field )
, enclosed_( enclosed )
, line_( line )
{
}
bool is_field_term( char_t check_char ) const
{
return (field_.find( check_char ) != string_t::npos);
}
bool is_enclosed_term( char_t check_char ) const
{
return (check_char == enclosed_);
}
bool is_line_term( char_t check_char ) const
{
return (check_char == line_);
}
private:
string_t field_;
char_t enclosed_;
char_t line_;
};
template<>
class csv_traits< wchar_t >
{
public:
enum { EOF_CHAR = 0 };
typedef std::wstring string_t;
typedef wchar_t char_t;
explicit csv_traits( const char_t* field = L",", char_t enclosed = L'"', char_t line = L'\n' )
: field_( field )
, enclosed_( enclosed )
, line_( line )
{
}
bool is_field_term( char_t check_char ) const
{
return (field_.find( check_char ) != string_t::npos);
}
bool is_enclosed_term( char_t check_char ) const
{
return (check_char == enclosed_);
}
bool is_line_term( char_t check_char ) const
{
return (check_char == line_);
}
private:
string_t field_;
char_t enclosed_;
char_t line_;
};
template< typename char_t >
class csv_stream
{
public:
virtual bool eof() = 0;
virtual char_t get() = 0;
virtual char_t seek_next() = 0;
};
template< typename char_t >
class csv_string_stream : public csv_stream< char_t >
{
public:
csv_string_stream( const char_t* string, size_t len )
: string_( string )
, len_( len )
, pos_( 0 )
{
}
csv_string_stream( typename const csv_traits< char_t >::string_t& string )
: string_( string.c_str() )
, len_( string.size() )
, pos_( 0 )
{
}
virtual bool eof()
{
return (len_ <= pos_);
}
virtual char_t get()
{
char_t temp = get( pos_ );
if( temp != csv_traits< char_t >::EOF_CHAR )
{
++pos_;
}
return temp;
}
virtual char_t seek_next()
{
return get( pos_ );
}
private:
virtual char_t get( size_t pos )
{
char_t temp = csv_traits< char_t >::EOF_CHAR;
if( pos < len_ )
{
temp = string_[pos];
}
return temp;
}
private:
const char_t* string_;
size_t len_;
size_t pos_;
};
template< typename char_t >
class csv_parser
{
public:
typedef std::vector< typename csv_traits< char_t >::string_t > csv_row_t;
static csv_row_t get_row( csv_stream< char_t >& stream, const csv_traits< char_t >& traits )
{
csv_row_t row;
get_row( row, stream, traits );
return row;
}
static void get_row( csv_row_t& row, csv_stream< char_t >& stream, const csv_traits< char_t >& traits )
{
enum
{
PARSE_NOMAL,
PARSE_ENCLOSE,
};
int state = PARSE_NOMAL;
csv_traits< char_t >::string_t field;
while( true )
{
char_t curr_char = stream.get();
if( curr_char == csv_traits< char_t >::EOF_CHAR )
{
if( field.empty() == false )
{
row.push_back( field );
}
break;
}
if( state == PARSE_ENCLOSE ) // 문자열이 닫히기전
{
if( traits.is_enclosed_term( curr_char ) == true )
{
char_t escaped_char = get_escape_char( stream, traits );
if( escaped_char != csv_traits< char_t >::EOF_CHAR )
{
field.push_back( escaped_char );
continue;
}
//문자열 끝
state = PARSE_NOMAL;
continue;
}
// 무조건 넣는다.
field.push_back( curr_char );
}
else
{
if( traits.is_enclosed_term( curr_char ) == true ) // 처음 enclosed 문자가 왔을 때 문자열 시작
{
if( field.empty() )
{
state = PARSE_ENCLOSE;
}
else
{
// 뜬금없이 enclosed_ 이 왔을 경우
// escape 상황일때
char_t escaped_char = get_escape_char( stream, traits );
if( escaped_char != csv_traits< char_t >::EOF_CHAR )
{
field.push_back( escaped_char );
continue;
}
// escape 상황이 아니면 그냥 추가
field.push_back( curr_char );
}
}
else if( traits.is_field_term( curr_char ) == true )
{
// field 완성
row.push_back( field );
field.clear();
}
else if( traits.is_line_term( curr_char ) == true )
{
// row 완성
row.push_back( field );
field.clear();
break;
}
else
{
field.push_back( curr_char );
}
}
}
}
private:
static char_t get_escape_char( csv_stream< char_t >& stream, const csv_traits< char_t >& traits )
{
char_t next_char = stream.seek_next();
if( traits.is_enclosed_term( next_char ) == true )
{
// 탈출문자
return stream.get();
}
return csv_traits< char_t >::EOF_CHAR;
}
};