|
|
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.
<br />
CCmdLineParser::CCmdLineParser(int argc, TCHAR* argv[], bool bCaseSensitive) : m_bCaseSensitive(bCaseSensitive)<br />
{<br />
m_ValsMap.clear();<br />
<br />
for (int i=1; i<argc; i++) {<br />
LPCTSTR sArg = _tcspbrk(argv[i], m_sDelimeters);<br />
if ( (NULL == sArg) || (sArg != argv[i]) || ('\0' == sArg[1]) ) {
continue;<br />
}<br />
<br />
sArg = _tcsinc(sArg);<br />
LPTSTR sVal = _tcspbrk(sArg, m_sValueSep);<br />
<br />
if(sVal == NULL) {
CString csKey(sArg);<br />
if( !m_bCaseSensitive ) {<br />
csKey.MakeLower();<br />
}<br />
m_ValsMap.insert(CValsMap::value_type(csKey, ""));<br />
} else {
CString csKey(sArg, sVal - sArg);<br />
if( !csKey.IsEmpty() ) {
if( !m_bCaseSensitive ) {<br />
csKey.MakeLower();<br />
}<br />
<br />
size_t stLen = _tcslen(sVal);<br />
if ( 1 == stLen ) {
sVal = _T("");<br />
} else {
if ('\"' == sVal[stLen-1]) {<br />
sVal[stLen-1] = '\\';<br />
}<br />
sVal = _tcsinc(sVal);<br />
}<br />
m_ValsMap.insert(CValsMap::value_type(csKey, sVal));<br />
}<br />
}<br />
}<br />
}<br />
|
|
|
|
|
|
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.
|
|
|
|
|
I see absoultly nothing wrong with the coding style and for the most part I believe that it is written quite well.
John
|
|
|
|
|
douglash1 wrote:
You have created wrapper functions for every member variable.
So?
douglash1 wrote:
There are even member functions that simply call the appropriate stl::map method.
Encapsulation of the data structures used. Makes sense.
douglash1 wrote:
You have mixed CString with stl code - not good.
Why?
CString gives stronger performance guarantees than std::string. I find them necessary in many circumstances.
we are here to help each other get through this thing, whatever it is Vonnegut jr. boost your code || Fold With Us! || sighist | doxygen
|
|
|
|
|
Somebody got out of the wrong side of bed
Thanks for the code - works very well
Tim Stubbs
|
|
|
|
|
Nice class.
You should consider changing the CValsMap from deriving from std::map to just a typedef
so from :
class CValsMap : public map<CCmdLineParser_String, CCmdLineParser_String> {};
to
typedef std::map<CCmdLineParser_String, CCmdLineParser_String> CValsMap;
You're not supposed to be able to derive from a class like map, which doesn't have a virtual destructor, as, according to the C++ standard, the behaviour is undefined when the object is destroyed.
Cheers
Jim
¡El diablo está en mis pantalones! ¡Mire, mire!
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
SELECT * FROM User WHERE Clue > 0
0 rows returned
|
|
|
|
|
Jim Crafton wrote: You're not supposed to be able to derive from a class like map, which doesn't have a virtual destructor, as, according to the C++ standard, the behaviour is undefined when the object is destroyed.
That's not strictly true, the standard does not make restriction on what you can derive from. There is only a problem if there is a pointer somewhere in the code to the base class and then the code "deletes" that base class. And then that is only a problem if the derived class has destructable (i.e. class based) data members. If he were to change the inheritance to private or protected that would make it safer.
|
|
|
|
|
Thanks for your article & code, i've changed the std::map to CMap (I have to run it on PPC) which I think you used before (looking at how things are defined).
Your class will be used in our mobile solution in an intermediate app that activates a route planning tool from our main app.(application is called FieldAssist (http://www.fieldassist.nl/[^])
Regards
Rutger
|
|
|
|
|
Hi Pavel,
Your class runs great! What license have you issued your code under? Is anyone free to use it in any application, open or closed source? I'd like to use it in an non-free application where I work.
Thanks!
R. Douglas Barbieri
dougbarbieri@yahoo.com
modified 18-May-22 21:01pm.
|
|
|
|
|
This code is free for any kind of use.
Pavel Antonov
|
|
|
|
|
Pavel,
My management needs a more specific choice of licensing in order to approve my use of this code. Could you specify if you mean "Public Domain", "BSD license", or "MIT license" when you say that the code is "Free for all use"?
Thanks so much, I appreciate your time and the donation of your code for us to use.
Thanks,
Andrew
|
|
|
|
|
Simple command parsing that you would expect and have seen everywhere doesn't work.. This code doesn't handle simple args/flags like "myprog -v -g input_file". One the code doesn't handle the -v arg and it doesn't handle a dangling filename which isn't associated with an arg. This a common method of usage. I'd like to see something like CCommandLineInfo but with the ability to handle spaces passed with an arg, i.e. quoted arg. What I don't need is something that creates its own arg mechansism; i.e. /key:value.
MLS
|
|
|
|
|
Try using argtable. It is an ANSI C lib for parsing GNU style command line arguments like you have described. It adheres to the GNU getopt standard.
The homepage is http://argtable.sourceforge.net.
|
|
|
|
|
Command-line parsing is one of these problems you'd think is well known and has been solved nicely by hundreds, but actually discover all (ok, most) implementations are either obscure, difficult to use, plain wrong, or all of these !
Then you find the CCmdLineParser and the sky is blue again
Congratulations for the very good job and thanks for making it available to all of us.
Serge
|
|
|
|
|
first i must say, really good work..
but one problem:
if i get a command line with a filename like this:
/file:c:\new folder\index.html
the parser only gets chars until the space.
Is there any solution ?
thx,
jogi
|
|
|
|
|
Thanx for your thanks )))
And about spaces -- it will work fine if you enclose value with spaces with quotes, like this:
/file:"c:\new folder\index.html"
Pavel
|
|
|
|
|
thx, great idea! Sometimes it is too easy to get
|
|
|
|
|