|
|
Comments and Discussions
|
|
 |
|

|
you integrated easily with GetCommandLine(). A very easy to use
|
|
|
|

|
Could we easily find out some filepath string in cmdline?
It's easy if we used /FileNameKey:"THE PATH OF FILE",like the sample in your article.
But in normally, filepath string be used don't have the marked part /FileNameKey . and then there is a easy way to find out it?
|
|
|
|

|
Good job! thanks very much.
But when the execute file is in one folder which include the Delimeter character "-", the Parse() function will take it as a key mistakenly. I think this is a bug.
And why don't you use the argc & argv? Below is my implementation of CCmdLineParser::CCmdLineParser(). I think this is simpler and easier to understand than yours.
const TCHAR CCmdLineParser::m_sDelimeters[] = _T("/"); const TCHAR CCmdLineParser::m_sValueSep[] = _T(":");
CCmdLineParser::CCmdLineParser(int argc, TCHAR* argv[], bool bCaseSensitive) : m_bCaseSensitive(bCaseSensitive)
{
const CString sEmpty;
m_ValsMap.clear();
for (int i=1; i<argc; i++) {
LPCTSTR sArg = _tcspbrk(argv[i], m_sDelimeters);
if ( (NULL == sArg) || (sArg != argv[i]) || ('\0' == sArg[1]) ) { continue;
}
sArg = _tcsinc(sArg);
LPCTSTR sVal = _tcspbrk(sArg, m_sValueSep);
if( NULL == sVal ) { CString csKey(sArg);
if(!m_bCaseSensitive) {
csKey.MakeLower();
}
m_ValsMap.insert(CValsMap::value_type(csKey, sEmpty));
} else { CString csKey(sArg, sVal - sArg);
if(!csKey.IsEmpty()) { if(!m_bCaseSensitive) {
csKey.MakeLower();
}
if ( 1 == _tcslen(sVal) ) { sVal = sEmpty;
} else { sVal = _tcsinc(sVal);
}
m_ValsMap.insert(CValsMap::value_type(csKey, sVal));
}
}
}
}
|
|
|
|

|
Sorry, there was a bug in my code, too. When the "Val" is enclosed in double-quotation, the last charater of argv[i] became '\"'.
Below is the revised version.
CCmdLineParser::CCmdLineParser(int argc, TCHAR* argv[], bool bCaseSensitive) : m_bCaseSensitive(bCaseSensitive)
{
m_ValsMap.clear();
for (int i=1; i<argc; i++) {
LPCTSTR sArg = _tcspbrk(argv[i], m_sDelimeters);
if ( (NULL == sArg) || (sArg != argv[i]) || ('\0' == sArg[1]) ) { // The m_sDelimeters chacracter not found || The first character is not in m_sDelimeters set || cmdline ends with /
continue;
}
sArg = _tcsinc(sArg);
LPTSTR sVal = _tcspbrk(sArg, m_sValueSep);
if(sVal == NULL) { // cmdline ends with /Key
CString csKey(sArg);
if( !m_bCaseSensitive ) {
csKey.MakeLower();
}
m_ValsMap.insert(CValsMap::value_type(csKey, ""));
} else { // cmdline ends with /Key:*
CString csKey(sArg, sVal - sArg);
if( !csKey.IsEmpty() ) { // Prevent /: case
if( !m_bCaseSensitive ) {
csKey.MakeLower();
}
size_t stLen = _tcslen(sVal);
if ( 1 == stLen ) { // cmdline ends with /Key:
sVal = _T("");
} else { // cmdline ends with /Key:Val
if ('\"' == sVal[stLen-1]) {
sVal[stLen-1] = '\\';
}
sVal = _tcsinc(sVal);
}
m_ValsMap.insert(CValsMap::value_type(csKey, sVal));
}
}
}
}
|
|
|
|
|

|
A very easy to use and simple interface. I like the fact that you integrated easily with GetCommandLine().
Thanks!
Kevin
|
|
|
|

|
I have been using this class with modifications in my project, so thought I'd contribute something back.
Added support for parameters with two dashes (like --foobar) or actually any number of delimiter combinations (-/-foobar):
LPCTSTR pszArg = _tcspbrk(pszCurrent, m_pszDelimeters);
if (! pszArg)
{
break;
}
LPCTSTR pszInArg;
do
{
pszArg = _tcsinc(pszArg);
pszInArg = _tcspbrk(pszArg, m_pszDelimeters);
} while (pszInArg == pszArg);
...
I also derived from CMapStringToString as many have suggested already and removed all the find and iterator functions to use those of the base class. Made code a lot simpler.
In addition, for those that use CppUnit for unit testing, here's a unit test:
typedef struct
{
LPCTSTR pszName;
LPCTSTR pszValue;
} CCmdLineTestDataPair;
typedef struct
{
LPCTSTR pszCmdLine;
BOOL fCaseSensitive;
ULONG ulParameters;
CCmdLineTestDataPair testDataPairs[3];
} CCmdLineTestData;
void CCmdLineUnitTest::testParseCmdLine(void)
{
CCmdLineTestData testdata[] =
{
{ _T(""), FALSE, 0, { { NULL, NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("--test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/-/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("----test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/p1 /p2"), TRUE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/p1=p1value /p2=p2value /p3=p3value"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } },
{ _T("/p1=\"p1value\" /p2=\'p2value\' /p3:`p3value`"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } }
};
for (int i = 0; i < sizeof(testdata) / sizeof(CCmdLineTestData); i++)
{
CCmdLine c(testdata[i].pszCmdLine, testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid command line parsed.", c.GetCmdLine().Compare(testdata[i].pszCmdLine) == 0);
CPPUNIT_ASSERT_MESSAGE("Invalid case sensitive parameter.", c.GetCaseSensitive() == testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid parameter count.", c.GetCount() == testdata[i].ulParameters);
for (int j = 0; j < sizeof(testdata[i].testDataPairs) / sizeof(CCmdLineTestDataPair); j++)
{
if (testdata[i].testDataPairs[j].pszName == NULL && testdata[i].testDataPairs[j].pszValue == NULL)
break;
CString strValue;
CPPUNIT_ASSERT_MESSAGE("Expected parameter not found.", c.Lookup(testdata[i].testDataPairs[j].pszName, strValue));
CPPUNIT_ASSERT_MESSAGE("Parameter value does not match.",
((strValue.GetLength() == 0) && (testdata[i].testDataPairs[j].pszValue == NULL))
|| strValue.Compare(testdata[i].testDataPairs[j].pszValue) == 0);
}
}
}
cheers
dB.
|
|
|
|

|
Someone asked me for full code.
CmdLine.h:
#pragma once
class CCmdLine
: public CMapStringToString
{
DECLARE_DYNAMIC(CCmdLine)
public:
CCmdLine(LPCTSTR sCmdLine = NULL, BOOL bCaseSensitive = FALSE);
virtual ~CCmdLine(void);
void Parse(LPCTSTR pszCmdLine);
inline const CString& GetCmdLine(void) const { return m_strCmdLine; }
inline void SetCaseSensitive(BOOL bSensitive) { m_bCaseSensitive = bSensitive; }
inline BOOL GetCaseSensitive(void) const { return m_bCaseSensitive; }
inline BOOL HasKey(LPCTSTR pszKey) const { CString strValue; return CMapStringToString::Lookup(pszKey, strValue); }
private:
CString m_strCmdLine;
BOOL m_bCaseSensitive;
static const TCHAR m_pszDelimeters[];
static const TCHAR m_pszValueSep[];
static const TCHAR m_pszQuotes[];
};
#ifdef CPPUNIT_API
class CCmdLineUnitTest : public CppUnit::TestCase
{
public:
void testParseCmdLine(void);
public:
CPPUNIT_TEST_SUITE(CCmdLineUnitTest);
CPPUNIT_TEST(testParseCmdLine);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(CCmdLineUnitTest);
#endif
CmdLine.cpp:
#include "stdafx.h"
IMPLEMENT_DYNAMIC(CCmdLine, CMapStringToString)
const TCHAR CCmdLine::m_pszDelimeters[] = _T("-/");
const TCHAR CCmdLine::m_pszQuotes[] = _T("\"\'`");
const TCHAR CCmdLine::m_pszValueSep[] = _T(" :=");
CCmdLine::CCmdLine(LPCTSTR pszCmdLine, BOOL bCaseSensitive)
: m_bCaseSensitive(bCaseSensitive)
{
if (pszCmdLine != NULL)
{
Parse(pszCmdLine);
}
}
CCmdLine::~CCmdLine()
{
}
void CCmdLine::Parse(LPCTSTR pszCmdLine)
{
if (! pszCmdLine)
return;
m_strCmdLine = pszCmdLine;
RemoveAll();
int nArgs = 0;
LPCTSTR pszCurrent = pszCmdLine;
while(TRUE)
{
if (_tcslen(pszCurrent) == 0)
{
break;
}
LPCTSTR pszArg = _tcspbrk(pszCurrent, m_pszDelimeters);
if (! pszArg)
{
break;
}
LPCTSTR pszInArg;
do
{
pszArg = _tcsinc(pszArg);
pszInArg = _tcspbrk(pszArg, m_pszDelimeters);
} while (pszInArg == pszArg);
if (_tcslen(pszArg) == 0)
{
break;
}
LPCTSTR pszVal = _tcspbrk(pszArg, m_pszValueSep);
if (pszVal == NULL)
{
CString strKey(pszArg);
if(! m_bCaseSensitive)
{
strKey.MakeLower();
}
SetAt(strKey, _T(""));
break;
}
else if (pszVal[0] == _T(' ') || _tcslen(pszVal) == 1 )
{
CString strKey(pszArg, (int) (pszVal - pszArg));
if(! strKey.IsEmpty())
{
if(!m_bCaseSensitive)
{
strKey.MakeLower();
}
SetAt(strKey, _T(""));
}
pszCurrent = _tcsinc(pszVal);
continue;
}
else
{
CString strKey(pszArg, (int) (pszVal - pszArg));
if (! m_bCaseSensitive)
{
strKey.MakeLower();
}
pszVal = _tcsinc(pszVal);
LPCTSTR pszQuote = _tcspbrk(pszVal, m_pszQuotes), pszEndQuote(NULL);
if (pszQuote == pszVal)
{
pszQuote = _tcsinc(pszVal);
pszEndQuote = _tcspbrk(pszQuote, m_pszQuotes);
}
else
{
pszQuote = pszVal;
pszEndQuote = _tcschr(pszQuote, _T(' '));
}
if (pszEndQuote == NULL)
{
CString strValue(pszQuote);
if(! strKey.IsEmpty())
{
SetAt(strKey, strValue);
}
break;
}
else
{
if(! strKey.IsEmpty())
{
CString strValue(pszQuote, (int) (pszEndQuote - pszQuote));
SetAt(strKey, strValue);
}
pszCurrent = _tcsinc(pszEndQuote);
continue;
}
}
}
}
#ifdef CPPUNIT_API
typedef struct
{
LPCTSTR pszName;
LPCTSTR pszValue;
} CCmdLineTestDataPair;
typedef struct
{
LPCTSTR pszCmdLine;
BOOL fCaseSensitive;
ULONG ulParameters;
CCmdLineTestDataPair testDataPairs[3];
} CCmdLineTestData;
void CCmdLineUnitTest::testParseCmdLine(void)
{
CCmdLineTestData testdata[] =
{
{ _T(""), FALSE, 0, { { NULL, NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("--test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/-/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("----test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/p1 /p2"), TRUE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/p1=p1value /p2=p2value /p3=p3value"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } },
{ _T("/p1=\"p1value\" /p2=\'p2value\' /p3:`p3value`"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } }
};
for (int i = 0; i < sizeof(testdata) / sizeof(CCmdLineTestData); i++)
{
CCmdLine c(testdata[i].pszCmdLine, testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid command line parsed.", c.GetCmdLine().Compare(testdata[i].pszCmdLine) == 0);
CPPUNIT_ASSERT_MESSAGE("Invalid case sensitive parameter.", c.GetCaseSensitive() == testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid parameter count.", c.GetCount() == testdata[i].ulParameters);
for (int j = 0; j < sizeof(testdata[i].testDataPairs) / sizeof(CCmdLineTestDataPair); j++)
{
if (testdata[i].testDataPairs[j].pszName == NULL && testdata[i].testDataPairs[j].pszValue == NULL)
break;
CString strValue;
CPPUNIT_ASSERT_MESSAGE("Expected parameter not found.", c.Lookup(testdata[i].testDataPairs[j].pszName, strValue));
CPPUNIT_ASSERT_MESSAGE("Parameter value does not match.",
((strValue.GetLength() == 0) && (testdata[i].testDataPairs[j].pszValue == NULL))
|| strValue.Compare(testdata[i].testDataPairs[j].pszValue) == 0);
}
}
}
#endif
|
|
|
|

|
Your coding style is too verbose.
You have created wrapper functions for every member variable. There are even member functions that simply call the appropriate stl::map method.
You have mixed CString with stl code - not good.
And your coding style reminds me of a C coder who once told me he could write an editor in 3 lines.
Go figure.
Still thanks for the code, I have chopped most of it but the algortihm works.
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
An intuitive and extensible command line parser class that handles multiple command line formats
| Type | Article |
| Licence | |
| First Posted | 17 Aug 2002 |
| Views | 151,557 |
| Bookmarked | 96 times |
|
|