[ATL/MFC/C++] Ini Parser(ini 파서)
2011. 11. 30. 17:35 in 프로그래밍/Let's Share it

ini 파서를 공유합니다.
그리고 일단 제가 공유하는 모든 코드들은,
CCL 파이센스이고, [ 상업적이용 Off / 코드 변경 Yes ] 이니,
용도에 맞게 사용하시기 바랍니다.
상업용도로 사용하시고자 한다면, 필히 저에게 알려주시기 바랍니다. (물론 공유는 해드립니다~)
일단, CIniParser Class를 공유하며,
각 API 설명은 아래와 같습니다.
내부적으로 CAtlMap을 사용하기 때문에 Get/Set 동작은 빠를 것으로 보입니다.
조건으로 Section명에 ;문자를 쓰면 안됩니다.
BOOL Parse(IN LPCTSTR lpszBuf)
lpszBuf 내용을 받아 파싱합니다.
내부에 Map을 사용하므로, Caller는 본 함수 호출이후에 lpszBuf를 Free해도 됩니다.
성공시 TRUE, 실패시 FALSE를 리턴합니다.
BOOL GetIni(OUT CString& strIni)
메모리상에 있는 Map을 Ini 포맷으로 변경합니다.
아래의 Parse에 전달한 lpszBuf와 Section 이름등 순서가 변경될 수 있음을 유의하시기 바랍니다.
물론, SetString한 순서도 변경될 수 있습니다.
단, 하나의 Section명은 한번만 등장하는것은 보장해 드립니다.
즉,
[a]
q=1
[b]
a=1
[a]
e=3
와 같이 [a]가 두번 등장하지 않고 다음과 같이 뭉쳐줍니다.
[a]
q=1
e=3
[b]
a=1
q=1
[b]
a=1
[a]
e=3
와 같이 [a]가 두번 등장하지 않고 다음과 같이 뭉쳐줍니다.
[a]
q=1
e=3
[b]
a=1
BOOL GetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, OUT CString& strValue)
메모리상에 있는 Map을 기반으로 GetPrivateProfileString과 같은 효과를 냅니다.
값이 있을때 TRUE, 없을때 FALSE를 리턴하니,
Caller에서 알아서 기본값 세팅을 하시기 바랍니다.
BOOL SetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, IN LPCTSTR lpszValue)
메모리상에 있는 Map을 수정합니다.
성공시 TRUE, 실패시 FALSE를 리턴합니다.
View Plain을 통하면 좀더 자세히 볼 수 있습니다.
참고로, 버그 회피를 위해, 해당 Class의 Instance은
Thread간 공유할 수 없도록 하였습니다.
Class의 Instance는 해당 Thread에서만 사용하도록 하십시요.
- #pragma once
- #include <atlcoll.h>
- class CIniParser
- {
- public:
- CIniParser(void);
- ~CIniParser(void);
- public:
- BOOL Parse(IN LPCTSTR lpszBuf);
- BOOL GetIni(OUT CString& strIni);
- public:
- BOOL GetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, OUT CString& strValue);
- BOOL SetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, IN LPCTSTR lpszValue);
- protected:
- VOID GetUpperSectionName(IN LPCTSTR lpszLine, OUT CString& strSectionName);
- VOID AddMap(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszLine);
- protected:
- DWORD m_dwThreadId;
- CAtlMap<CString,CString> m_cMap;
- CString m_strTmp;
- CString m_strTmp2;
- CString m_strTmp3;
- CString m_strTmp4;
- };
IniParser.cpp
- #include "StdAfx.h"
- #include "IniParser.h"
- #include <strsafe.h>
- CIniParser::CIniParser(void)
- {
- m_dwThreadId = ::GetCurrentThreadId();
- }
- CIniParser::~CIniParser(void)
- {
- }
- BOOL CIniParser::Parse(IN LPCTSTR lpszBuf)
- {
- BOOL bRtnValue = TRUE;
- INT i = 0;
- INT nLast = 0;
- CString strSectionName;
- CString strLine;
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- return FALSE;
- }
- // Map 초기화
- m_cMap.RemoveAll();
- if (NULL == lpszBuf)
- {
- bRtnValue = FALSE;
- goto FINAL;
- }
- for (i=0; i<STRSAFE_MAX_CCH; i++)
- {
- if ((TEXT('\n') == lpszBuf[i]) ||
- (TEXT('\0') == lpszBuf[i]))
- {
- // parse !!!
- strLine.TrimRight(TEXT("\r\n"));
- strLine.TrimLeft(TEXT("\r\n "));
- if (TEXT('[') == strLine[0])
- {
- // 만일 [로 시작했다면,...
- GetUpperSectionName(strLine, strSectionName);
- }
- else
- {
- // Map에 추가
- AddMap(strSectionName, strLine);
- }
- // 다시 초기화
- strLine = TEXT("");
- }
- else
- {
- strLine.AppendChar(lpszBuf[i]);
- }
- if (TEXT('\0') == lpszBuf[i])
- {
- break;
- }
- }
- FINAL:
- return bRtnValue;
- }
- VOID CIniParser::AddMap(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszLine)
- {
- INT nFind = -1;
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- return;
- }
- if (NULL == lpszLine)
- {
- return;
- }
- m_strTmp = lpszLine;
- if (TEXT(';') == m_strTmp[0])
- {
- // ;로 시작한다면, 주석 처리
- goto FINAL;
- }
- nFind = m_strTmp.Find(TEXT('='));
- if (-1 == nFind)
- {
- // = 가 없다???
- goto FINAL;
- }
- m_strTmp2 = m_strTmp.Mid(0, nFind);
- m_strTmp2.TrimRight(TEXT(" "));
- if (TRUE == m_strTmp2.IsEmpty())
- {
- goto FINAL;
- }
- m_strTmp3 = m_strTmp.Mid(nFind+1);
- // m_strTmp2=m_strTmp3
- // 와 같이 parse되었다.
- // 이제 Map에 추가하자.
- // Map에 축가할 Key는 SECTIONNAME;KEYNAME 와 같은 포맷이다.
- m_strTmp4.Format(TEXT("%s;%s"), lpszSectionName, m_strTmp2);
- m_strTmp4.MakeUpper();
- // Map에 추가되었다.
- m_cMap.SetAt(m_strTmp4, m_strTmp3);
- FINAL:
- return;
- }
- // lpszLine이 Section 포맷이 아니라면, strSectionName은 Old값을 유지한다.
- // lpszLine은 왼쪽에 ' '으로 trim 되었다고 가정
- VOID CIniParser::GetUpperSectionName(IN LPCTSTR lpszLine, OUT CString& strSectionName)
- {
- INT i = 0;
- LPCTSTR lpszFind = NULL;
- LPCTSTR lpszFind2 = NULL;
- if (NULL == lpszLine)
- {
- return;
- }
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- return;
- }
- if (TEXT('[') != lpszLine[0])
- {
- return;
- }
- lpszFind = _tcschr(lpszLine, TEXT(']'));
- if (NULL == lpszFind)
- {
- return;
- }
- lpszFind2 = _tcschr(lpszLine, TEXT(';'));
- if (NULL != lpszFind2)
- {
- if (lpszFind2 < lpszFind)
- {
- // section명에 ;가 오면 안된다.
- return;
- }
- }
- if (1 == lpszFind - lpszLine)
- {
- return;
- }
- strSectionName = TEXT("");
- for (i=1; i<lpszFind - lpszLine; i++)
- {
- if (TEXT(';') == lpszLine[i])
- {
- break;
- }
- strSectionName.AppendChar(lpszLine[i]);
- }
- // 대문자로~!!!
- strSectionName.MakeUpper();
- }
- // 리턴 : TRUE(성공) / FALSE(실패)
- BOOL CIniParser::SetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, IN LPCTSTR lpszValue)
- {
- BOOL bRtnValue = FALSE;
- CString strKey;
- if ((NULL == lpszSectionName) || (NULL == lpszKeyName) || (NULL == lpszValue))
- {
- bRtnValue = FALSE;
- goto FINAL;
- }
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- return FALSE;
- }
- strKey.Format(TEXT("%s;%s"), lpszSectionName, lpszKeyName);
- strKey.MakeUpper();
- m_cMap.SetAt(strKey, lpszValue);
- if (FALSE == m_cMap.Lookup(strKey, m_strTmp))
- {
- bRtnValue = FALSE;
- assert(FALSE);
- goto FINAL;
- }
- if (0 != _tcscmp(m_strTmp, lpszValue))
- {
- bRtnValue = FALSE;
- assert(FALSE);
- goto FINAL;
- }
- // 여기까지 왔다면 성공
- bRtnValue = TRUE;
- FINAL:
- return bRtnValue;
- }
- // 리턴 : TRUE(있다) / FALSE(없다)
- BOOL CIniParser::GetString(IN LPCTSTR lpszSectionName, IN LPCTSTR lpszKeyName, OUT CString& strValue)
- {
- BOOL bRtnValue = FALSE;
- CString strKey;
- strValue = TEXT("");
- if ((NULL == lpszSectionName) || (NULL == lpszKeyName))
- {
- bRtnValue = FALSE;
- goto FINAL;
- }
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- bRtnValue = FALSE;
- goto FINAL;
- }
- strKey.Format(TEXT("%s;%s"), lpszSectionName, lpszKeyName);
- strKey.MakeUpper();
- if (TRUE != m_cMap.Lookup(strKey, strValue))
- {
- // Map에 없다.
- bRtnValue = FALSE;
- strValue = TEXT("");
- goto FINAL;
- }
- // 여기까지 왔다면 성공
- bRtnValue = TRUE;
- FINAL:
- return bRtnValue;
- }
- BOOL CIniParser::GetIni(OUT CString& strIni)
- {
- BOOL bRtnValue = TRUE;
- INT nFind = 0;
- POSITION pos = {0,};
- CString strKey;
- CString strValue;
- CString strSectionName;
- CString strKeyName;
- CString strNewKey;
- CString strLine;
- CString strOld;
- CString strNode;
- CAtlMap<CString,CString> cMapFormat;
- strIni = TEXT("");
- if (m_dwThreadId != ::GetCurrentThreadId())
- {
- return FALSE;
- }
- pos = m_cMap.GetStartPosition();
- // 포맷 map을 만든다.
- // 즉,
- // (SEC1;KEY, VAL1), (SEC1;KEY2, VAL2), (SEC2;KEY1, VAL3), ...
- // 와 같은 map을
- // (SEC, KEY=VAL1\r\nKEY2=VAL2\r\n), (SEC2, KEY1=KEY3\r\n), ....
- // 로 변환한다.
- while (pos != NULL)
- {
- strKey = m_cMap.GetKeyAt(pos);
- strValue = m_cMap.GetNextValue(pos);
- nFind = strKey.Find(TEXT(';'));
- if (-1 == nFind)
- {
- assert(FALSE);
- continue;
- }
- strSectionName = strKey.Mid(0, nFind);
- strKeyName = strKey.Mid(nFind+1);
- strNewKey.Format(TEXT("[%s]"), strSectionName);
- strLine.Format(TEXT("%s=%s\r\n"), strKeyName, strValue);
- if (TRUE == cMapFormat.Lookup(strNewKey, strOld))
- {
- strLine.Insert(0, strOld);
- }
- cMapFormat.SetAt(strNewKey, strLine);
- }
- pos = cMapFormat.GetStartPosition();
- while (pos != NULL)
- {
- strKey = cMapFormat.GetKeyAt(pos);
- strValue = cMapFormat.GetNextValue(pos);
- strNode.Format(TEXT("%s\r\n%s\r\n"), strKey, strValue);
- strIni.Insert(0, strNode);
- }
- strIni.TrimRight(TEXT("\r\n"));
- // 여기까지 왔다면 성공
- bRtnValue = TRUE;
- return bRtnValue;
- }
- <SPAN id=tx_marker_caret></SPAN>
'프로그래밍 > Let's Share it' 카테고리의 다른 글
VC9.0에서 crypto++ library를 이용하여 SHA256(sha2) 구하기 (8) | 2011.12.14 |
---|---|
비동기(OVERLAPPED I/O, IOCP, Thread Pool) IPC PIPE Class 공유 (0) | 2011.12.14 |
concat과 printf를 동시에 수행하는 함수 공유 (0) | 2011.10.14 |
Window Event Log(윈도우 이벤트 로그) 읽기 (9) | 2011.07.20 |
Proxy 서버 구하기 (자동 구성 스크립트, pac) (0) | 2011.04.04 |