십수년 전에.. 개발자로 취업을 해서 초기에 델파이, PHP, C/C++ 을 실무에서 사용하다가 저를 가르쳐주신 사수 차장님의 반강제(?)적인 권유로 인해 만들어 본 PacketParser Class 입니다.
보시기에 부족한 부분이 많겠지만 너그럽게 봐주시기 바랍니다 (__)
( 너무 오래 전에 만들어서 기억도 못 했는데 최근에 프로젝트를 진행했던 소스들을 보다가 문득 발견하게 되어서 찾은 기념으로 한번 올려봅니다...)
클래스의 기능은 사용자가 지정한 구분자(Delimeter)를 기준으로 문자열을 Parsing 하여 문자, 숫자 등의 데이터로 각 데이터값을 순차적 또는 인덱스를 지정하여 값을 확인할 수 있습니다.
어찌 보면 살짝(?) 부족한 문자열 클래스라고 볼수도 있겠네요. 다행히 문자열 클래스보다 메모리는 덜 잡아 먹겠네요. 하..
클래스 명은 CPacketParser 입니다.
CPakcetParser.h(헤더 파일)
헤더 파일 소스 입니다.
#pragma once
#include <iostream>
#define BUFSIZE 8192
#define BASE_DELIMETER ','
class CPacketParser
{
private :
int m_nIndex; // current Index
int m_nMaxSize; // After Parsing Data Size
char m_chDel; // Delimeter
char m_chEnd; // String End(NULL)
char m_sData[BUFSIZE]; // Base Data
std::vector<char *> m_arrDatas; // After Parsing Data
public :
CPacketParser(char chDelimeter = BASE_DELIMETER);
virtual ~CPacketParser();
int GetSize();
bool IsEmpty();
void Clear();
int Find(LPSTR sValue);
byte GetByteValue();
short GetShortValue();
int GetIntValue();
long GetLongValue();
float GetFloatValue();
double GetDoubleValue();
LPSTR GetStringValue();
char GetCharValue();
void SetString(LPCSTR sData, UINT nSize = 0);
void SetString(LPCWSTR sData);
void SetDelimeter(char chDel);
void SetEndChar(char chEnd);
void Trim(LPSTR sData);
char GetDelimeter();
LPSTR GetString();
LPSTR GetString(int nIndex);
int GetCurrentIndex();
};
심플하죠..?
버퍼 사이즈는 그때 통신 데이터 사이즈를 감안하여 최대 8MB 로 지정하였고 기본 구분자는 ',' 로 설정하였고 객체 생성 시에 구분자를 지정할 수도 있습니다.
멤버 변수에 대한 설명은 주석과 같구요, 각 함수에 대한 설명은 함수의 기능이 구현된 소스파일에서 설명 드리겠습니다.
CPacketParser.cpp(소스 파일)
소스파일 전체 소스 입니다.
#include "PacketParser.h"
#define TRIM_RIGHT(x) { int i ; for (i = strlen(x) - 1; (i >= 0)&&(x[i]==' '); i--); x[i + 1] = 0; };
#define TRIM_LEFT(x) { _tcsrev(x); TRIM_RIGHT(x); _tcsrev(x); };
#define TRIM(x) { TRIM_RIGHT(x); TRIM_LEFT(x); };
CPacketParser::CPacketParser(char chDelimeter)
{
m_nIndex = 0;
m_nMaxSize = 0;
m_chDel = chDelimeter;
m_chEnd = 0;
memset(m_sData, 0, sizeof(m_sData));
m_arrDatas.clear();
}
CPacketParser::~CPacketParser()
{
m_nIndex = 0;
m_nMaxSize = 0;
m_chDel = 0;
memset(m_sData, 0, sizeof(m_sData));
if (!m_arrDatas.empty())
{
for (size_t i = 0; i < m_arrDatas.size(); i++)
delete [] m_arrDatas[i];
m_arrDatas.clear();
}
}
int CPacketParser::GetSize()
{
return m_nMaxSize;
}
bool CPacketParser::IsEmpty()
{
return (m_nMaxSize > 0) ? FALSE : TRUE;
}
void CPacketParser::Clear()
{
m_nIndex = 0;
m_nMaxSize = 0;
memset(m_sData, 0, sizeof(m_sData));
if (!m_arrDatas.empty())
{
for (size_t i = 0; i < m_arrDatas.size(); i++)
delete [] m_arrDatas[i];
m_arrDatas.clear();
}
}
int CPacketParser::Find( LPSTR sValue )
{
for (int i = 0; i < m_nMaxSize; i++)
{
if (strcmp(m_arrDatas[i], sValue) == 0)
return i;
}
return -1;
}
byte CPacketParser::GetByteValue()
{
return (byte)GetLongValue();
}
short CPacketParser::GetShortValue()
{
return (short)GetLongValue();
}
int CPacketParser::GetIntValue()
{
return (int)GetLongValue();
}
long CPacketParser::GetLongValue()
{
if(m_nMaxSize <= m_nIndex || m_nIndex < 0)
return -1;
long ntemp = atol(m_arrDatas[m_nIndex++]);
return ntemp;
}
float CPacketParser::GetFloatValue()
{
return (float)GetDoubleValue();
}
double CPacketParser::GetDoubleValue()
{
if (m_nMaxSize <= m_nIndex || m_nIndex < 0)
return -1;
double ntemp = atof(m_arrDatas[m_nIndex++]);
return ntemp;
}
LPSTR CPacketParser::GetStringValue()
{
if (m_nMaxSize <= m_nIndex || m_nIndex < 0)
return "";
return m_arrDatas[m_nIndex++];
}
char CPacketParser::GetCharValue()
{
if (m_nMaxSize <= m_nIndex || m_nIndex < 0)
return -1;
//SetTrim(m_arrDatas[m_nIndex]);
return m_arrDatas[m_nIndex++][0];
}
void CPacketParser::SetString( LPCSTR sData, UINT nSize /*= 0*/ )
{
if (sData == NULL)
return ;
char temp[BUFSIZE + 1]; // Declare Buffer
memset(temp, 0, sizeof(temp)); // Init
if (nSize > 0)
{
memcpy(temp, sData, nSize); // Const Char Data Copy
}
else
{
nSize = strlen(sData);
memcpy(temp, sData, strlen(sData)); // Const Char Data Copy
}
char * ptok = static_cast<char *>(memchr(temp, m_chEnd, sizeof(temp))); // Check NULL Data Position
if (NULL == ptok) return ;
int templen = (int)ptok - (int)temp; // Start Data Position - NULL Data Position
temp[templen] = m_chDel; // Last Data Next Input Delimeter
if (!m_arrDatas.empty()) // Data Array Init
m_arrDatas.clear();
int nOffset = 0;
ptok = static_cast<char *>(memchr(temp, m_chDel, sizeof(temp))); // Find Next Delimeter
while(NULL != ptok && ((UINT)nOffset <= nSize) ) // Exist Delimeter
{
int nCurrentSize = (int)ptok - (int)temp - nOffset; // Current Data Size
if (nCurrentSize <= 0)
{
nOffset += 1;
continue;
}
char * stemp = NULL;
stemp = new char[nCurrentSize + 1]; // Create Current Data Buffer
memset(stemp, 0, nCurrentSize + 1); // Init
memcpy(stemp, temp + nOffset, nCurrentSize); // Copy Current Data
Trim(stemp);
m_arrDatas.push_back(stemp); // Append Current Data To Data Array
nOffset += nCurrentSize + 1; // Current position Set
if (nOffset + 1 >= sizeof(temp)) // Check Total Size & Current Position
break;
ptok = static_cast<char *>(memchr(temp + nOffset, m_chDel, sizeof(temp) - nOffset)); // Find Next Delimeter
}
m_nMaxSize = m_arrDatas.size(); // Total Count
memset(m_sData, 0, sizeof(m_sData));
memcpy(m_sData, sData, nSize);
memset(temp, 0, sizeof(temp));
}
void CPacketParser::SetString(LPCWSTR sData)
{
int nLen = wcslen(sData);
char * stemp = new char[nLen * 2];
memset(stemp, 0, nLen * 2);
WideCharToMultiByte(CP_ACP, 0, sData, -1, stemp, nLen * 2, NULL, NULL);
SetString(stemp);
delete [] stemp;
}
void CPacketParser::SetDelimeter(char chDel)
{
m_chDel = chDel;
}
void CPacketParser::SetEndChar(char chEnd)
{
m_chEnd = chEnd;
}
void CPacketParser::Trim( LPSTR sData )
{
if (sData == NULL)
return ;
int nSize = strlen(sData);
int nRightCount = 0;
for (int i = nSize; i >= 0; i--)
{
if (sData[i] != ' ' && sData[i] != 0 && sData[i] != '\t')
{
nRightCount = i + 1;
break;
}
}
int nLeftCount = 0;
for (int i = 0; i < nSize; i++)
{
if (sData[i] != ' ' && sData[i] != 0 && sData[i] != '\t')
{
nLeftCount = i;
break;
}
}
char * strValue = new char[nSize];
memset(strValue, 0, nSize);
memcpy(strValue, sData + nLeftCount, nSize - nLeftCount - (nSize - nRightCount));
memset(sData, 0, nSize);
memcpy(sData, strValue, nSize);
if (strValue != NULL)
{
delete [] strValue;
strValue = NULL;
}
}
char CPacketParser::GetDelimeter()
{
return m_chDel;
}
LPSTR CPacketParser::GetString()
{
return m_sData;
}
LPSTR CPacketParser::GetString(int nIndex)
{
if (nIndex > m_nMaxSize || nIndex < 0)
return NULL;
return m_arrDatas[nIndex];
}
int CPacketParser::GetCurrentIndex()
{
return m_nIndex;
}
소스파일에 대한 기능은 함수별로 설명 드리겠습니다.(설명이 조금이라도 필요한 함수들만...)
CPacketParser(char chDelimeter) : 생성자는 멤버변수를 초기화 하고 구분자를 파라미터로 받아서 설정할 수 있습니다.
CPacketParser::CPacketParser(char chDelimeter)
{
m_nIndex = 0;
m_nMaxSize = 0;
m_chDel = chDelimeter;
m_chEnd = 0;
memset(m_sData, 0, sizeof(m_sData));
m_arrDatas.clear();
}
CPacketParser::~CPacketParser()
{
m_nIndex = 0;
m_nMaxSize = 0;
m_chDel = 0;
memset(m_sData, 0, sizeof(m_sData));
if (!m_arrDatas.empty())
{
for (size_t i = 0; i < m_arrDatas.size(); i++)
delete [] m_arrDatas[i];
m_arrDatas.clear();
}
}
int CPacketParser::GetSize()
{
return m_nMaxSize;
}
bool CPacketParser::IsEmpty()
{
return (m_nMaxSize > 0) ? FALSE : TRUE;
}
void CPacketParser::Clear()
{
m_nIndex = 0;
m_nMaxSize = 0;
memset(m_sData, 0, sizeof(m_sData));
if (!m_arrDatas.empty())
{
for (size_t i = 0; i < m_arrDatas.size(); i++)
delete [] m_arrDatas[i];
m_arrDatas.clear();
}
}
int CPacketParser::Find( LPSTR sValue )
{
for (int i = 0; i < m_nMaxSize; i++)
{
if (strcmp(m_arrDatas[i], sValue) == 0)
return i;
}
return -1;
}
byte CPacketParser::GetByteValue()
{
return (byte)GetLongValue();
}
short CPacketParser::GetShortValue()
{
return (short)GetLongValue();
}
int CPacketParser::GetIntValue()
{
return (int)GetLongValue();
}
long CPacketParser::GetLongValue()
{
if(m_nMaxSize <= m_nIndex || m_nIndex < 0)
return -1;
long ntemp = atol(m_arrDatas[m_nIndex++]);
return ntemp;
}
float CPacketParser::GetFloatValue()
{
return (float)GetDoubleValue();
}
double CPacketParser::GetDoubleValue()
{
if (m_nMaxSize <= m_nIndex || m_nIndex < 0)
return -1;
double ntemp = atof(m_arrDatas[m_nIndex++]);
return ntemp;
}
LPSTR CPacketParser::GetStringValue()
{
if (m_nMaxSize <= m_nIndex || m_nIndex < 0)
return "";
return m_arrDatas[m_nIndex++];
}
LPSTR CPacketParser::GetString(int nIndex)
{
if (nIndex > m_nMaxSize || nIndex < 0)
return NULL;
return m_arrDatas[nIndex];
}
void CPacketParser::SetString( LPCSTR sData, UINT nSize /*= 0*/ )
{
if (sData == NULL)
return ;
char temp[BUFSIZE + 1]; // Declare Buffer
memset(temp, 0, sizeof(temp)); // Init
if (nSize > 0)
{
memcpy(temp, sData, nSize); // Const Char Data Copy
}
else
{
nSize = strlen(sData);
memcpy(temp, sData, strlen(sData)); // Const Char Data Copy
}
char * ptok = static_cast<char *>(memchr(temp, m_chEnd, sizeof(temp))); // Check NULL Data Position
if (NULL == ptok) return ;
int templen = (int)ptok - (int)temp; // Start Data Position - NULL Data Position
temp[templen] = m_chDel; // Last Data Next Input Delimeter
if (!m_arrDatas.empty()) // Data Array Init
m_arrDatas.clear();
int nOffset = 0;
ptok = static_cast<char *>(memchr(temp, m_chDel, sizeof(temp))); // Find Next Delimeter
while(NULL != ptok && ((UINT)nOffset <= nSize) ) // Exist Delimeter
{
int nCurrentSize = (int)ptok - (int)temp - nOffset; // Current Data Size
if (nCurrentSize <= 0)
{
nOffset += 1;
continue;
}
char * stemp = NULL;
stemp = new char[nCurrentSize + 1]; // Create Current Data Buffer
memset(stemp, 0, nCurrentSize + 1); // Init
memcpy(stemp, temp + nOffset, nCurrentSize); // Copy Current Data
Trim(stemp);
m_arrDatas.push_back(stemp); // Append Current Data To Data Array
nOffset += nCurrentSize + 1; // Current position Set
if (nOffset + 1 >= sizeof(temp)) // Check Total Size & Current Position
break;
ptok = static_cast<char *>(memchr(temp + nOffset, m_chDel, sizeof(temp) - nOffset)); // Find Next Delimeter
}
m_nMaxSize = m_arrDatas.size(); // Total Count
memset(m_sData, 0, sizeof(m_sData));
memcpy(m_sData, sData, nSize);
memset(temp, 0, sizeof(temp));
}
먼저 파싱할 데이터가 있는지 확인하고 없으면 파싱을 중지합니다.
파싱할 데이터가 있을 경우 데이터를 임시 버퍼에 복사합니다.( Size 가 지정되지 않았을 경우 사이즈 확인 후 복사)
문자열의 끝을 찾습니다.(null(문자열의 끝)) 문자열이 아닐 경우 파싱을 중지합니다.
문자열일 경우 원활한 파싱 진행을 위해 문자열의 끝을 구분자로 교체합니다.
제일 처음 나오는 구분자를 찾습니다.
구분자가 존재하고 그 위치가 전체 문자열의 크기를 넘지 않는지 확인하고 조건에 충족할 경우 파싱된 데이터의 사이즈를 확인 후 파싱된 문자열 데이터를 가공하여 파싱 데이터를 저장할 배열에 넣습니다.
다음 데이터 검색을 위해 temp 데이터의 메모리 시작 위치를 offset 만큼 이동시켜서 계속 데이터 검색을 진행합니다.
모든 데이터를 파싱 한 후 파싱을 진행했던 문자열 원본을 m_sData 에 복사하고 파싱된 데이터 크기를 저장한 후 임시 버퍼인 temp 의 데이터를 삭제(초기화) 합니다.
void CPacketParser::SetString(LPCWSTR sData)
{
int nLen = wcslen(sData);
char * stemp = new char[nLen * 2];
memset(stemp, 0, nLen * 2);
WideCharToMultiByte(CP_ACP, 0, sData, -1, stemp, nLen * 2, NULL, NULL);
SetString(stemp);
delete [] stemp;
}
void CPacketParser::SetDelimeter(char chDel)
{
m_chDel = chDel;
}
void CPacketParser::Trim( LPSTR sData )
{
if (sData == NULL)
return ;
int nSize = strlen(sData);
int nRightCount = 0;
for (int i = nSize; i >= 0; i--)
{
if (sData[i] != ' ' && sData[i] != 0 && sData[i] != '\t')
{
nRightCount = i + 1;
break;
}
}
int nLeftCount = 0;
for (int i = 0; i < nSize; i++)
{
if (sData[i] != ' ' && sData[i] != 0 && sData[i] != '\t')
{
nLeftCount = i;
break;
}
}
char * strValue = new char[nSize];
memset(strValue, 0, nSize);
memcpy(strValue, sData + nLeftCount, nSize - nLeftCount - (nSize - nRightCount));
memset(sData, 0, nSize);
memcpy(sData, strValue, nSize);
if (strValue != NULL)
{
delete [] strValue;
strValue = NULL;
}
}
클래스 내부 C/C++ 함수 레퍼런스 및 설명
CPacketParser 클래스 제작 시에 사용한 C/C++ 함수의 종류와 기능에 대해 드리겠습니다.
void memset(void *s, int c, size_t n);
- s : 채울 대상의 메모리
- c : 채울 값, int 형이지만 1바이트(char) 로 인식.
- n : 채울 개수
>> s 가 포인트하는 메모리를 값 c 로 n 개를 채웁니다.
int atol(const chat * s);
- s : 정수로 변환될 문자열
>> s 문자열을 long 형 정수로 변환하여 반환합니다.
문자열은 숫자 및 부호로 이루어져 있어야 하며 숫자의 앞부분의 Tab 이나 공백은 무시하고 변환합니다.
문자열이 정수로 변환이 불가능할 경우 0 을 반환합니다.
double atof(const char * s);
- s: 실수로 변환될 문자열
>> s 문자열을 double 형 부동 소수점으로 변환하여 반환합니다.
문자열은 숫자 및 부호, 소수점으로 이루어져 있어야 하며 숫자의 앞부분의 Tab 이나 공백은 무시하고 변환합니다.
문자열이 실수로 변환이 불가능할 경우 0 을 반환합니다.
void * memcpy(void * dest, void * src, size_t n);
- dest : 복사 대상 메모리(target)
- src : 복사원 메모리(source)
- n : 복사할 길이
>> src 번지에 있는 데이터를 dest 가 지정하는 번지로 n 바이트만큼 복사를 합니다.
복사에 성공하였을 경우 dest 포인터를 반환하고, 에러 발생 시 NULL 을 반환합니다.
size_t strlen(const char * s);
- s : 길이를 계산할 문자열
>> 문자열 s 의 길이를 계산합니다.
참고로 문자열의 길이란 문자열의 선두에서부터 NULL 문자 사이의 문자 개수를 말합니다.
void * memchr(const void * s, char c, size_t n);
- s: 시작 포인트 메모리 번지
- c : 찾아야 되는 문자
- n : 찾을 수 있는 최대 개수
>> s 의 메모리 번지부터 n 바이트 내의 메모리까지 중에 문자 c 를 찾아서 위치를 반환합니다.
발견 시 최초로 발견한 문자 c 의 위치를 반환하고 없을 경우 NULL 을 반환합니다.
int strcmp(const char * s1, const char * s2);
- s1 : 크기를 비교할 첫번째 문자열
- s2 : 크기를 비교할 두번째 문자열
>> 두개의 문자열의 대소를 비교합니다.(대문자와 소문자 구분)
s1 > s2 : 양수 반환
s1 = s2 : 0 을 반환
s1 < s2 : 음수 반환
int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
- CodePage : 변환 방법(타켓 인코딩)
- dwFlags : 0
- lpWideCharStr : 원본 문자열(source)
- cchWideChar : 원본 문자열 길이(-1 을 넣으면 자동할당)
- lpMultiByteStr : 변환된 문자열(target)
- cbMultiByte : 변환된 문자열 크기(유니코드 문자열의 2배를 할당)
- lpDefaultChar, lpUserDefaultChar : 실패 시 사용(기본적으로 NULL 을 할당)
>> 유니코드 문자열을 멀티바이트 문자열로 변환하여 할당합니다.
반대 기능의 함수도 있지만 요즘은 사용하지 않을 함수 이므로 여기서 중략...
여기까지 임미다.
'개발 > C&C++' 카테고리의 다른 글
[Windows] Keyboard - key code & value (0) | 2021.11.08 |
---|---|
[API] PROCESS 권한 확인 및 변경 - OpenProcessToken / LookupPrivilegeValue / AdjustTokenPrivileges (0) | 2017.03.14 |
[API] ShellExecute Function (0) | 2017.03.10 |
[TEMPLATE] VECTOR / MAP - RELEASE MEMORY (0) | 2017.03.09 |
[MFC] CDaoFieldInfo / Column or Field 의 DataType (0) | 2017.03.08 |